diff --git a/DEPS b/DEPS
index 4490f847..76c0908 100644
--- a/DEPS
+++ b/DEPS
@@ -306,19 +306,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': 'd77efc6fbf0b5265b6faa739fc7e5d9e9799e0d8',
+  'src_internal_revision': 'c0441ac66681bb025fe45ca06acbd21a72b8f39e',
   # 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': '7777976cd518253e939f4dc39d0b992cf6a7419b',
+  'skia_revision': '9b103a4148e291537909a1c0765b43a5125676d2',
   # 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': 'e1016444345b1bd45aade04a243fdf094b23f488',
+  'v8_revision': '93c5bbc582e38f3a543ae0e5a28e593f2e7cc6dd',
   # 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': 'cf77126a7f89f0195d973484b8f66af65882eb4e',
+  'angle_revision': 'f431641a948660f5e1709aa7cd89b16ccf93f1de',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -326,7 +326,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '4282836f334034e852b8bc4e38459fdf8ae644db',
+  'pdfium_revision': '35d21add50698046463a32f3c6c486913b3b2f9b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -381,7 +381,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': '6d39c23fce9fdf9e5ce7c5730de4bc14556ec159',
+  'catapult_revision': '27e029f52955bf8ac9e35a1dacf4f56bb39d3eb8',
   # 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.
@@ -825,7 +825,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '87be368d5db43790e9d4b22620fe04228e70df17',
+    'cbd13ba3774974cb20cd7dbaf2acc4860fe6d0f4',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1162,7 +1162,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' + '@' + 'b48f979f2c28a68c21787fb94bb3eca15bc8a955',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '0ab00d0ab4c48eb09ff9a359365c55528ef2bbb5',
       'condition': 'checkout_chromeos',
   },
 
@@ -1197,7 +1197,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '9305434c21feace5efde065e50151596484b7bc5',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '784db7a5f04f5093941b6c2228e40b9a10d553b2',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1663,7 +1663,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '7cf1c2d0f40780828dae7a1590484cfc3055b48c',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '74cdba3b28c2a48bb3c5b5cae32812eb6c76a8da',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '8ef97ff3b7332e38e61b347a2fbed425a4617151',
@@ -1848,7 +1848,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'e082b08475761a2ba6a3349dfea72f704c8b68d4',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '22b6564d77af5aa637c0da59219bfb681cc55156',
+    Var('webrtc_git') + '/src.git' + '@' + 'aaa123debbd106669fc849425b92607edbd5d26f',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -1982,7 +1982,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'LOhlejPDYShTct4KFwCENGHD1GcylIMQ5F_b9YMiGFoC',
+        'version': '3As5TxenU3QWcTt0v-ps4C9SOGC8pOlLFCq5lCtnZNMC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3995,7 +3995,7 @@
 
   'src/components/optimization_guide/internal': {
       'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' +
-        '0f3da705ae7b53e643f8454750743a98b3f6c619',
+        'b5dce536acaa9d9fedc1ea98b42586100f0e11c7',
       'condition': 'checkout_src_internal',
   },
 
@@ -4055,7 +4055,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '1d21e57550bc1ef3e6578f7ad5c0d2f2cd2d08ff',
+        '9a183ec88b60cfbeef16ebd32e27a83f9a06126a',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index a9fccb96..d5f49ab4 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -1194,6 +1194,16 @@
       [_THIRD_PARTY_EXCEPT_BLINK],  # Don't warn in third_party folders.
     ),
     BanRule(
+      r'/\bstd::execution::(par|seq)\b',
+      (
+           'std::execution::(par|seq) is banned; they do not fit into '
+           ' Chrome\'s threading model, and libc++ doesn\'t have full '
+           'support.'
+      ),
+      True,
+      [_THIRD_PARTY_EXCEPT_BLINK],
+    ),
+    BanRule(
       r'/\bstd::bit_cast\b',
       (
         'std::bit_cast is banned; use base::bit_cast instead for values and '
diff --git a/android_webview/browser/network_service/aw_proxying_restricted_cookie_manager.cc b/android_webview/browser/network_service/aw_proxying_restricted_cookie_manager.cc
index 7f8d239..9cb955e 100644
--- a/android_webview/browser/network_service/aw_proxying_restricted_cookie_manager.cc
+++ b/android_webview/browser/network_service/aw_proxying_restricted_cookie_manager.cc
@@ -11,17 +11,28 @@
 
 #include "android_webview/browser/aw_cookie_access_policy.h"
 #include "base/memory/ptr_util.h"
+#include "base/ranges/algorithm.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
+#include "net/cookies/parsed_cookie.h"
 #include "services/network/public/mojom/restricted_cookie_manager.mojom-forward.h"
+#include "services/network/public/mojom/restricted_cookie_manager.mojom.h"
 #include "url/gurl.h"
 
 namespace android_webview {
 
+namespace {
+
+bool AreAllCookiesDisabled() {
+  return !AwCookieAccessPolicy::GetInstance()->GetShouldAcceptCookies();
+}
+
+}  // namespace
+
 class AwProxyingRestrictedCookieManagerListener
     : public network::mojom::CookieChangeListener {
  public:
@@ -39,9 +50,13 @@
         client_listener_(std::move(client_listener)) {}
 
   void OnCookieChange(const net::CookieChangeInfo& change) override {
-    if (aw_restricted_cookie_manager_ &&
-        aw_restricted_cookie_manager_->AllowCookies(url_, site_for_cookies_,
-                                                    has_storage_access_)) {
+    if (AreAllCookiesDisabled()) {
+      return;
+    }
+    if (change.cookie.IsPartitioned() ||
+        (aw_restricted_cookie_manager_ &&
+         aw_restricted_cookie_manager_->AllowFullCookies(
+             url_, site_for_cookies_, has_storage_access_))) {
       client_listener_->OnCookieChange(change);
     }
   }
@@ -96,13 +111,40 @@
     bool is_ad_tagged,
     GetAllForUrlCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  if (AllowCookies(url, site_for_cookies, has_storage_access)) {
-    underlying_restricted_cookie_manager_->GetAllForUrl(
-        url, site_for_cookies, top_frame_origin, has_storage_access,
-        std::move(options), is_ad_tagged, std::move(callback));
-  } else {
+
+  if (AreAllCookiesDisabled()) {
     std::move(callback).Run(std::vector<net::CookieWithAccessResult>());
+    return;
   }
+
+  bool allow_all_cookies =
+      AllowFullCookies(url, site_for_cookies, has_storage_access);
+
+  underlying_restricted_cookie_manager_->GetAllForUrl(
+      url, site_for_cookies, top_frame_origin, has_storage_access,
+      std::move(options), is_ad_tagged,
+      base::BindOnce(
+          [](bool allow_all_cookies,
+             const std::vector<::net::CookieWithAccessResult>& cookies) {
+            if (allow_all_cookies) {
+              return cookies;
+            }
+
+            // We get back a const vector of cookies so we can't modify the
+            // original vector. So we instead need to copy all the partitioned
+            // cookies to a new list.
+            std::vector<::net::CookieWithAccessResult> partitioned_cookies;
+
+            std::copy_if(cookies.begin(), cookies.end(),
+                         std::back_inserter(partitioned_cookies),
+                         [](::net::CookieWithAccessResult cookie) {
+                           return cookie.cookie.IsPartitioned();
+                         });
+
+            return partitioned_cookies;
+          },
+          allow_all_cookies)
+          .Then(std::move(callback)));
 }
 
 void AwProxyingRestrictedCookieManager::SetCanonicalCookie(
@@ -114,8 +156,13 @@
     net::CookieInclusionStatus status,
     SetCanonicalCookieCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  if (AreAllCookiesDisabled()) {
+    std::move(callback).Run(false);
+    return;
+  }
 
-  if (AllowCookies(url, site_for_cookies, has_storage_access)) {
+  if (cookie.IsPartitioned() ||
+      AllowFullCookies(url, site_for_cookies, has_storage_access)) {
     underlying_restricted_cookie_manager_->SetCanonicalCookie(
         cookie, url, site_for_cookies, top_frame_origin, has_storage_access,
         status, std::move(callback));
@@ -157,8 +204,19 @@
     const std::string& cookie,
     SetCookieFromStringCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  if (AreAllCookiesDisabled()) {
+    std::move(callback).Run();
+    return;
+  }
 
-  if (AllowCookies(url, site_for_cookies, has_storage_access)) {
+  // We have to do a quick parsing of the cookie string just to see if the
+  // partitioned header is set before handing over the string to the restricted
+  // cookie manager.
+  net::ParsedCookie parsed_cookie(cookie);
+
+  if (parsed_cookie.IsValid() &&
+      ((parsed_cookie.IsPartitioned() && parsed_cookie.IsSecure()) ||
+       AllowFullCookies(url, site_for_cookies, has_storage_access))) {
     underlying_restricted_cookie_manager_->SetCookieFromString(
         url, site_for_cookies, top_frame_origin, has_storage_access, cookie,
         std::move(callback));
@@ -176,20 +234,38 @@
     bool is_ad_tagged,
     GetCookiesStringCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
-  if (AllowCookies(url, site_for_cookies, has_storage_access)) {
-    // In Android Webview the access to cookies can change dynamically. For
-    // now never request a shared memory region so that a full IPC is issued
-    // every time. This prevents a client retaining access to the cookie value
-    // past the moment where it was denied. (crbug.com/1393050): Implement a
-    // strategy so that the shared memory access can be revoked from here.
-    underlying_restricted_cookie_manager_->GetCookiesString(
-        url, site_for_cookies, top_frame_origin, has_storage_access,
-        /*get_version_shared_memory=*/false, is_ad_tagged, std::move(callback));
-  } else {
+  if (AreAllCookiesDisabled()) {
     std::move(callback).Run(network::mojom::kInvalidCookieVersion,
                             base::ReadOnlySharedMemoryRegion(), "");
+    return;
   }
+
+  // In order to know if we should return cookies or not, we need to have
+  // access to the canonical cookies before they are parsed into a cookie
+  // line so that we can apply our filtering logic. Because of this, we don't
+  // use restricted cookie managers underlying GetCookiesString method. This
+  // isn't a huge loss because the GetCookiesString method mostly has logic
+  // around creating shared memory which we don't use on WebView. In Android
+  // Webview the access to cookies can change dynamically. For now never
+  // request a shared memory region so that a full IPC is issued every time.
+  // This prevents a client retaining access to the cookie value past the
+  // moment where it was denied. (crbug.com/1393050): Implement a strategy so
+  // that the shared memory access can be revoked from here.
+
+  auto match_options = network::mojom::CookieManagerGetOptions::New();
+  match_options->name = "";
+  match_options->match_type = network::mojom::CookieMatchType::STARTS_WITH;
+
+  auto bound_callback =
+      base::BindOnce(std::move(callback), network::mojom::kInvalidCookieVersion,
+                     base::ReadOnlySharedMemoryRegion());
+
+  GetAllForUrl(url, site_for_cookies, top_frame_origin, has_storage_access,
+               std::move(match_options), is_ad_tagged,
+               base::BindOnce([](const std::vector<net::CookieWithAccessResult>&
+                                     cookies) {
+                 return net::CanonicalCookie::BuildCookieLine(cookies);
+               }).Then(std::move(bound_callback)));
 }
 
 void AwProxyingRestrictedCookieManager::CookiesEnabledFor(
@@ -200,7 +276,7 @@
     CookiesEnabledForCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   std::move(callback).Run(
-      AllowCookies(url, site_for_cookies, has_storage_access));
+      AllowFullCookies(url, site_for_cookies, has_storage_access));
 }
 
 AwProxyingRestrictedCookieManager::AwProxyingRestrictedCookieManager(
@@ -229,7 +305,7 @@
   mojo::MakeSelfOwnedReceiver(std::move(wrapper), std::move(receiver));
 }
 
-bool AwProxyingRestrictedCookieManager::AllowCookies(
+bool AwProxyingRestrictedCookieManager::AllowFullCookies(
     const GURL& url,
     const net::SiteForCookies& site_for_cookies,
     bool has_storage_access) const {
diff --git a/android_webview/browser/network_service/aw_proxying_restricted_cookie_manager.h b/android_webview/browser/network_service/aw_proxying_restricted_cookie_manager.h
index 3c0ed93..099564f7d 100644
--- a/android_webview/browser/network_service/aw_proxying_restricted_cookie_manager.h
+++ b/android_webview/browser/network_service/aw_proxying_restricted_cookie_manager.h
@@ -17,8 +17,15 @@
 
 namespace android_webview {
 
-// A RestrictedCookieManager which conditionally proxies to an underlying
-// RestrictedCookieManager, first consulting WebView's cookie settings.
+// A RestrictedCookieManager conditionally returns cookies from an underlying
+// RestrictedCookieManager, after consulting WebView's cookie settings.
+// We need to do this because Chromium typically configures this per
+// BrowserContext but Android developers can set cookie permissions per WebView.
+// To work around this, we need to intercept cookies while they are being
+// retrieved in the renderer. We have to optimistically retrieve cookies because
+// partitioned cookies should always be allowed regardless of if third party
+// cookies are allowed or not, and there is no way for us to communicate this to
+// the restricted cookie manager when we proxy calls to it.
 class AwProxyingRestrictedCookieManager
     : public network::mojom::RestrictedCookieManager {
  public:
@@ -88,9 +95,9 @@
                          CookiesEnabledForCallback callback) override;
 
   // This one is internal.
-  bool AllowCookies(const GURL& url,
-                    const net::SiteForCookies& site_for_cookies,
-                    bool has_storage_access) const;
+  bool AllowFullCookies(const GURL& url,
+                        const net::SiteForCookies& site_for_cookies,
+                        bool has_storage_access) const;
 
  private:
   AwProxyingRestrictedCookieManager(
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 f7a3baae..c313d4af 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
@@ -887,6 +887,7 @@
         Flag.baseFeature(
                 BlinkFeatures.BACK_FORWARD_CACHE_SEND_NOT_RESTORED_REASONS,
                 "Expose NotRestoredReasons via PerformanceNavigationTiming API."),
+        Flag.baseFeature("SkipUnnecessaryThreadHopsForParseHeaders"),
         // Add new commandline switches and features above. The final entry should have a
         // trailing comma for cleaner diffs.
     };
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
index e48fda7..75c41cd 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
@@ -9,6 +9,7 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import android.util.Pair;
+import android.webkit.JavascriptInterface;
 
 import androidx.annotation.IntDef;
 import androidx.test.InstrumentationRegistry;
@@ -55,6 +56,8 @@
 import java.util.List;
 import java.util.Set;
 import java.util.TimeZone;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.function.Supplier;
 
 /** Tests for the CookieManager. */
 @DoNotBatch(reason = "The cookie manager is global state")
@@ -246,7 +249,7 @@
         try {
             // Set a cookie with the httponly flag, one with samesite=Strict, and one with
             // samesite=Lax, to ensure that they are all visible to CookieManager in the app.
-            String cookies[] = {
+            String[] cookies = {
                 "httponly=foo1; HttpOnly",
                 "strictsamesite=foo2; SameSite=Strict",
                 "laxsamesite=foo3; SameSite=Lax"
@@ -274,7 +277,7 @@
         try {
             // Set a partitioned cookie and an unpartitioned cookie to ensure that they are all
             // visible to CookieManager in the app.
-            String cookies[] = {
+            String[] cookies = {
                 "partitioned_cookie=foo; SameSite=None; Secure; Partitioned",
                 "unpartitioned_cookie=bar; SameSite=None; Secure"
             };
@@ -1225,6 +1228,147 @@
     @Test
     @MediumTest
     @Feature({"AndroidWebView", "Privacy"})
+    public void testPartitionedNetCookies() throws Throwable {
+        TestWebServer webServer = TestWebServer.startSsl();
+
+        try {
+            String[] cookies = {
+                "partitioned_cookie=foo; SameSite=None; Secure; Partitioned",
+                "unpartitioned_cookie=bar; SameSite=None; Secure"
+            };
+            List<Pair<String, String>> responseHeaders = new ArrayList<Pair<String, String>>();
+            for (String cookie : cookies) {
+                responseHeaders.add(Pair.create("Set-Cookie", cookie));
+            }
+
+            String iframeWithNetRequest =
+                    """
+                    <html>
+                    <body>
+                    <!-- Force a network request to happen from the iframe with a navigation so -->
+                    <!-- that we can intercept it and see which cookies were attached -->
+                    <img src="/path_to_intercept" >
+                    </body>
+                    </html>
+                    """;
+            String iframeUrl = webServer.setResponse("/", iframeWithNetRequest, responseHeaders);
+            // We don't need this to do anything fancy, we just need the path to exist
+            webServer.setResponse("/path_to_intercept", "hello", responseHeaders);
+
+            String url = toThirdPartyUrl(makeIframeUrl(webServer, "/parent.html", iframeUrl));
+
+            allowFirstPartyCookies();
+            allowThirdPartyCookies(mAwContents);
+            mActivityTestRule.loadUrlSync(
+                    mAwContents, mContentsClient.getOnPageFinishedHelper(), url);
+            Assert.assertEquals(
+                    "All cookies should be returned when 3PCs are enabled",
+                    "partitioned_cookie=foo; unpartitioned_cookie=bar",
+                    webServer.getLastRequest("/path_to_intercept").headerValue("Cookie"));
+
+            blockThirdPartyCookies(mAwContents);
+            mActivityTestRule.loadUrlSync(
+                    mAwContents, mContentsClient.getOnPageFinishedHelper(), url);
+            Assert.assertEquals(
+                    "Partitioned cookies should be returned when 3PCs are disabled",
+                    "partitioned_cookie=foo",
+                    webServer.getLastRequest("/path_to_intercept").headerValue("Cookie"));
+
+            blockAllCookies();
+            mActivityTestRule.loadUrlSync(
+                    mAwContents, mContentsClient.getOnPageFinishedHelper(), url);
+            Assert.assertEquals(
+                    "No cookies should be returned when all cookies are disabled",
+                    "",
+                    webServer.getLastRequest("/path_to_intercept").headerValue("Cookie"));
+
+        } finally {
+            webServer.shutdown();
+        }
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"AndroidWebView", "Privacy"})
+    public void testPartitionedJSCookies() throws Throwable {
+        String partitionedCookie = "partitioned-cookie=123";
+        String unpartitionedCookie = "regular-cookie=456";
+
+        // Using SSL server here since CookieStore API requires a secure schema.
+        TestWebServer webServer = TestWebServer.startSsl();
+        try {
+            // TODO(https://crbug.com/1523964): The WebView cookie manager API does not currently
+            // provide access to
+            // third party partitioned urls so we need to retrieve these cookies from the iframe
+            // itself to validate this
+            // behavior. We should refactor this test once support has been added to just use the
+            // CookieManager.
+            final LinkedBlockingQueue<String> javascriptInterfaceQueue =
+                    new LinkedBlockingQueue<>();
+            AwActivityTestRule.addJavascriptInterfaceOnUiThread(
+                    mAwContents,
+                    new Object() {
+                        @JavascriptInterface
+                        public void report(String cookies) {
+                            javascriptInterfaceQueue.add(cookies);
+                        }
+                    },
+                    "cookieResults");
+
+            Supplier<String> iframeCookiesSupplier =
+                    () -> {
+                        String iframeUrl =
+                                toThirdPartyUrl(
+                                        makeCookieScriptResultsUrl(
+                                                webServer,
+                                                "/iframe.html",
+                                                partitionedCookie
+                                                        + "; Secure; Path=/; SameSite=None;"
+                                                        + " Partitioned;",
+                                                unpartitionedCookie
+                                                        + "; Secure; Path=/; SameSite=None;"));
+
+                        String url = makeIframeUrl(webServer, "/parent.html", iframeUrl);
+
+                        try {
+                            mActivityTestRule.loadUrlSync(
+                                    mAwContents, mContentsClient.getOnPageFinishedHelper(), url);
+
+                            return AwActivityTestRule.waitForNextQueueElement(
+                                    javascriptInterfaceQueue);
+                        } catch (Exception e) {
+                            // Failed to retrieve so we can treat this as "no-data" - this in turn
+                            // will fail equality checks
+                            return "Failed to retrieve data";
+                        }
+                    };
+
+            allowFirstPartyCookies();
+            blockThirdPartyCookies(mAwContents);
+            Assert.assertEquals(
+                    "Only partitioned cookies should be returned when 3PCs are disabled",
+                    partitionedCookie,
+                    iframeCookiesSupplier.get());
+
+            allowThirdPartyCookies(mAwContents);
+            Assert.assertEquals(
+                    "All cookies should be returned when 3PCs are enabled",
+                    partitionedCookie + "; " + unpartitionedCookie,
+                    iframeCookiesSupplier.get());
+
+            blockAllCookies();
+            Assert.assertEquals(
+                    "No cookies should ever be returned if all cookies are disabled",
+                    "",
+                    iframeCookiesSupplier.get());
+        } finally {
+            webServer.shutdown();
+        }
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"AndroidWebView", "Privacy"})
     public void testModernCookieSameSite_Disabled() throws Throwable {
         // Tests that the legacy behavior is active when "modern" SameSite behavior is not specified
         // via command-line flag.
@@ -1676,6 +1820,28 @@
     }
 
     /**
+     * Creates a response on the TestWebServer with a script that attempts to set a list of cookies
+     * and then reports them back to a java bridge.
+     *
+     * @param webServer the webServer on which to create the response
+     * @param path the path component of the url (e.g "/cookie_test.html")
+     * @param cookies A list of cookies to set
+     * @return the url which gets the response
+     */
+    private String makeCookieScriptResultsUrl(
+            TestWebServer webServer, String path, String... cookies) {
+        String response = "<html><body><script>";
+
+        for (String cookie : cookies) {
+            response += String.format("document.cookie='%s';", cookie);
+        }
+
+        response += "cookieResults.report(document.cookie);</script></body></html>";
+
+        return webServer.setResponse(path, response, null);
+    }
+
+    /**
      * Makes a url look as if it comes from a different host.
      * @param  url the url to fake.
      * @return  the resulting url after faking.
diff --git a/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java b/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java
index 3d801acc..21a08af6 100644
--- a/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java
+++ b/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java
@@ -97,6 +97,15 @@
     }
 
     @Override
+    public void onBackPressed() {
+        if (mWebView != null && mWebView.canGoBack()) {
+            mWebView.goBack();
+        } else {
+            super.onBackPressed();
+        }
+    }
+
+    @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         getMenuInflater().inflate(R.menu.main_menu, menu);
         if (!WebViewFeature.isFeatureSupported(WebViewFeature.TRACING_CONTROLLER_BASIC_USAGE)) {
diff --git a/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserFragment.java b/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserFragment.java
index d5f4970..695b833 100644
--- a/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserFragment.java
+++ b/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserFragment.java
@@ -290,15 +290,6 @@
         }
     }
 
-    public boolean onBackPressed() {
-        if (mWebView.canGoBack()) {
-            mWebView.goBack();
-            return true;
-        } else {
-            return false;
-        }
-    }
-
     ViewGroup getContainer() {
         return getView().findViewById(R.id.container);
     }
diff --git a/ash/accessibility/magnifier/magnifier_glass.h b/ash/accessibility/magnifier/magnifier_glass.h
index 7d266ba1..98027b7 100644
--- a/ash/accessibility/magnifier/magnifier_glass.h
+++ b/ash/accessibility/magnifier/magnifier_glass.h
@@ -85,15 +85,16 @@
   // location follows the mouse, which causes the layers to also move.
   raw_ptr<views::Widget> host_widget_ = nullptr;
 
+  // Draws a multicolored black/white/black border on top of |border_layer_|.
+  // Also draws a shadow around the border. This must be ordered before
+  // |border_layer_| so that it gets destroyed after |border_layer_|, otherwise
+  // |border_layer_| will have a pointer to a deleted delegate.
+  std::unique_ptr<BorderRenderer> border_renderer_;
+
   // Draws the background with a zoom filter applied.
   std::unique_ptr<ui::Layer> zoom_layer_;
   // Draws an outline that is overlaid on top of |zoom_layer_|.
   std::unique_ptr<ui::Layer> border_layer_;
-  // Draws a multicolored black/white/black border on top of |border_layer_|.
-  // Also draws a shadow around the border. This must be ordered after
-  // |border_layer_| so that it gets destroyed after |border_layer_|, otherwise
-  // |border_layer_| will have a pointer to a deleted delegate.
-  std::unique_ptr<BorderRenderer> border_renderer_;
 };
 
 }  // namespace ash
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 0c6e24f..a22f211 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -471,12 +471,6 @@
              "ClipboardHistoryUrlTitles",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// If enabled, clipboard history explicitly pastes into `content::WebContents`
-// instead of using synthetic key events.
-BASE_FEATURE(kClipboardHistoryWebContentsPaste,
-             "ClipboardHistoryWebContentsPaste",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // If enabled and account falls under the new deal, will be allowed to toggle
 // auto updates.
 BASE_FEATURE(kConsumerAutoUpdateToggleAllowed,
@@ -512,11 +506,6 @@
              "CryptauthAttestationSyncing",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Enables contextual nudges for gesture education.
-BASE_FEATURE(kContextualNudges,
-             "ContextualNudges",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Enables or disables Crostini GPU support.
 // Note that this feature can be overridden by login_manager based on
 // whether a per-board build sets the USE virtio_gpu flag.
@@ -1427,17 +1416,10 @@
              "HiddenNetworkWarning",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Enables hiding of ARC media notifications. If this is enabled, all ARC
-// notifications that are of the media type will not be shown. This
-// is because they will be replaced by native media session notifications.
-// TODO(beccahughes): Remove after launch. (https://crbug.com/897836)
-BASE_FEATURE(kHideArcMediaNotifications,
-             "HideArcMediaNotifications",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // When enabled, shelf navigation controls and the overview tray item will be
 // removed from the shelf in tablet mode (unless otherwise specified by user
-// preferences, or policy).
+// preferences, or policy). This feature also enables "contextual nudges" for
+// gesture education.
 BASE_FEATURE(kHideShelfControlsInTabletMode,
              "HideShelfControlsInTabletMode",
              base::FEATURE_ENABLED_BY_DEFAULT);
@@ -2658,11 +2640,6 @@
 // Controls whether the snap group feature is enabled or not.
 BASE_FEATURE(kSnapGroup, "SnapGroup", base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Enables battery indicator for styluses in the palette tray
-BASE_FEATURE(kStylusBatteryStatus,
-             "StylusBatteryStatus",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Enables or disables using the system input engine for physical typing in
 // Japanese.
 BASE_FEATURE(kSystemJapanesePhysicalTyping,
@@ -3043,13 +3020,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-bool AreContextualNudgesEnabled() {
-  if (!IsHideShelfControlsInTabletModeEnabled()) {
-    return false;
-  }
-  return base::FeatureList::IsEnabled(kContextualNudges);
-}
-
 bool AreDesksTemplatesEnabled() {
   return base::FeatureList::IsEnabled(kDesksTemplates);
 }
@@ -3238,10 +3208,6 @@
          chromeos::features::IsClipboardHistoryRefreshEnabled();
 }
 
-bool IsClipboardHistoryWebContentsPasteEnabled() {
-  return base::FeatureList::IsEnabled(kClipboardHistoryWebContentsPaste);
-}
-
 bool IsContinuousOverviewScrollAnimationEnabled() {
   return base::FeatureList::IsEnabled(kContinuousOverviewScrollAnimation) &&
          chromeos::features::IsJellyEnabled();
@@ -3593,10 +3559,6 @@
   return base::FeatureList::IsEnabled(kHibernate);
 }
 
-bool IsHideArcMediaNotificationsEnabled() {
-  return base::FeatureList::IsEnabled(kHideArcMediaNotifications);
-}
-
 bool IsHideShelfControlsInTabletModeEnabled() {
   return base::FeatureList::IsEnabled(kHideShelfControlsInTabletMode);
 }
@@ -4358,10 +4320,6 @@
   return base::FeatureList::IsEnabled(kSystemTrayShadow);
 }
 
-bool IsStylusBatteryStatusEnabled() {
-  return base::FeatureList::IsEnabled(kStylusBatteryStatus);
-}
-
 bool IsTetheringExperimentalFunctionalityEnabled() {
   return base::FeatureList::IsEnabled(kTetheringExperimentalFunctionality);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index df8230c..08e7066 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -138,10 +138,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kClipboardHistoryUrlTitles);
 COMPONENT_EXPORT(ASH_CONSTANTS)
-BASE_DECLARE_FEATURE(kClipboardHistoryWebContentsPaste);
-COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kConsumerAutoUpdateToggleAllowed);
-COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kContextualNudges);
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kContinuousOverviewScrollAnimation);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kCoralFeature);
@@ -460,8 +457,6 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kHibernate);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kHiddenNetworkWarning);
 COMPONENT_EXPORT(ASH_CONSTANTS)
-BASE_DECLARE_FEATURE(kHideArcMediaNotifications);
-COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kHideShelfControlsInTabletMode);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kHindiInscriptLayout);
 COMPONENT_EXPORT(ASH_CONSTANTS)
@@ -810,7 +805,6 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kSnoopingProtection);
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kStandaloneWindowMigrationUx);
-COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kStylusBatteryStatus);
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kStartAssistantAudioDecoderOnDemand);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kSuppressTextMessages);
@@ -897,7 +891,6 @@
 // Keep alphabetized.
 
 COMPONENT_EXPORT(ASH_CONSTANTS) bool AreCaptureModeDemoToolsEnabled();
-COMPONENT_EXPORT(ASH_CONSTANTS) bool AreContextualNudgesEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool AreDesksTemplatesEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool AreHelpAppWelcomeTipsEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS)
@@ -947,8 +940,6 @@
 bool IsCheckPasswordsAgainstCryptohomeHelperEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsClipboardHistoryLongpressEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsClipboardHistoryUrlTitlesEnabled();
-COMPONENT_EXPORT(ASH_CONSTANTS)
-bool IsClipboardHistoryWebContentsPasteEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsConsumerAutoUpdateToggleAllowed();
 COMPONENT_EXPORT(ASH_CONSTANTS)
 bool IsContinuousOverviewScrollAnimationEnabled();
@@ -1059,7 +1050,6 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 bool AreAnyGlanceablesTimeManagementViewsEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsHibernateEnabled();
-COMPONENT_EXPORT(ASH_CONSTANTS) bool IsHideArcMediaNotificationsEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsHideShelfControlsInTabletModeEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS)
 bool IsHoldingSpaceCameraAppIntegrationEnabled();
@@ -1271,7 +1261,6 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsSystemLiveCaptionEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsSystemNudgeMigrationEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsSystemTrayShadowEnabled();
-COMPONENT_EXPORT(ASH_CONSTANTS) bool IsStylusBatteryStatusEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS)
 bool IsTetheringExperimentalFunctionalityEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsTilingWindowResizeEnabled();
diff --git a/ash/controls/contextual_tooltip.cc b/ash/controls/contextual_tooltip.cc
index 83e7546..4fd7122 100644
--- a/ash/controls/contextual_tooltip.cc
+++ b/ash/controls/contextual_tooltip.cc
@@ -104,8 +104,9 @@
 }  // namespace
 
 void RegisterProfilePrefs(PrefRegistrySimple* registry) {
-  if (features::AreContextualNudgesEnabled())
+  if (features::IsHideShelfControlsInTabletModeEnabled()) {
     registry->RegisterDictionaryPref(prefs::kContextualTooltips);
+  }
 }
 
 bool ShouldShowNudge(PrefService* prefs,
@@ -116,7 +117,7 @@
       *recheck_delay = delay;
   };
 
-  if (!features::AreContextualNudgesEnabled()) {
+  if (!features::IsHideShelfControlsInTabletModeEnabled()) {
     set_recheck_delay(base::TimeDelta());
     return false;
   }
diff --git a/ash/controls/contextual_tooltip_unittest.cc b/ash/controls/contextual_tooltip_unittest.cc
index 82bc24a..370e539b 100644
--- a/ash/controls/contextual_tooltip_unittest.cc
+++ b/ash/controls/contextual_tooltip_unittest.cc
@@ -27,14 +27,12 @@
  public:
   ContextualTooltipTest() {
     if (GetParam()) {
-      scoped_feature_list_.InitWithFeatures(
-          {ash::features::kContextualNudges,
-           ash::features::kHideShelfControlsInTabletMode},
-          {});
+      scoped_feature_list_.InitAndEnableFeature(
+          features::kHideShelfControlsInTabletMode);
 
     } else {
       scoped_feature_list_.InitAndDisableFeature(
-          ash::features::kContextualNudges);
+          features::kHideShelfControlsInTabletMode);
     }
   }
   ~ContextualTooltipTest() override = default;
diff --git a/ash/events/peripheral_customization_event_rewriter.cc b/ash/events/peripheral_customization_event_rewriter.cc
index 55ce328..0b8efe0 100644
--- a/ash/events/peripheral_customization_event_rewriter.cc
+++ b/ash/events/peripheral_customization_event_rewriter.cc
@@ -598,9 +598,12 @@
   }
   auto remapping_action = remapping_action_result->remapping_action;
 
-  metrics_manager_->RecordRemappingActionWhenButtonPressed(
-      *remapping_action,
-      ToMetricsString(remapping_action_result->peripheral_kind).data());
+  if (event.type() == ui::ET_KEY_PRESSED ||
+      event.type() == ui::ET_MOUSE_PRESSED) {
+    metrics_manager_->RecordRemappingActionWhenButtonPressed(
+        *remapping_action,
+        ToMetricsString(remapping_action_result->peripheral_kind).data());
+  }
 
   if (remapping_action->is_accelerator_action()) {
     if (event.type() == ui::ET_KEY_PRESSED ||
diff --git a/ash/events/peripheral_customization_event_rewriter_unittest.cc b/ash/events/peripheral_customization_event_rewriter_unittest.cc
index 3af1fbb..4613b5a3 100644
--- a/ash/events/peripheral_customization_event_rewriter_unittest.cc
+++ b/ash/events/peripheral_customization_event_rewriter_unittest.cc
@@ -8,6 +8,7 @@
 
 #include <algorithm>
 #include <memory>
+#include <variant>
 
 #include "ash/accelerators/accelerator_controller_impl.h"
 #include "ash/constants/ash_features.h"
@@ -19,6 +20,7 @@
 #include "ash/public/mojom/input_device_settings.mojom.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
+#include "base/containers/adapters.h"
 #include "base/containers/flat_map.h"
 #include "base/notreached.h"
 #include "base/strings/stringprintf.h"
@@ -26,8 +28,11 @@
 #include "base/test/scoped_feature_list.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
+#include "ui/events/base_event_utils.h"
 #include "ui/events/event.h"
 #include "ui/events/event_constants.h"
+#include "ui/events/event_sink.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/keycodes/dom/dom_code.h"
 #include "ui/events/keycodes/dom/dom_key.h"
 #include "ui/events/keycodes/keyboard_codes_posix.h"
@@ -36,6 +41,7 @@
 #include "ui/events/ozone/layout/scoped_keyboard_layout_engine.h"
 #include "ui/events/ozone/layout/stub/stub_keyboard_layout_engine.h"
 #include "ui/events/test/test_event_rewriter_continuation.h"
+#include "ui/events/test/test_event_source.h"
 #include "ui/events/types/event_type.h"
 #include "ui/gfx/geometry/point_f.h"
 #include "ui/gfx/geometry/vector2d_f.h"
@@ -46,6 +52,220 @@
 
 constexpr int kMouseDeviceId = 1;
 constexpr int kGraphicsTabletDeviceId = 2;
+constexpr int kRandomKeyboardDeviceId = 3;
+
+class TestEventSink : public ui::EventSink {
+ public:
+  TestEventSink() = default;
+  TestEventSink(const TestEventSink&) = delete;
+  TestEventSink& operator=(const TestEventSink&) = delete;
+  ~TestEventSink() override = default;
+
+  // Returns the recorded events.
+  std::vector<std::unique_ptr<ui::Event>> TakeEvents() {
+    return std::move(events_);
+  }
+
+  // ui::EventSink:
+  ui::EventDispatchDetails OnEventFromSource(ui::Event* event) override {
+    events_.emplace_back(event->Clone());
+    return ui::EventDispatchDetails();
+  }
+
+ private:
+  std::vector<std::unique_ptr<ui::Event>> events_;
+};
+
+struct TestKeyEvent {
+  ui::EventType type;
+  ui::DomCode code;
+  ui::DomKey key;
+  ui::KeyboardCode keycode;
+  ui::EventFlags flags = ui::EF_NONE;
+
+  bool operator==(const TestKeyEvent&) const = default;
+};
+
+struct TestMouseEvent {
+  ui::EventType type;
+  ui::EventFlags flags;
+  ui::EventFlags changed_button_flags;
+  uint32_t linux_key_code;
+
+  bool operator==(const TestMouseEvent&) const = default;
+};
+
+using TestEventVariant = std::variant<TestKeyEvent, TestMouseEvent>;
+
+std::string ConvertToString(const TestMouseEvent& mouse_event) {
+  return base::StringPrintf(
+      "MouseEvent type=%d flags=0x%X changed_button_flags=0x%X",
+      mouse_event.type, mouse_event.flags, mouse_event.changed_button_flags);
+}
+
+std::string ConvertToString(const TestKeyEvent& key_event) {
+  std::string flags_name =
+      base::JoinString(ui::EventFlagsNames(key_event.flags), "|");
+  return base::StringPrintf(
+      "KeyboardEvent type=%d code=%s(0x%06X) flags=%s(0x%X) vk=0x%02X "
+      "key=%s(0x%08X)",
+      key_event.type,
+      ui::KeycodeConverter::DomCodeToCodeString(key_event.code).c_str(),
+      static_cast<uint32_t>(key_event.code), flags_name.c_str(),
+      key_event.flags, key_event.keycode,
+      ui::KeycodeConverter::DomKeyToKeyString(key_event.key).c_str(),
+      static_cast<uint32_t>(key_event.key));
+}
+
+std::string ConvertToString(const TestEventVariant& event) {
+  return std::visit([](auto&& event) { return ConvertToString(event); }, event);
+}
+
+inline std::ostream& operator<<(std::ostream& os,
+                                const TestEventVariant& event) {
+  return os << ConvertToString(event);
+}
+
+// Factory template of TestKeyEvents just to reduce a lot of code/data
+// duplication.
+template <ui::DomCode code,
+          ui::DomKey::Base key,
+          ui::KeyboardCode keycode,
+          ui::EventFlags modifier_flag = ui::EF_NONE,
+          ui::DomKey::Base shifted_key = key>
+struct TestKey {
+  // Returns press key event.
+  static constexpr TestKeyEvent Pressed(ui::EventFlags flags = ui::EF_NONE) {
+    return {ui::ET_KEY_PRESSED, code,
+            (flags & ui::EF_SHIFT_DOWN) ? shifted_key : key, keycode,
+            flags | modifier_flag};
+  }
+
+  // Returns release key event.
+  static constexpr TestKeyEvent Released(ui::EventFlags flags = ui::EF_NONE) {
+    // Note: modifier flag should not be present on release events.
+    return {ui::ET_KEY_RELEASED, code,
+            (flags & ui::EF_SHIFT_DOWN) ? shifted_key : key, keycode, flags};
+  }
+
+  // Returns press then release key events.
+  static std::vector<TestEventVariant> Typed(
+      ui::EventFlags flags = ui::EF_NONE) {
+    return {Pressed(flags), Released(flags)};
+  }
+};
+
+// Short cut of TestKey construction for Character keys.
+template <ui::DomCode code,
+          char key,
+          ui::KeyboardCode keycode,
+          char shifted_key = key>
+using TestCharKey = TestKey<code,
+                            ui::DomKey::Constant<key>::Character,
+                            keycode,
+                            ui::EF_NONE,
+                            ui::DomKey::Constant<shifted_key>::Character>;
+
+template <ui::EventFlags changed_button_flag, uint32_t linux_key_code = 0>
+struct TestButton {
+  // Returns press button event.
+  static constexpr TestMouseEvent Pressed(ui::EventFlags flags = ui::EF_NONE) {
+    return {ui::ET_MOUSE_PRESSED, flags | changed_button_flag,
+            changed_button_flag, linux_key_code};
+  }
+
+  // Returns release button events.
+  static constexpr TestMouseEvent Released(ui::EventFlags flags = ui::EF_NONE) {
+    return {ui::ET_MOUSE_RELEASED, flags | changed_button_flag,
+            changed_button_flag, linux_key_code};
+  }
+
+  // Returns press then release button events.
+  static std::vector<TestEventVariant> Typed(
+      ui::EventFlags flags = ui::EF_NONE) {
+    return {Pressed(flags), Released(flags)};
+  }
+};
+
+using ButtonLeft = TestButton<ui::EF_LEFT_MOUSE_BUTTON>;
+using ButtonRight = TestButton<ui::EF_RIGHT_MOUSE_BUTTON>;
+using ButtonMiddle = TestButton<ui::EF_MIDDLE_MOUSE_BUTTON>;
+using ButtonForward = TestButton<ui::EF_FORWARD_MOUSE_BUTTON, BTN_FORWARD>;
+using ButtonBack = TestButton<ui::EF_BACK_MOUSE_BUTTON, BTN_BACK>;
+using ButtonExtra = TestButton<ui::EF_FORWARD_MOUSE_BUTTON, BTN_EXTRA>;
+using ButtonSide = TestButton<ui::EF_BACK_MOUSE_BUTTON, BTN_SIDE>;
+
+using KeyA = TestCharKey<ui::DomCode::US_A, 'a', ui::VKEY_A, 'A'>;
+using KeyB = TestCharKey<ui::DomCode::US_B, 'b', ui::VKEY_B, 'B'>;
+using KeyC = TestCharKey<ui::DomCode::US_C, 'c', ui::VKEY_C, 'C'>;
+using KeyD = TestCharKey<ui::DomCode::US_D, 'd', ui::VKEY_D, 'D'>;
+using KeyM = TestCharKey<ui::DomCode::US_M, 'm', ui::VKEY_M, 'M'>;
+using KeyN = TestCharKey<ui::DomCode::US_N, 'n', ui::VKEY_N, 'N'>;
+using KeyV = TestCharKey<ui::DomCode::US_V, 'v', ui::VKEY_V, 'V'>;
+using KeyZ = TestCharKey<ui::DomCode::US_Z, 'z', ui::VKEY_Z, 'Z'>;
+using KeyComma = TestCharKey<ui::DomCode::COMMA, ',', ui::VKEY_OEM_COMMA, '<'>;
+using KeyPeriod =
+    TestCharKey<ui::DomCode::PERIOD, '.', ui::VKEY_OEM_PERIOD, '>'>;
+using KeyDigit1 = TestCharKey<ui::DomCode::DIGIT1, '1', ui::VKEY_1, '!'>;
+using KeyDigit2 = TestCharKey<ui::DomCode::DIGIT2, '2', ui::VKEY_2, '@'>;
+using KeyDigit3 = TestCharKey<ui::DomCode::DIGIT3, '3', ui::VKEY_3, '#'>;
+using KeyDigit4 = TestCharKey<ui::DomCode::DIGIT4, '4', ui::VKEY_4, '$'>;
+using KeyDigit5 = TestCharKey<ui::DomCode::DIGIT5, '5', ui::VKEY_5, '%'>;
+using KeyDigit6 = TestCharKey<ui::DomCode::DIGIT6, '6', ui::VKEY_6, '^'>;
+using KeyDigit7 = TestCharKey<ui::DomCode::DIGIT7, '7', ui::VKEY_7, '&'>;
+using KeyDigit8 = TestCharKey<ui::DomCode::DIGIT8, '8', ui::VKEY_8, '*'>;
+using KeyDigit9 = TestCharKey<ui::DomCode::DIGIT9, '9', ui::VKEY_9, '('>;
+using KeyDigit0 = TestCharKey<ui::DomCode::DIGIT0, '0', ui::VKEY_0, ')'>;
+using KeyMinus = TestCharKey<ui::DomCode::MINUS, '-', ui::VKEY_OEM_MINUS, '_'>;
+using KeyEqual = TestCharKey<ui::DomCode::EQUAL, '=', ui::VKEY_OEM_PLUS, '+'>;
+using KeyArrowLeft =
+    TestKey<ui::DomCode::ARROW_LEFT, ui::DomKey::ARROW_LEFT, ui::VKEY_LEFT>;
+using KeyArrowRight =
+    TestKey<ui::DomCode::ARROW_RIGHT, ui::DomKey::ARROW_RIGHT, ui::VKEY_RIGHT>;
+using KeyArrowUp =
+    TestKey<ui::DomCode::ARROW_UP, ui::DomKey::ARROW_UP, ui::VKEY_UP>;
+using KeyArrowDown =
+    TestKey<ui::DomCode::ARROW_DOWN, ui::DomKey::ARROW_DOWN, ui::VKEY_DOWN>;
+using KeyBrowserBack = TestKey<ui::DomCode::BROWSER_BACK,
+                               ui::DomKey::BROWSER_BACK,
+                               ui::VKEY_BROWSER_BACK>;
+using KeyBrowserForward = TestKey<ui::DomCode::BROWSER_FORWARD,
+                                  ui::DomKey::BROWSER_FORWARD,
+                                  ui::VKEY_BROWSER_FORWARD>;
+
+// Modifier keys.
+using KeyLShift = TestKey<ui::DomCode::SHIFT_LEFT,
+                          ui::DomKey::SHIFT,
+                          ui::VKEY_SHIFT,
+                          ui::EF_SHIFT_DOWN>;
+using KeyRShift = TestKey<ui::DomCode::SHIFT_RIGHT,
+                          ui::DomKey::SHIFT,
+                          ui::VKEY_RSHIFT,
+                          ui::EF_SHIFT_DOWN>;
+using KeyLMeta = TestKey<ui::DomCode::META_LEFT,
+                         ui::DomKey::META,
+                         ui::VKEY_LWIN,
+                         ui::EF_COMMAND_DOWN>;
+using KeyRMeta = TestKey<ui::DomCode::META_RIGHT,
+                         ui::DomKey::META,
+                         ui::VKEY_RWIN,
+                         ui::EF_COMMAND_DOWN>;
+using KeyLControl = TestKey<ui::DomCode::CONTROL_LEFT,
+                            ui::DomKey::CONTROL,
+                            ui::VKEY_CONTROL,
+                            ui::EF_CONTROL_DOWN>;
+using KeyRControl = TestKey<ui::DomCode::CONTROL_RIGHT,
+                            ui::DomKey::CONTROL,
+                            ui::VKEY_RCONTROL,
+                            ui::EF_CONTROL_DOWN>;
+using KeyLAlt = TestKey<ui::DomCode::ALT_LEFT,
+                        ui::DomKey::ALT,
+                        ui::VKEY_MENU,
+                        ui::EF_ALT_DOWN>;
+using KeyRAlt = TestKey<ui::DomCode::ALT_RIGHT,
+                        ui::DomKey::ALT,
+                        ui::VKEY_RMENU,
+                        ui::EF_ALT_DOWN>;
 
 class TestEventRewriterContinuation
     : public ui::test::TestEventRewriterContinuation {
@@ -139,27 +359,28 @@
 };
 
 using EventTypeVariant = absl::variant<ui::MouseEvent, ui::KeyEvent>;
+
 struct EventRewriterTestData {
-  EventTypeVariant incoming_event;
-  std::optional<EventTypeVariant> rewritten_event;
+  TestEventVariant incoming_event;
+  std::optional<TestEventVariant> rewritten_event;
   std::optional<mojom::Button> pressed_button;
 
-  EventRewriterTestData(EventTypeVariant incoming_event,
-                        std::optional<EventTypeVariant> rewritten_event)
+  EventRewriterTestData(TestEventVariant incoming_event,
+                        std::optional<TestEventVariant> rewritten_event)
       : incoming_event(incoming_event),
         rewritten_event(rewritten_event),
         pressed_button(std::nullopt) {}
 
-  EventRewriterTestData(EventTypeVariant incoming_event,
-                        std::optional<EventTypeVariant> rewritten_event,
+  EventRewriterTestData(TestEventVariant incoming_event,
+                        std::optional<TestEventVariant> rewritten_event,
                         mojom::CustomizableButton button)
       : incoming_event(incoming_event), rewritten_event(rewritten_event) {
     pressed_button = mojom::Button();
     pressed_button->set_customizable_button(button);
   }
 
-  EventRewriterTestData(EventTypeVariant incoming_event,
-                        std::optional<EventTypeVariant> rewritten_event,
+  EventRewriterTestData(TestEventVariant incoming_event,
+                        std::optional<TestEventVariant> rewritten_event,
                         ui::KeyboardCode key_code)
       : incoming_event(incoming_event), rewritten_event(rewritten_event) {
     pressed_button = mojom::Button();
@@ -187,18 +408,6 @@
       std::make_unique<ui::StubKeyboardLayoutEngine>());
 }
 
-ui::KeyEvent CreateKeyButtonEvent(ui::EventType type,
-                                  ui::KeyboardCode key_code,
-                                  int flags = ui::EF_NONE,
-                                  ui::DomCode code = ui::DomCode::NONE,
-                                  ui::DomKey key = ui::DomKey::NONE,
-                                  int device_id = kMouseDeviceId) {
-  auto engine = CreateLayoutEngine();
-  ui::KeyEvent key_event(type, key_code, code, flags, key, /*time_stamp=*/{});
-  key_event.set_source_device_id(device_id);
-  return key_event;
-}
-
 ui::MouseEvent CreateMouseButtonEvent(ui::EventType type,
                                       int flags,
                                       int changed_button_flags,
@@ -227,11 +436,6 @@
       static_cast<uint32_t>(key_event.GetDomKey()), key_event.scan_code());
 }
 
-std::string ConvertToString(const EventTypeVariant& event) {
-  return absl::visit([](auto&& event) { return ConvertToString(event); },
-                     event);
-}
-
 std::string ConvertToString(const ui::Event& event) {
   if (event.IsMouseEvent()) {
     return ConvertToString(*event.AsMouseEvent());
@@ -242,14 +446,6 @@
   NOTREACHED_NORETURN();
 }
 
-ui::Event& GetEventFromVariant(EventTypeVariant& event) {
-  if (absl::holds_alternative<ui::MouseEvent>(event)) {
-    return absl::get<ui::MouseEvent>(event);
-  } else {
-    return absl::get<ui::KeyEvent>(event);
-  }
-}
-
 mojom::Button GetButton(ui::KeyboardCode key_code) {
   mojom::Button button;
   button.set_vkey(key_code);
@@ -283,7 +479,8 @@
     AshTestBase::SetUp();
     controller_scoped_resetter_ = std::make_unique<
         InputDeviceSettingsController::ScopedResetterForTest>();
-    controller_ = std::make_unique<TestInputDeviceSettingsController>();
+    controller_ = std::make_unique<
+        testing::NiceMock<TestInputDeviceSettingsController>>();
     mouse_settings_ = mojom::MouseSettings::New();
     graphics_tablet_settings_ = mojom::GraphicsTabletSettings::New();
     ON_CALL(*controller_, GetMouseSettings(testing::_))
@@ -297,9 +494,13 @@
     rewriter_ = std::make_unique<PeripheralCustomizationEventRewriter>(
         controller_.get());
     metrics_manager_ = std::make_unique<InputDeviceSettingsMetricsManager>();
+
+    source_.AddEventRewriter(rewriter_.get());
   }
 
   void TearDown() override {
+    source_.RemoveEventRewriter(rewriter_.get());
+
     rewriter_.reset();
     controller_.reset();
     controller_scoped_resetter_.reset();
@@ -308,29 +509,163 @@
     metrics_manager_.reset();
   }
 
+  std::vector<TestEventVariant> RunRewriter(
+      const std::vector<TestEventVariant>& events,
+      ui::EventFlags extra_flags = ui::EF_NONE,
+      int device_id = kMouseDeviceId) {
+    struct ModifierInfo {
+      ui::EventFlags flag;
+      ui::DomCode code;
+      ui::DomKey key;
+      ui::KeyboardCode keycode;
+    };
+    // We'll use modifier keys at left side heuristically.
+    static constexpr ModifierInfo kModifierList[] = {
+        {ui::EF_SHIFT_DOWN, ui::DomCode::SHIFT_LEFT, ui::DomKey::SHIFT,
+         ui::VKEY_SHIFT},
+        {ui::EF_CONTROL_DOWN, ui::DomCode::CONTROL_LEFT, ui::DomKey::CONTROL,
+         ui::VKEY_CONTROL},
+        {ui::EF_ALT_DOWN, ui::DomCode::ALT_LEFT, ui::DomKey::ALT,
+         ui::VKEY_MENU},
+        {ui::EF_COMMAND_DOWN, ui::DomCode::META_LEFT, ui::DomKey::META,
+         ui::VKEY_LWIN},
+        {ui::EF_MOD3_DOWN, ui::DomCode::CAPS_LOCK, ui::DomKey::CAPS_LOCK,
+         ui::VKEY_CAPITAL},
+    };
+
+    // Send modifier key press events to update rewriter's modifier flag state.
+    ui::EventFlags current_flags = 0;
+    for (const auto& modifier : kModifierList) {
+      if (!(extra_flags & modifier.flag)) {
+        continue;
+      }
+      current_flags |= modifier.flag;
+      SendKeyEvent(TestKeyEvent{ui::ET_KEY_PRESSED, modifier.code, modifier.key,
+                                modifier.keycode, current_flags},
+                   kRandomKeyboardDeviceId);
+    }
+    CHECK_EQ(current_flags, extra_flags);
+
+    // Add extra_flags to each TestkeyEvent.
+    std::vector<TestEventVariant> key_events;
+    for (const auto& event : events) {
+      if (const auto* test_key_event = std::get_if<TestKeyEvent>(&event)) {
+        TestKeyEvent new_event = *test_key_event;
+        new_event.flags = new_event.flags | current_flags;
+        key_events.push_back(new_event);
+      } else {
+        const auto* test_mouse_event = std::get_if<TestMouseEvent>(&event);
+        CHECK(test_mouse_event);
+        TestMouseEvent new_event = *test_mouse_event;
+        new_event.flags = new_event.flags | current_flags;
+        key_events.push_back(new_event);
+      }
+    }
+    auto result = SendKeyEvents(key_events, device_id);
+
+    // Send modifier key release events to unset rewriter'.s modifier flag
+    // state.
+    for (const auto& modifier : base::Reversed(kModifierList)) {
+      if (!(extra_flags & modifier.flag)) {
+        continue;
+      }
+      current_flags &= ~modifier.flag;
+      SendKeyEvent(TestKeyEvent{ui::ET_KEY_RELEASED, modifier.code,
+                                modifier.key, modifier.keycode, current_flags},
+                   kRandomKeyboardDeviceId);
+    }
+    CHECK_EQ(current_flags, 0);
+
+    return result;
+  }
+
+  // Sends a KeyEvent to the rewriter, returns the rewritten events.
+  // Note: one event may be rewritten into multiple events.
+  std::vector<TestEventVariant> SendKeyEvent(const TestKeyEvent& event,
+                                             int device_id) {
+    return SendKeyEvents({event}, device_id);
+  }
+
+  std::vector<TestEventVariant> SendKeyEvents(
+      const std::vector<TestEventVariant>& events,
+      int device_id) {
+    // Just in case some events may be there.
+    if (!sink_.TakeEvents().empty()) {
+      ADD_FAILURE() << "Rewritten events were left";
+    }
+
+    // Convert TestKeyEvent into ui::KeyEvent, then dispatch it to the
+    // rewriter.
+    for (const TestEventVariant& event : events) {
+      if (const auto* test_key_event = std::get_if<TestKeyEvent>(&event)) {
+        ui::KeyEvent key_event(test_key_event->type, test_key_event->keycode,
+                               test_key_event->code, test_key_event->flags,
+                               test_key_event->key, ui::EventTimeForNow());
+        key_event.set_source_device_id(device_id);
+        ui::EventDispatchDetails details = source_.Send(&key_event);
+        CHECK(!details.dispatcher_destroyed);
+      } else {
+        const auto* test_mouse_event = std::get_if<TestMouseEvent>(&event);
+        CHECK(test_mouse_event);
+        ui::MouseEvent mouse_event(test_mouse_event->type,
+                                   /*location=*/gfx::PointF{},
+                                   /*root_location=*/gfx::PointF{},
+                                   /*time_stamp=*/ui::EventTimeForNow(),
+                                   test_mouse_event->flags,
+                                   test_mouse_event->changed_button_flags);
+        mouse_event.set_source_device_id(device_id);
+        if (test_mouse_event->linux_key_code) {
+          ui::SetForwardBackMouseButtonProperty(
+              mouse_event, test_mouse_event->linux_key_code);
+        }
+        ui::EventDispatchDetails details = source_.Send(&mouse_event);
+        CHECK(!details.dispatcher_destroyed);
+      }
+    }
+
+    // Convert the rewritten ui::Events back to TestKeyEvent.
+    auto rewritten_events = sink_.TakeEvents();
+    std::vector<TestEventVariant> result;
+    for (const auto& rewritten_event : rewritten_events) {
+      if (rewritten_event->IsKeyEvent()) {
+        auto* rewritten_key_event = rewritten_event->AsKeyEvent();
+        result.push_back(TestKeyEvent{
+            rewritten_key_event->type(), rewritten_key_event->code(),
+            rewritten_key_event->GetDomKey(), rewritten_key_event->key_code(),
+            rewritten_key_event->flags()});
+      } else if (rewritten_event->IsMouseEvent()) {
+        auto* rewritten_mouse_event = rewritten_event->AsMouseEvent();
+        auto property = ui::GetForwardBackMouseButtonProperty(*rewritten_event);
+        result.push_back(TestMouseEvent{
+            rewritten_mouse_event->type(), rewritten_mouse_event->flags(),
+            rewritten_mouse_event->changed_button_flags(),
+            property.value_or(0)});
+      } else {
+        ADD_FAILURE() << "Unexpected rewritten event: "
+                      << rewritten_event->ToString();
+        continue;
+      }
+    }
+    return result;
+  }
+
  protected:
   std::unique_ptr<PeripheralCustomizationEventRewriter> rewriter_;
   std::unique_ptr<InputDeviceSettingsController::ScopedResetterForTest>
       controller_scoped_resetter_;
-  std::unique_ptr<TestInputDeviceSettingsController> controller_;
+  std::unique_ptr<testing::NiceMock<TestInputDeviceSettingsController>>
+      controller_;
   base::test::ScopedFeatureList scoped_feature_list_;
   mojom::MouseSettingsPtr mouse_settings_;
   mojom::GraphicsTabletSettingsPtr graphics_tablet_settings_;
   std::unique_ptr<InputDeviceSettingsMetricsManager> metrics_manager_;
+
+  TestEventSink sink_;
+  ui::test::TestEventSource source_{&sink_};
 };
 
 TEST_F(PeripheralCustomizationEventRewriterTest, MouseButtonWithoutObserving) {
-  TestEventRewriterContinuation continuation;
-
-  auto back_mouse_event = CreateMouseButtonEvent(
-      ui::ET_MOUSE_PRESSED, ui::EF_BACK_MOUSE_BUTTON, ui::EF_BACK_MOUSE_BUTTON);
-
-  rewriter_->RewriteEvent(back_mouse_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  ASSERT_TRUE(continuation.passthrough_event->IsMouseEvent());
-  EXPECT_EQ(ConvertToString(back_mouse_event),
-            ConvertToString(*continuation.passthrough_event));
+  EXPECT_EQ(ButtonBack::Typed(), RunRewriter(ButtonBack::Typed()));
 }
 
 TEST_F(PeripheralCustomizationEventRewriterTest,
@@ -361,24 +696,16 @@
                                   mojom::RemappingAction::NewAcceleratorAction(
                                       AcceleratorAction::kBrightnessDown)));
 
-  rewriter_->RewriteEvent(CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_A),
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-  EXPECT_TRUE(continuation.discarded());
+  EXPECT_EQ(std::vector<TestEventVariant>{}, RunRewriter({KeyA::Pressed()}));
   ASSERT_TRUE(accelerator_observer.has_action_performed());
-  EXPECT_EQ(AcceleratorAction::kBrightnessDown,
-            accelerator_observer.action_performed());
 
-  continuation.reset();
   accelerator_observer.reset();
-  rewriter_->RewriteEvent(CreateKeyButtonEvent(ui::ET_KEY_RELEASED, ui::VKEY_A),
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-  EXPECT_TRUE(continuation.discarded());
+  EXPECT_EQ(std::vector<TestEventVariant>{}, RunRewriter({KeyA::Released()}));
   ASSERT_FALSE(accelerator_observer.has_action_performed());
 }
 
 TEST_F(PeripheralCustomizationEventRewriterTest, MouseEventActionRewriting) {
   TestAcceleratorObserver accelerator_observer;
-  TestEventRewriterContinuation continuation;
 
   mouse_settings_->button_remappings.push_back(mojom::ButtonRemapping::New(
       "",
@@ -386,22 +713,13 @@
       mojom::RemappingAction::NewAcceleratorAction(
           AcceleratorAction::kLaunchApp0)));
 
-  rewriter_->RewriteEvent(
-      CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED, ui::EF_MIDDLE_MOUSE_BUTTON,
-                             ui::EF_MIDDLE_MOUSE_BUTTON),
-      continuation.weak_ptr_factory_.GetWeakPtr());
-  EXPECT_TRUE(continuation.discarded());
+  EXPECT_EQ(std::vector<TestEventVariant>{},
+            RunRewriter({ButtonMiddle::Pressed()}));
   ASSERT_TRUE(accelerator_observer.has_action_performed());
-  EXPECT_EQ(AcceleratorAction::kLaunchApp0,
-            accelerator_observer.action_performed());
 
-  continuation.reset();
   accelerator_observer.reset();
-  rewriter_->RewriteEvent(
-      CreateMouseButtonEvent(ui::ET_MOUSE_RELEASED, ui::EF_MIDDLE_MOUSE_BUTTON,
-                             ui::EF_MIDDLE_MOUSE_BUTTON),
-      continuation.weak_ptr_factory_.GetWeakPtr());
-  EXPECT_TRUE(continuation.discarded());
+  EXPECT_EQ(std::vector<TestEventVariant>{},
+            RunRewriter({ButtonMiddle::Released()}));
   ASSERT_FALSE(accelerator_observer.has_action_performed());
 }
 
@@ -428,7 +746,6 @@
 
 TEST_F(PeripheralCustomizationEventRewriterTest,
        MouseEventFlagAppliedOnRelease) {
-  TestEventRewriterContinuation continuation;
   mouse_settings_->button_remappings.push_back(mojom::ButtonRemapping::New(
       /*name=*/"",
       mojom::Button::NewCustomizableButton(mojom::CustomizableButton::kMiddle),
@@ -439,29 +756,11 @@
       mojom::RemappingAction::NewStaticShortcutAction(
           mojom::StaticShortcutAction::kMiddleClick)));
 
-  ui::KeyEvent key_press_event =
-      CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_0);
-  ui::KeyEvent key_release_event =
-      CreateKeyButtonEvent(ui::ET_KEY_RELEASED, ui::VKEY_0);
-
-  rewriter_->RewriteEvent(key_press_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  continuation.reset();
-  rewriter_->RewriteEvent(key_release_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-
-  ui::MouseEvent expected_event(
-      ui::ET_MOUSE_RELEASED, gfx::PointF{}, gfx::PointF{}, /*time_stamp=*/{},
-      ui::EF_MIDDLE_MOUSE_BUTTON, ui::EF_MIDDLE_MOUSE_BUTTON);
-  EXPECT_EQ(ConvertToString(expected_event),
-            ConvertToString(*continuation.passthrough_event));
+  EXPECT_EQ(ButtonMiddle::Typed(), RunRewriter(KeyDigit0::Typed()));
 }
 
 TEST_F(PeripheralCustomizationEventRewriterTest,
        KeyEventFlagNotAppliedOnRelease) {
-  TestEventRewriterContinuation continuation;
   mouse_settings_->button_remappings.push_back(mojom::ButtonRemapping::New(
       /*name=*/"", mojom::Button::NewVkey(ui::VKEY_0),
       mojom::RemappingAction::NewKeyEvent(mojom::KeyEvent::New(
@@ -469,24 +768,7 @@
           static_cast<int>(ui::DomKey::CONTROL), ui::EF_CONTROL_DOWN,
           /*key_display=*/""))));
 
-  ui::KeyEvent key_press_event =
-      CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_0);
-  ui::KeyEvent key_release_event =
-      CreateKeyButtonEvent(ui::ET_KEY_RELEASED, ui::VKEY_0);
-
-  rewriter_->RewriteEvent(key_press_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  continuation.reset();
-  rewriter_->RewriteEvent(key_release_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-
-  ui::KeyEvent expected_event(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL,
-                              ui::DomCode::CONTROL_LEFT, ui::EF_NONE,
-                              ui::DomKey::CONTROL, /*time_stamp=*/{});
-  EXPECT_EQ(ConvertToString(expected_event),
-            ConvertToString(*continuation.passthrough_event));
+  EXPECT_EQ(KeyLControl::Typed(), RunRewriter(KeyDigit0::Typed()));
 }
 
 TEST_F(PeripheralCustomizationEventRewriterTest,
@@ -514,35 +796,22 @@
 
   layout_engine->SetCustomLookupTableForTesting(us_table);
 
-  TestEventRewriterContinuation continuation;
   mouse_settings_->button_remappings.push_back(mojom::ButtonRemapping::New(
       /*name=*/"", mojom::Button::NewVkey(ui::VKEY_0),
       mojom::RemappingAction::NewKeyEvent(mojom::KeyEvent::New(
           ui::VKEY_OEM_MINUS, static_cast<int>(ui::DomCode::MINUS),
           static_cast<int>(ui::DomKey::Constant<'-'>::Character), ui::EF_NONE,
           /*key_display=*/""))));
-
-  rewriter_->RewriteEvent(CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_0),
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(CreateKeyButtonEvent(
-                ui::ET_KEY_PRESSED, ui::VKEY_OEM_MINUS, ui::EF_NONE,
-                ui::DomCode::MINUS, ui::DomKey::Constant<'-'>::Character)),
-            ConvertToString(*continuation.passthrough_event));
+  EXPECT_EQ(KeyMinus::Typed(), RunRewriter(KeyDigit0::Typed()));
 
   // Switch to German (DE) layout table and expect the remapped button to have a
   // different VKEY and DomKey.
   layout_engine->SetCustomLookupTableForTesting(de_table);
   ash::AcceleratorKeycodeLookupCache::Get()->Clear();
 
-  continuation.reset();
-  rewriter_->RewriteEvent(CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_0),
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(CreateKeyButtonEvent(
-                ui::ET_KEY_PRESSED, ui::VKEY_OEM_4, ui::EF_NONE,
-                ui::DomCode::MINUS, ui::DomKey::Constant<u'ß'>::Character)),
-            ConvertToString(*continuation.passthrough_event));
+  EXPECT_EQ((TestKey<ui::DomCode::MINUS, ui::DomKey::Constant<u'ß'>::Character,
+                     ui::VKEY_OEM_4>::Typed()),
+            RunRewriter(KeyDigit0::Typed()));
 }
 
 TEST_F(PeripheralCustomizationEventRewriterTest,
@@ -555,14 +824,8 @@
           static_cast<int>(ui::DomKey::Constant<'a'>::Character), ui::EF_NONE,
           /*key_display=*/""))));
 
-  rewriter_->RewriteEvent(
-      CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_0, ui::EF_SHIFT_DOWN),
-      continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(CreateKeyButtonEvent(
-                ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_SHIFT_DOWN,
-                ui::DomCode::US_A, ui::DomKey::Constant<'A'>::Character)),
-            ConvertToString(*continuation.passthrough_event));
+  EXPECT_EQ(KeyA::Typed(ui::EF_SHIFT_DOWN),
+            RunRewriter(KeyDigit0::Typed(ui::EF_SHIFT_DOWN)));
 }
 
 TEST_F(PeripheralCustomizationEventRewriterTest,
@@ -576,16 +839,8 @@
           static_cast<int>(ui::DomKey::Constant<'a'>::Character), ui::EF_NONE,
           /*key_display=*/""))));
 
-  rewriter_->RewriteEvent(
-      CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                             ui::EF_SHIFT_DOWN | ui::EF_FORWARD_MOUSE_BUTTON,
-                             ui::EF_FORWARD_MOUSE_BUTTON),
-      continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(CreateKeyButtonEvent(
-                ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_SHIFT_DOWN,
-                ui::DomCode::US_A, ui::DomKey::Constant<'A'>::Character)),
-            ConvertToString(*continuation.passthrough_event));
+  EXPECT_EQ(KeyA::Typed(ui::EF_SHIFT_DOWN),
+            RunRewriter(ButtonForward::Typed(ui::EF_SHIFT_DOWN)));
 }
 
 TEST_F(PeripheralCustomizationEventRewriterTest,
@@ -599,27 +854,10 @@
           static_cast<int>(ui::DomKey::SHIFT), ui::EF_SHIFT_DOWN,
           /*key_display=*/""))));
 
-  rewriter_->RewriteEvent(
-      CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED, ui::EF_FORWARD_MOUSE_BUTTON,
-                             ui::EF_FORWARD_MOUSE_BUTTON),
-      continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(CreateKeyButtonEvent(
-                ui::ET_KEY_PRESSED, ui::VKEY_SHIFT, ui::EF_SHIFT_DOWN,
-                ui::DomCode::SHIFT_LEFT, ui::DomKey::SHIFT)),
-            ConvertToString(*continuation.passthrough_event));
-
-  continuation.reset();
-  rewriter_->RewriteEvent(
-      CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE,
-                           ui::DomCode::US_A,
-                           ui::DomKey::Constant<'a'>::Character),
-      continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(CreateKeyButtonEvent(
-                ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_SHIFT_DOWN,
-                ui::DomCode::US_A, ui::DomKey::Constant<'A'>::Character)),
-            ConvertToString(*continuation.passthrough_event));
+  EXPECT_EQ(std::vector<TestEventVariant>{KeyLShift::Pressed()},
+            RunRewriter({ButtonForward::Pressed()}));
+  EXPECT_EQ(KeyA::Typed(ui::EF_SHIFT_DOWN),
+            RunRewriter(KeyA::Typed(), ui::EF_NONE, kRandomKeyboardDeviceId));
 }
 
 class MouseButtonObserverTest
@@ -632,93 +870,63 @@
     testing::ValuesIn(std::vector<EventRewriterTestData>{
         // MouseEvent tests:
         {
-            CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                   ui::EF_BACK_MOUSE_BUTTON,
-                                   ui::EF_BACK_MOUSE_BUTTON),
+            ButtonBack::Pressed(),
             std::nullopt,
             mojom::CustomizableButton::kBack,
         },
         {
-            CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                   ui::EF_FORWARD_MOUSE_BUTTON,
-                                   ui::EF_FORWARD_MOUSE_BUTTON),
+            ButtonForward::Pressed(),
             std::nullopt,
             mojom::CustomizableButton::kForward,
         },
         {
-            CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                   ui::EF_MIDDLE_MOUSE_BUTTON,
-                                   ui::EF_MIDDLE_MOUSE_BUTTON),
+            ButtonMiddle::Pressed(),
             std::nullopt,
             mojom::CustomizableButton::kMiddle,
         },
         {
-            CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                   ui::EF_MIDDLE_MOUSE_BUTTON |
-                                       ui::EF_LEFT_MOUSE_BUTTON,
-                                   ui::EF_MIDDLE_MOUSE_BUTTON),
+            ButtonMiddle::Pressed(ui::EF_LEFT_MOUSE_BUTTON),
             std::nullopt,
             mojom::CustomizableButton::kMiddle,
         },
 
         // Observer notified only when mouse button pressed.
-        {CreateMouseButtonEvent(ui::ET_MOUSE_RELEASED,
-                                ui::EF_BACK_MOUSE_BUTTON,
-                                ui::EF_BACK_MOUSE_BUTTON),
+        {ButtonBack::Released(),
          /*rewritten_event=*/std::nullopt},
 
         // Left click ignored for buttons from a mouse.
-        {CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                ui::EF_LEFT_MOUSE_BUTTON,
-                                ui::EF_LEFT_MOUSE_BUTTON),
-         CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                ui::EF_LEFT_MOUSE_BUTTON,
-                                ui::EF_LEFT_MOUSE_BUTTON)},
+        {ButtonLeft::Pressed(), ButtonLeft::Pressed()},
 
         // Right click ignored for buttons from a mouse.
         {
-            CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                   ui::EF_RIGHT_MOUSE_BUTTON,
-                                   ui::EF_RIGHT_MOUSE_BUTTON),
-            CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                   ui::EF_RIGHT_MOUSE_BUTTON,
-                                   ui::EF_RIGHT_MOUSE_BUTTON),
+            ButtonRight::Pressed(),
+            ButtonRight::Pressed(),
         },
 
         // Other flags are ignored when included in the event with other
         // buttons.
-        {CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                ui::EF_LEFT_MOUSE_BUTTON |
-                                    ui::EF_BACK_MOUSE_BUTTON,
-                                ui::EF_LEFT_MOUSE_BUTTON),
-         CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                ui::EF_LEFT_MOUSE_BUTTON,
-                                ui::EF_LEFT_MOUSE_BUTTON)},
-        {CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                ui::EF_RIGHT_MOUSE_BUTTON |
-                                    ui::EF_MIDDLE_MOUSE_BUTTON,
-                                ui::EF_NONE),
-         CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                ui::EF_RIGHT_MOUSE_BUTTON,
-                                ui::EF_NONE)},
+        {ButtonLeft::Pressed(ui::EF_BACK_MOUSE_BUTTON), ButtonLeft::Pressed()},
+
+        {
+            ButtonRight::Pressed(ui::EF_MIDDLE_MOUSE_BUTTON),
+            ButtonRight::Pressed(),
+        },
 
         // KeyEvent tests:
         {
-            CreateKeyButtonEvent(ui::ET_KEY_PRESSED,
-                                 ui::VKEY_A,
-                                 ui::EF_COMMAND_DOWN),
+            KeyA::Pressed(ui::EF_COMMAND_DOWN),
             std::nullopt,
             ui::VKEY_A,
         },
         {
-            CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_B, ui::EF_NONE),
+            KeyB::Pressed(),
             std::nullopt,
             ui::VKEY_B,
         },
 
         // Test that key releases are consumed, but not sent to observers.
         {
-            CreateKeyButtonEvent(ui::ET_KEY_RELEASED, ui::VKEY_A),
+            KeyB::Released(),
             std::nullopt,
         },
     }),
@@ -726,6 +934,9 @@
       std::string name = ConvertToString(info.param.incoming_event);
       std::replace(name.begin(), name.end(), ' ', '_');
       std::replace(name.begin(), name.end(), '=', '_');
+      std::replace(name.begin(), name.end(), '_', '_');
+      std::replace(name.begin(), name.end(), '(', '_');
+      std::replace(name.begin(), name.end(), ')', '_');
       return name;
     });
 
@@ -737,11 +948,9 @@
       /*customization_restriction=*/mojom::CustomizationRestriction::
           kAllowCustomizations);
 
-  TestEventRewriterContinuation continuation;
-  rewriter_->RewriteEvent(GetEventFromVariant(data.incoming_event),
-                          continuation.weak_ptr_factory_.GetWeakPtr());
+  auto rewritten_events = RunRewriter({data.incoming_event});
   if (!data.rewritten_event) {
-    ASSERT_TRUE(continuation.discarded());
+    ASSERT_TRUE(rewritten_events.empty());
     if (data.pressed_button) {
       const auto& actual_pressed_buttons =
           controller_->pressed_mouse_buttons().at(kMouseDeviceId);
@@ -749,21 +958,16 @@
       EXPECT_EQ(*data.pressed_button, *actual_pressed_buttons[0]);
     }
   } else {
-    ASSERT_TRUE(continuation.passthrough_event);
-    EXPECT_EQ(ConvertToString(*data.rewritten_event),
-              ConvertToString(*continuation.passthrough_event));
+    ASSERT_FALSE(rewritten_events.empty());
+    EXPECT_EQ(*data.rewritten_event, rewritten_events.front());
   }
 
   rewriter_->StopObserving();
-  continuation.reset();
 
   // After we stop observing, the passthrough event should be an identity of the
   // original.
-  rewriter_->RewriteEvent(GetEventFromVariant(data.incoming_event),
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(data.incoming_event),
-            ConvertToString(*continuation.passthrough_event));
+  EXPECT_EQ(std::vector<TestEventVariant>{data.incoming_event},
+            RunRewriter({data.incoming_event}));
 }
 
 TEST_F(MouseButtonObserverTest, MouseBackButtonRecognition) {
@@ -772,13 +976,7 @@
       /*customization_restriction=*/mojom::CustomizationRestriction::
           kAllowCustomizations);
 
-  ui::MouseEvent incoming_event = CreateMouseButtonEvent(
-      ui::ET_MOUSE_PRESSED, ui::EF_BACK_MOUSE_BUTTON, ui::EF_BACK_MOUSE_BUTTON);
-  ui::SetForwardBackMouseButtonProperty(incoming_event, BTN_BACK);
-
-  TestEventRewriterContinuation continuation;
-  rewriter_->RewriteEvent(incoming_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
+  EXPECT_EQ(std::vector<TestEventVariant>{}, RunRewriter(ButtonBack::Typed()));
 
   const auto& actual_pressed_buttons =
       controller_->pressed_mouse_buttons().at(kMouseDeviceId);
@@ -794,13 +992,7 @@
       /*customization_restriction=*/mojom::CustomizationRestriction::
           kAllowCustomizations);
 
-  ui::MouseEvent incoming_event = CreateMouseButtonEvent(
-      ui::ET_MOUSE_PRESSED, ui::EF_BACK_MOUSE_BUTTON, ui::EF_BACK_MOUSE_BUTTON);
-  ui::SetForwardBackMouseButtonProperty(incoming_event, BTN_SIDE);
-
-  TestEventRewriterContinuation continuation;
-  rewriter_->RewriteEvent(incoming_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
+  EXPECT_EQ(std::vector<TestEventVariant>{}, RunRewriter(ButtonSide::Typed()));
 
   const auto& actual_pressed_buttons =
       controller_->pressed_mouse_buttons().at(kMouseDeviceId);
@@ -816,14 +1008,8 @@
       /*customization_restriction=*/mojom::CustomizationRestriction::
           kAllowCustomizations);
 
-  ui::MouseEvent incoming_event =
-      CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED, ui::EF_FORWARD_MOUSE_BUTTON,
-                             ui::EF_FORWARD_MOUSE_BUTTON);
-  ui::SetForwardBackMouseButtonProperty(incoming_event, BTN_FORWARD);
-
-  TestEventRewriterContinuation continuation;
-  rewriter_->RewriteEvent(incoming_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
+  EXPECT_EQ(std::vector<TestEventVariant>{},
+            RunRewriter(ButtonForward::Typed()));
 
   const auto& actual_pressed_buttons =
       controller_->pressed_mouse_buttons().at(kMouseDeviceId);
@@ -839,14 +1025,7 @@
       /*customization_restriction=*/mojom::CustomizationRestriction::
           kAllowCustomizations);
 
-  ui::MouseEvent incoming_event =
-      CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED, ui::EF_FORWARD_MOUSE_BUTTON,
-                             ui::EF_FORWARD_MOUSE_BUTTON);
-  ui::SetForwardBackMouseButtonProperty(incoming_event, BTN_EXTRA);
-
-  TestEventRewriterContinuation continuation;
-  rewriter_->RewriteEvent(incoming_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
+  EXPECT_EQ(std::vector<TestEventVariant>{}, RunRewriter(ButtonExtra::Typed()));
 
   const auto& actual_pressed_buttons =
       controller_->pressed_mouse_buttons().at(kMouseDeviceId);
@@ -856,70 +1035,35 @@
       *actual_pressed_buttons[0]);
 }
 
-TEST_F(MouseButtonObserverTest, BlockEventRewritingForKeyEvent) {
-  TestEventRewriterContinuation continuation;
-
+TEST_F(MouseButtonObserverTest, kDisableKeyEventRewritesRestriction) {
   rewriter_->StartObservingMouse(
       kMouseDeviceId,
       /*customization_restriction=*/mojom::CustomizationRestriction::
-          kDisallowCustomizations);
+          kDisableKeyEventRewrites);
 
-  ui::KeyEvent key_event =
-      CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_COMMAND_DOWN);
-  rewriter_->RewriteEvent(key_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-  // Key event shouldn't be discarded if the mouse can't rewrite key
-  // events.
-  ASSERT_TRUE(continuation.passthrough_event);
-  ASSERT_TRUE(continuation.passthrough_event->IsKeyEvent());
-  EXPECT_EQ(ConvertToString(key_event),
-            ConvertToString(*continuation.passthrough_event));
+  // Key events should not be modified if no key event customizations are
+  // allowed.
+  EXPECT_EQ(KeyA::Typed(ui::EF_COMMAND_DOWN),
+            RunRewriter(KeyA::Typed(ui::EF_COMMAND_DOWN)));
 
-  ui::MouseEvent mouse_event =
-      CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED, ui::EF_RIGHT_MOUSE_BUTTON,
-                             ui::EF_RIGHT_MOUSE_BUTTON);
-  continuation.reset();
-  rewriter_->RewriteEvent(mouse_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-  // Mouse event shouldn't be discarded if the mouse can't rewrite this
-  // mouse event.
-  ASSERT_TRUE(continuation.passthrough_event);
-  ASSERT_TRUE(continuation.passthrough_event->IsMouseEvent());
-  EXPECT_EQ(ConvertToString(mouse_event),
-            ConvertToString(*continuation.passthrough_event));
+  // Mouse event should be discarded if only key event rewrites aren't allowed.
+  EXPECT_EQ(std::vector<TestEventVariant>{}, RunRewriter(ButtonSide::Typed()));
+}
 
-  rewriter_->StopObserving();
-  continuation.reset();
-
+TEST_F(MouseButtonObserverTest, AllowCustomizationsRestriction) {
   rewriter_->StartObservingMouse(
       kMouseDeviceId,
       /*customization_restriction=*/mojom::CustomizationRestriction::
           kAllowCustomizations);
 
-  ui::KeyEvent new_key_event =
-      CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_COMMAND_DOWN);
-  rewriter_->RewriteEvent(new_key_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-  // New key event should be discarded if the mouse can rewrite key
-  // events.
-  ASSERT_TRUE(continuation.discarded());
-  EXPECT_EQ(nullptr, continuation.passthrough_event);
-
-  ui::MouseEvent new_mouse_event =
-      CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED, ui::EF_MIDDLE_MOUSE_BUTTON,
-                             ui::EF_MIDDLE_MOUSE_BUTTON);
-  continuation.reset();
-  rewriter_->RewriteEvent(new_mouse_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-  // New mouse event should be discarded if the mouse can rewrite this
-  // mouse event.
-  ASSERT_TRUE(continuation.discarded());
-  EXPECT_EQ(nullptr, continuation.passthrough_event);
+  // kAllowCustomizations should swallow both key events and mouse events.
+  EXPECT_EQ(std::vector<TestEventVariant>{},
+            RunRewriter(KeyA::Typed(ui::EF_COMMAND_DOWN)));
+  EXPECT_EQ(std::vector<TestEventVariant>{}, RunRewriter(ButtonBack::Typed()));
 }
 
 TEST_F(PeripheralCustomizationEventRewriterTest,
        RewriteEventFromButtonEmitMetrics) {
-  TestEventRewriterContinuation continuation;
   base::HistogramTester histogram_tester;
   mouse_settings_->button_remappings.push_back(
       mojom::ButtonRemapping::New("", mojom::Button::NewVkey(ui::VKEY_A),
@@ -940,28 +1084,20 @@
       "Pressed",
       /*expected_count=*/0);
 
-  rewriter_->RewriteEvent(
-      CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_COMMAND_DOWN),
-      continuation.weak_ptr_factory_.GetWeakPtr());
+  RunRewriter(KeyA::Typed(), ui::EF_NONE, kMouseDeviceId);
 
   histogram_tester.ExpectTotalCount(
       "ChromeOS.Settings.Device.Mouse.ButtonRemapping.AcceleratorAction."
       "Pressed",
       /*expected_count=*/1u);
 
-  continuation.reset();
-
-  auto pen_button_event =
-      CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_Z, ui::EF_COMMAND_DOWN);
-  pen_button_event.set_source_device_id(kGraphicsTabletDeviceId);
-
   histogram_tester.ExpectTotalCount(
       "ChromeOS.Settings.Device.GraphicsTabletPen.ButtonRemapping.KeyEvent."
       "Pressed",
       /*expected_count=*/0);
 
-  rewriter_->RewriteEvent(pen_button_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
+  RunRewriter(KeyZ::Typed(), ui::EF_NONE, kGraphicsTabletDeviceId);
+
   histogram_tester.ExpectTotalCount(
       "ChromeOS.Settings.Device.GraphicsTabletPen.ButtonRemapping.KeyEvent."
       "Pressed",
@@ -976,26 +1112,13 @@
       /*customization_restriction=*/mojom::CustomizationRestriction::
           kAllowAlphabetKeyEventRewrites);
 
-  ui::KeyEvent key_event = CreateKeyButtonEvent(
-      ui::ET_KEY_PRESSED, ui::VKEY_LEFT, ui::EF_COMMAND_DOWN);
-  rewriter_->RewriteEvent(key_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-  // Key event shouldn't be discarded if the key
-  // code is not alphabet letter.
-  ASSERT_TRUE(continuation.passthrough_event);
-  ASSERT_TRUE(continuation.passthrough_event->IsKeyEvent());
-  EXPECT_EQ(ConvertToString(key_event),
-            ConvertToString(*continuation.passthrough_event));
+  // Key event shouldn't be discarded if the key code is not alphabet letter.
+  EXPECT_EQ(KeyArrowLeft::Typed(ui::EF_COMMAND_DOWN),
+            RunRewriter(KeyArrowLeft::Typed(ui::EF_COMMAND_DOWN)));
 
-  ui::KeyEvent new_key_event =
-      CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_COMMAND_DOWN);
-  continuation.reset();
-  rewriter_->RewriteEvent(new_key_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-  // New key event should be discarded if the key
-  // code is alphabet letter.
-  ASSERT_TRUE(continuation.discarded());
-  EXPECT_EQ(nullptr, continuation.passthrough_event);
+  // New key event should be discarded if the key code is alphabet letter.
+  EXPECT_EQ(std::vector<TestEventVariant>{},
+            RunRewriter(KeyA::Typed(ui::EF_COMMAND_DOWN)));
 }
 
 TEST_F(MouseButtonObserverTest, RewriteAlphabetOrNumberKeyEvent) {
@@ -1006,36 +1129,17 @@
       /*customization_restriction=*/mojom::CustomizationRestriction::
           kAllowAlphabetOrNumberKeyEventRewrites);
 
-  ui::KeyEvent key_event = CreateKeyButtonEvent(
-      ui::ET_KEY_PRESSED, ui::VKEY_LEFT, ui::EF_COMMAND_DOWN);
-  rewriter_->RewriteEvent(key_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-  // Key event shouldn't be discarded if the key
-  // code is not alphabet letter or number.
-  ASSERT_TRUE(continuation.passthrough_event);
-  ASSERT_TRUE(continuation.passthrough_event->IsKeyEvent());
-  EXPECT_EQ(ConvertToString(key_event),
-            ConvertToString(*continuation.passthrough_event));
+  // Key event shouldn't be discarded if the key code is not alphabet letter or
+  // number.
+  EXPECT_EQ(KeyArrowLeft::Typed(ui::EF_COMMAND_DOWN),
+            RunRewriter(KeyArrowLeft::Typed(ui::EF_COMMAND_DOWN)));
 
-  ui::KeyEvent new_alphabet_key_event =
-      CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_COMMAND_DOWN);
-  continuation.reset();
-  rewriter_->RewriteEvent(new_alphabet_key_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-  // New key event should be discarded if the key
-  // code is alphabet letter.
-  ASSERT_TRUE(continuation.discarded());
-  EXPECT_EQ(nullptr, continuation.passthrough_event);
+  // New key event should be discarded if the key code is alphabet letter.
+  EXPECT_EQ(std::vector<TestEventVariant>{}, RunRewriter(KeyA::Typed()));
 
-  ui::KeyEvent new_number_key_event =
-      CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_0, ui::EF_COMMAND_DOWN);
-  continuation.reset();
-  rewriter_->RewriteEvent(new_number_key_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-  // New key event should be discarded if the key
-  // code is number.
-  ASSERT_TRUE(continuation.discarded());
-  EXPECT_EQ(nullptr, continuation.passthrough_event);
+  // New key event should be discarded if the key code is a number.
+  EXPECT_EQ(std::vector<TestEventVariant>{},
+            RunRewriter(KeyDigit0::Typed(ui::EF_COMMAND_DOWN)));
 }
 
 class GraphicsTabletButtonObserverTest
@@ -1047,98 +1151,69 @@
     GraphicsTabletButtonObserverTest,
     testing::ValuesIn(std::vector<EventRewriterTestData>{
         {
-            CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                   ui::EF_RIGHT_MOUSE_BUTTON,
-                                   ui::EF_RIGHT_MOUSE_BUTTON),
+            ButtonRight::Pressed(),
             std::nullopt,
             mojom::CustomizableButton::kRight,
         },
         {
-            CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                   ui::EF_BACK_MOUSE_BUTTON,
-                                   ui::EF_BACK_MOUSE_BUTTON),
+            ButtonBack::Pressed(),
             std::nullopt,
             mojom::CustomizableButton::kBack,
         },
         {
-            CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                   ui::EF_FORWARD_MOUSE_BUTTON,
-                                   ui::EF_FORWARD_MOUSE_BUTTON),
+            ButtonForward::Pressed(),
             std::nullopt,
             mojom::CustomizableButton::kForward,
         },
         {
-            CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                   ui::EF_MIDDLE_MOUSE_BUTTON,
-                                   ui::EF_MIDDLE_MOUSE_BUTTON),
+            ButtonMiddle::Pressed(),
             std::nullopt,
             mojom::CustomizableButton::kMiddle,
         },
         {
-            CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                   ui::EF_MIDDLE_MOUSE_BUTTON |
-                                       ui::EF_LEFT_MOUSE_BUTTON,
-                                   ui::EF_MIDDLE_MOUSE_BUTTON),
+            ButtonMiddle::Pressed(ui::EF_LEFT_MOUSE_BUTTON),
             std::nullopt,
             mojom::CustomizableButton::kMiddle,
         },
 
         // Observer notified only when the button is pressed.
         {
-            CreateMouseButtonEvent(ui::ET_MOUSE_RELEASED,
-                                   ui::EF_BACK_MOUSE_BUTTON,
-                                   ui::EF_BACK_MOUSE_BUTTON),
+            ButtonBack::Released(),
             std::nullopt,
         },
 
         // Left click ignored for buttons from a graphics tablet.
         {
-            CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                   ui::EF_LEFT_MOUSE_BUTTON,
-                                   ui::EF_LEFT_MOUSE_BUTTON),
-            CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                   ui::EF_LEFT_MOUSE_BUTTON,
-                                   ui::EF_LEFT_MOUSE_BUTTON),
+            ButtonLeft::Pressed(),
+            ButtonLeft::Pressed(),
         },
 
         // Other flags are ignored when included in the event with other
         // buttons.
         {
-            CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                   ui::EF_LEFT_MOUSE_BUTTON |
-                                       ui::EF_BACK_MOUSE_BUTTON,
-                                   ui::EF_LEFT_MOUSE_BUTTON),
-            CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                   ui::EF_LEFT_MOUSE_BUTTON,
-                                   ui::EF_LEFT_MOUSE_BUTTON),
+            ButtonLeft::Pressed(ui::EF_BACK_MOUSE_BUTTON),
+            ButtonLeft::Pressed(),
         },
         {
-            CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                   ui::EF_LEFT_MOUSE_BUTTON |
-                                       ui::EF_MIDDLE_MOUSE_BUTTON,
-                                   ui::EF_NONE),
-            CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                   ui::EF_LEFT_MOUSE_BUTTON,
-                                   ui::EF_NONE),
+            ButtonLeft::Pressed(ui::EF_MIDDLE_MOUSE_BUTTON),
+            ButtonLeft::Pressed(),
         },
 
         // KeyEvent tests:
         {
-            CreateKeyButtonEvent(ui::ET_KEY_PRESSED,
-                                 ui::VKEY_A,
-                                 ui::EF_COMMAND_DOWN),
+            KeyA::Pressed(ui::EF_COMMAND_DOWN),
             std::nullopt,
             ui::VKEY_A,
         },
         {
-            CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_B, ui::EF_NONE),
+            KeyB::Pressed(),
             std::nullopt,
             ui::VKEY_B,
         },
 
         // Test that key releases are consumed, but not sent to observers.
         {
-            CreateKeyButtonEvent(ui::ET_KEY_RELEASED, ui::VKEY_A),
+            KeyA::Released(ui::EF_COMMAND_DOWN),
             std::nullopt,
         },
     }),
@@ -1146,6 +1221,9 @@
       std::string name = ConvertToString(info.param.incoming_event);
       std::replace(name.begin(), name.end(), ' ', '_');
       std::replace(name.begin(), name.end(), '=', '_');
+      std::replace(name.begin(), name.end(), '_', '_');
+      std::replace(name.begin(), name.end(), '(', '_');
+      std::replace(name.begin(), name.end(), ')', '_');
       return name;
     });
 
@@ -1157,13 +1235,10 @@
       /*customization_restriction=*/mojom::CustomizationRestriction::
           kAllowCustomizations);
 
-  auto& event = GetEventFromVariant(data.incoming_event);
-  event.set_source_device_id(kGraphicsTabletDeviceId);
-
-  TestEventRewriterContinuation continuation;
-  rewriter_->RewriteEvent(event, continuation.weak_ptr_factory_.GetWeakPtr());
+  auto rewritten_events =
+      RunRewriter({data.incoming_event}, ui::EF_NONE, kGraphicsTabletDeviceId);
   if (!data.rewritten_event) {
-    ASSERT_TRUE(continuation.discarded());
+    ASSERT_TRUE(rewritten_events.empty());
     if (data.pressed_button) {
       const auto& actual_pressed_buttons =
           controller_->pressed_graphics_tablet_buttons().at(
@@ -1172,20 +1247,17 @@
       EXPECT_EQ(*data.pressed_button, *actual_pressed_buttons[0]);
     }
   } else {
-    ASSERT_TRUE(continuation.passthrough_event);
-    EXPECT_EQ(ConvertToString(*data.rewritten_event),
-              ConvertToString(*continuation.passthrough_event));
+    ASSERT_FALSE(rewritten_events.empty());
+    EXPECT_EQ(*data.rewritten_event, rewritten_events.front());
   }
 
   rewriter_->StopObserving();
-  continuation.reset();
 
   // After we stop observing, the passthrough event should be an identity of the
   // original.
-  rewriter_->RewriteEvent(event, continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(data.incoming_event),
-            ConvertToString(*continuation.passthrough_event));
+  EXPECT_EQ(
+      std::vector<TestEventVariant>{data.incoming_event},
+      RunRewriter({data.incoming_event}, ui::EF_NONE, kGraphicsTabletDeviceId));
 }
 
 class ButtonRewritingTest
@@ -1209,12 +1281,7 @@
                   static_cast<int>(ui::DomKey::Constant<'b'>::Character),
                   ui::EF_NONE,
                   /*key_display=*/"")},
-             {CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_A),
-              CreateKeyButtonEvent(ui::ET_KEY_PRESSED,
-                                   ui::VKEY_B,
-                                   ui::EF_NONE,
-                                   ui::DomCode::US_B,
-                                   ui::DomKey::Constant<'b'>::Character)}},
+             {KeyA::Pressed(), KeyB::Pressed()}},
             // Remap A -> B, Pressing B is no-op.
             {{GetButton(ui::VKEY_A),
               mojom::KeyEvent(
@@ -1223,8 +1290,7 @@
                   static_cast<int>(ui::DomKey::Constant<'b'>::Character),
                   ui::EF_NONE,
                   /*key_display=*/"")},
-             {CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_B),
-              CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_B)}},
+             {KeyB::Pressed(), KeyB::Pressed()}},
             // Remap CTRL -> ALT.
             {{GetButton(ui::VKEY_CONTROL),
               mojom::KeyEvent(ui::VKEY_MENU,
@@ -1232,14 +1298,7 @@
                               static_cast<int>(ui::DomKey::ALT),
                               ui::EF_ALT_DOWN,
                               /*key_display=*/"")},
-             {CreateKeyButtonEvent(ui::ET_KEY_PRESSED,
-                                   ui::VKEY_CONTROL,
-                                   ui::EF_CONTROL_DOWN),
-              CreateKeyButtonEvent(ui::ET_KEY_PRESSED,
-                                   ui::VKEY_MENU,
-                                   ui::EF_ALT_DOWN,
-                                   ui::DomCode::ALT_LEFT,
-                                   ui::DomKey::ALT)}},
+             {KeyLControl::Pressed(), KeyLAlt::Pressed()}},
             // Remap CTRL -> ALT and press with shift down.
             {{GetButton(ui::VKEY_CONTROL),
               mojom::KeyEvent(ui::VKEY_MENU,
@@ -1247,14 +1306,8 @@
                               static_cast<int>(ui::DomKey::ALT),
                               ui::EF_ALT_DOWN,
                               /*key_display=*/"")},
-             {CreateKeyButtonEvent(ui::ET_KEY_PRESSED,
-                                   ui::VKEY_CONTROL,
-                                   ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN),
-              CreateKeyButtonEvent(ui::ET_KEY_PRESSED,
-                                   ui::VKEY_MENU,
-                                   ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN,
-                                   ui::DomCode::ALT_LEFT,
-                                   ui::DomKey::ALT)}},
+             {KeyLControl::Pressed(ui::EF_SHIFT_DOWN),
+              KeyLAlt::Pressed(ui::EF_SHIFT_DOWN)}},
             // Remap A -> CTRL + SHIFT + B.
             {{GetButton(ui::VKEY_A),
               mojom::KeyEvent(
@@ -1263,12 +1316,8 @@
                   static_cast<int>(ui::DomKey::Constant<'b'>::Character),
                   ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN,
                   /*key_display=*/"")},
-             {CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE),
-              CreateKeyButtonEvent(ui::ET_KEY_PRESSED,
-                                   ui::VKEY_B,
-                                   ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN,
-                                   ui::DomCode::US_B,
-                                   ui::DomKey::Constant<'B'>::Character)}},
+             {KeyA::Pressed(),
+              KeyB::Pressed(ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN)}},
 
             // MouseEvent rewriting test cases:
             // Remap Middle -> CTRL + SHIFT + B.
@@ -1279,14 +1328,9 @@
                   static_cast<int>(ui::DomKey::Constant<'b'>::Character),
                   ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN,
                   /*key_display=*/"")},
-             {CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                     ui::EF_MIDDLE_MOUSE_BUTTON,
-                                     ui::EF_MIDDLE_MOUSE_BUTTON),
-              CreateKeyButtonEvent(ui::ET_KEY_PRESSED,
-                                   ui::VKEY_B,
-                                   ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN,
-                                   ui::DomCode::US_B,
-                                   ui::DomKey::Constant<'B'>::Character)}},
+             {ButtonMiddle::Pressed(),
+              KeyB::Pressed(ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN)}},
+
             // Remap Middle -> CTRL + SHIFT + B with ALT down.
             {{GetButton(mojom::CustomizableButton::kMiddle),
               mojom::KeyEvent(
@@ -1295,16 +1339,9 @@
                   static_cast<int>(ui::DomKey::Constant<'b'>::Character),
                   ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN,
                   /*key_display=*/"")},
-             {CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                     ui::EF_MIDDLE_MOUSE_BUTTON |
-                                         ui::EF_ALT_DOWN,
-                                     ui::EF_MIDDLE_MOUSE_BUTTON),
-              CreateKeyButtonEvent(ui::ET_KEY_PRESSED,
-                                   ui::VKEY_B,
-                                   ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN |
-                                       ui::EF_ALT_DOWN,
-                                   ui::DomCode::US_B,
-                                   ui::DomKey::Constant<'B'>::Character)}},
+             {ButtonMiddle::Pressed(ui::EF_ALT_DOWN),
+              KeyB::Pressed(ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN |
+                            ui::EF_ALT_DOWN)}},
             // Remap Back -> Meta.
             {{GetButton(mojom::CustomizableButton::kBack),
               mojom::KeyEvent(ui::VKEY_LWIN,
@@ -1312,14 +1349,7 @@
                               static_cast<int>(ui::DomKey::META),
                               ui::EF_COMMAND_DOWN,
                               /*key_display=*/"")},
-             {CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                     ui::EF_BACK_MOUSE_BUTTON,
-                                     ui::EF_BACK_MOUSE_BUTTON),
-              CreateKeyButtonEvent(ui::ET_KEY_PRESSED,
-                                   ui::VKEY_LWIN,
-                                   ui::EF_COMMAND_DOWN,
-                                   ui::DomCode::META_LEFT,
-                                   ui::DomKey::META)}},
+             {ButtonBack::Pressed(), KeyLMeta::Pressed()}},
             // Remap Middle -> B and check left mouse button is a no-op.
             {{GetButton(mojom::CustomizableButton::kMiddle),
               mojom::KeyEvent(
@@ -1328,13 +1358,8 @@
                   static_cast<int>(ui::DomKey::Constant<'b'>::Character),
                   ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN,
                   /*key_display=*/"")},
-             {CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                     ui::EF_LEFT_MOUSE_BUTTON | ui::EF_ALT_DOWN,
-                                     ui::EF_LEFT_MOUSE_BUTTON),
-              CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                                     ui::EF_LEFT_MOUSE_BUTTON | ui::EF_ALT_DOWN,
-                                     ui::EF_LEFT_MOUSE_BUTTON)}},
-
+             {ButtonLeft::Pressed(ui::EF_ALT_DOWN),
+              ButtonLeft::Pressed(ui::EF_ALT_DOWN)}},
         }));
 
 TEST_P(ButtonRewritingTest, GraphicsPenRewriteEvent) {
@@ -1346,15 +1371,9 @@
           "", button.Clone(),
           mojom::RemappingAction::NewKeyEvent(key_event.Clone())));
 
-  auto& event = GetEventFromVariant(data.incoming_event);
-  event.set_source_device_id(kGraphicsTabletDeviceId);
-
-  TestEventRewriterContinuation continuation;
-  rewriter_->RewriteEvent(event, continuation.weak_ptr_factory_.GetWeakPtr());
-
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(*data.rewritten_event),
-            ConvertToString(*continuation.passthrough_event));
+  EXPECT_EQ(
+      std::vector<TestEventVariant>{*data.rewritten_event},
+      RunRewriter({data.incoming_event}, ui::EF_NONE, kGraphicsTabletDeviceId));
 }
 
 TEST_P(ButtonRewritingTest, GraphicsTabletRewriteEvent) {
@@ -1366,15 +1385,9 @@
           "", button.Clone(),
           mojom::RemappingAction::NewKeyEvent(key_event.Clone())));
 
-  auto& event = GetEventFromVariant(data.incoming_event);
-  event.set_source_device_id(kGraphicsTabletDeviceId);
-
-  TestEventRewriterContinuation continuation;
-  rewriter_->RewriteEvent(event, continuation.weak_ptr_factory_.GetWeakPtr());
-
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(*data.rewritten_event),
-            ConvertToString(*continuation.passthrough_event));
+  EXPECT_EQ(
+      std::vector<TestEventVariant>{*data.rewritten_event},
+      RunRewriter({data.incoming_event}, ui::EF_NONE, kGraphicsTabletDeviceId));
 }
 
 TEST_P(ButtonRewritingTest, MouseRewriteEvent) {
@@ -1385,259 +1398,151 @@
       "", button.Clone(),
       mojom::RemappingAction::NewKeyEvent(key_event.Clone())));
 
-  auto& event = GetEventFromVariant(data.incoming_event);
-  event.set_source_device_id(kMouseDeviceId);
-
-  TestEventRewriterContinuation continuation;
-  rewriter_->RewriteEvent(event, continuation.weak_ptr_factory_.GetWeakPtr());
-
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(*data.rewritten_event),
-            ConvertToString(*continuation.passthrough_event));
+  EXPECT_EQ(std::vector<TestEventVariant>{*data.rewritten_event},
+            RunRewriter({data.incoming_event}));
 }
 
-class ModifierRewritingTest
-    : public PeripheralCustomizationEventRewriterTest,
-      public testing::WithParamInterface<
-          std::tuple<ui::KeyboardCode, ui::DomCode, ui::EventFlags>> {
-  void SetUp() override {
-    PeripheralCustomizationEventRewriterTest::SetUp();
-    std::tie(key_code, dom_code, flag) = GetParam();
-  }
-
-  void TearDown() override {
-    PeripheralCustomizationEventRewriterTest::TearDown();
-  }
-
- protected:
-  ui::KeyboardCode key_code;
-  ui::DomCode dom_code;
-  ui::EventFlags flag;
+class ModifierRewritingTest : public PeripheralCustomizationEventRewriterTest,
+                              public testing::WithParamInterface<TestKeyEvent> {
 };
 
-INSTANTIATE_TEST_SUITE_P(
-    All,
-    ModifierRewritingTest,
-    testing::ValuesIn(
-        std::vector<std::tuple<ui::KeyboardCode, ui::DomCode, ui::EventFlags>>({
-            {ui::VKEY_LWIN, ui::DomCode::META_LEFT, ui::EF_COMMAND_DOWN},
-            {ui::VKEY_RWIN, ui::DomCode::META_RIGHT, ui::EF_COMMAND_DOWN},
-            {ui::VKEY_SHIFT, ui::DomCode::SHIFT_LEFT, ui::EF_SHIFT_DOWN},
-            {ui::VKEY_LSHIFT, ui::DomCode::SHIFT_LEFT, ui::EF_SHIFT_DOWN},
-            {ui::VKEY_RSHIFT, ui::DomCode::SHIFT_RIGHT, ui::EF_SHIFT_DOWN},
-            {ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT, ui::EF_CONTROL_DOWN},
-            {ui::VKEY_RCONTROL, ui::DomCode::CONTROL_RIGHT,
-             ui::EF_CONTROL_DOWN},
-            {ui::VKEY_MENU, ui::DomCode::ALT_LEFT, ui::EF_ALT_DOWN},
-            {ui::VKEY_RMENU, ui::DomCode::ALT_RIGHT, ui::EF_ALT_DOWN},
-        })));
+INSTANTIATE_TEST_SUITE_P(All,
+                         ModifierRewritingTest,
+                         testing::ValuesIn(std::vector<TestKeyEvent>({
+                             KeyLMeta::Pressed(),
+                             KeyRMeta::Pressed(),
+                             KeyLShift::Pressed(),
+                             KeyRShift::Pressed(),
+                             KeyLControl::Pressed(),
+                             KeyRControl::Pressed(),
+                             KeyLAlt::Pressed(),
+                             KeyRAlt::Pressed(),
+                         })));
 
 TEST_P(ModifierRewritingTest, ModifierKeyCombo) {
-  TestEventRewriterContinuation continuation;
+  const auto& data = GetParam();
 
   mouse_settings_->button_remappings.push_back(mojom::ButtonRemapping::New(
       "", mojom::Button::NewVkey(ui::VKEY_0),
       mojom::RemappingAction::NewKeyEvent(mojom::KeyEvent::New(
-          key_code, (int)dom_code, (int)ui::DomKey::NONE, flag,
+          data.keycode, (int)data.code, (int)data.key, data.flags,
           /*key_display=*/""))));
 
-  rewriter_->RewriteEvent(
-      CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_0, ui::EF_NONE),
-      continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(CreateKeyButtonEvent(ui::ET_KEY_PRESSED, key_code,
-                                                 flag, dom_code)),
-            ConvertToString(*continuation.passthrough_event));
+  auto modifier_pressed_event = data;
+  auto modifier_released_event = data;
+  modifier_released_event.type = ui::ET_KEY_RELEASED;
+  modifier_released_event.flags = ui::EF_NONE;
 
-  continuation.reset();
-  rewriter_->RewriteEvent(
-      CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE),
-      continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(
-                CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, flag)),
-            ConvertToString(*continuation.passthrough_event));
+  // Press down remapped button that maps to a modifier.
+  EXPECT_EQ((std::vector<TestEventVariant>{modifier_pressed_event}),
+            RunRewriter(std::vector<TestEventVariant>{KeyDigit0::Pressed()}));
 
-  continuation.reset();
-  rewriter_->RewriteEvent(
-      CreateKeyButtonEvent(ui::ET_KEY_RELEASED, ui::VKEY_0, ui::EF_NONE),
-      continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(CreateKeyButtonEvent(ui::ET_KEY_RELEASED, key_code,
-                                                 ui::EF_NONE, dom_code)),
-            ConvertToString(*continuation.passthrough_event));
+  // When a key is pressed it should have the remapped modifier flag.
+  EXPECT_EQ((KeyA::Typed(data.flags)), RunRewriter(KeyA::Typed()));
 
-  continuation.reset();
-  rewriter_->RewriteEvent(
-      CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE),
-      continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_A,
-                                                 ui::EF_NONE)),
-            ConvertToString(*continuation.passthrough_event));
+  // Release the remapped modifier button.
+  EXPECT_EQ(std::vector<TestEventVariant>{modifier_released_event},
+            RunRewriter(std::vector<TestEventVariant>{KeyDigit0::Released()}));
+
+  // Other pressed key should no longer have the remapped flag.
+  EXPECT_EQ((KeyA::Typed()), RunRewriter(KeyA::Typed()));
 }
 
 TEST_P(ModifierRewritingTest, MultiModifierKeyCombo) {
-  TestEventRewriterContinuation continuation;
+  const auto& data = GetParam();
 
   mouse_settings_->button_remappings.push_back(mojom::ButtonRemapping::New(
       "", mojom::Button::NewVkey(ui::VKEY_0),
       mojom::RemappingAction::NewKeyEvent(mojom::KeyEvent::New(
-          key_code, (int)dom_code, (int)ui::DomKey::NONE, flag,
+          data.keycode, (int)data.code, (int)data.key, data.flags,
           /*key_display=*/""))));
 
-  const ui::EventFlags test_flag =
-      flag == ui::EF_COMMAND_DOWN ? ui::EF_SHIFT_DOWN : ui::EF_COMMAND_DOWN;
+  const ui::EventFlags test_flag = data.flags == ui::EF_COMMAND_DOWN
+                                       ? ui::EF_SHIFT_DOWN
+                                       : ui::EF_COMMAND_DOWN;
 
-  rewriter_->RewriteEvent(
-      CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_0, ui::EF_NONE),
-      continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(CreateKeyButtonEvent(ui::ET_KEY_PRESSED, key_code,
-                                                 flag, dom_code)),
-            ConvertToString(*continuation.passthrough_event));
+  auto modifier_pressed_event = data;
+  auto modifier_released_event = data;
+  modifier_released_event.type = ui::ET_KEY_RELEASED;
+  modifier_released_event.flags = ui::EF_NONE;
 
-  continuation.reset();
-  rewriter_->RewriteEvent(
-      CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, test_flag),
-      continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_A,
-                                                 test_flag | flag)),
-            ConvertToString(*continuation.passthrough_event));
+  // Press down remapped button that maps to a modifier.
+  EXPECT_EQ((std::vector<TestEventVariant>{modifier_pressed_event}),
+            RunRewriter(std::vector<TestEventVariant>{KeyDigit0::Pressed()}));
 
-  continuation.reset();
-  rewriter_->RewriteEvent(
-      CreateKeyButtonEvent(ui::ET_KEY_RELEASED, ui::VKEY_0, ui::EF_NONE),
-      continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(CreateKeyButtonEvent(ui::ET_KEY_RELEASED, key_code,
-                                                 ui::EF_NONE, dom_code)),
-            ConvertToString(*continuation.passthrough_event));
+  // When a key is pressed it should have the remapped modifier flag as well as
+  // the additional test flag.
+  EXPECT_EQ((KeyA::Typed(data.flags | test_flag)),
+            RunRewriter(KeyA::Typed(test_flag)));
 
-  continuation.reset();
-  rewriter_->RewriteEvent(
-      CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, test_flag),
-      continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_A,
-                                                 test_flag)),
-            ConvertToString(*continuation.passthrough_event));
+  // Release the remapped modifier button.
+  EXPECT_EQ(std::vector<TestEventVariant>{modifier_released_event},
+            RunRewriter(std::vector<TestEventVariant>{KeyDigit0::Released()}));
+
+  // Other pressed key should no longer have the remapped flag.
+  EXPECT_EQ((KeyA::Typed(test_flag)), RunRewriter(KeyA::Typed(test_flag)));
 }
 
 TEST_P(ModifierRewritingTest, MouseEvent) {
-  TestEventRewriterContinuation continuation;
-  const ui::EventFlags test_flag =
-      flag == ui::EF_COMMAND_DOWN ? ui::EF_SHIFT_DOWN : ui::EF_COMMAND_DOWN;
+  const auto& data = GetParam();
+
+  const ui::EventFlags test_flag = data.flags == ui::EF_COMMAND_DOWN
+                                       ? ui::EF_SHIFT_DOWN
+                                       : ui::EF_COMMAND_DOWN;
 
   mouse_settings_->button_remappings.push_back(mojom::ButtonRemapping::New(
       "", mojom::Button::NewVkey(ui::VKEY_0),
       mojom::RemappingAction::NewKeyEvent(mojom::KeyEvent::New(
-          key_code, (int)dom_code, (int)ui::DomKey::NONE, flag,
+          data.keycode, (int)data.code, (int)data.key, data.flags,
           /*key_display=*/""))));
 
-  rewriter_->RewriteEvent(
-      CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_0, ui::EF_NONE),
-      continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(CreateKeyButtonEvent(ui::ET_KEY_PRESSED, key_code,
-                                                 flag, dom_code)),
-            ConvertToString(*continuation.passthrough_event));
+  auto modifier_pressed_event = data;
+  auto modifier_released_event = data;
+  modifier_released_event.type = ui::ET_KEY_RELEASED;
+  modifier_released_event.flags = ui::EF_NONE;
 
-  continuation.reset();
-  rewriter_->RewriteEvent(
-      CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                             test_flag | ui::EF_FORWARD_MOUSE_BUTTON,
-                             ui::EF_FORWARD_MOUSE_BUTTON),
-      continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(
-      ConvertToString(CreateMouseButtonEvent(
-          ui::ET_MOUSE_PRESSED, ui::EF_FORWARD_MOUSE_BUTTON | test_flag | flag,
-          ui::EF_FORWARD_MOUSE_BUTTON)),
-      ConvertToString(*continuation.passthrough_event));
+  // Press down remapped button that maps to a modifier.
+  EXPECT_EQ((std::vector<TestEventVariant>{modifier_pressed_event}),
+            RunRewriter(std::vector<TestEventVariant>{KeyDigit0::Pressed()}));
 
-  continuation.reset();
-  rewriter_->RewriteEvent(
-      CreateKeyButtonEvent(ui::ET_KEY_RELEASED, ui::VKEY_0, ui::EF_NONE),
-      continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(CreateKeyButtonEvent(ui::ET_KEY_RELEASED, key_code,
-                                                 ui::EF_NONE, dom_code)),
-            ConvertToString(*continuation.passthrough_event));
+  // When a button is pressed it should have the remapped modifier flag as well
+  // as the additional test flag.
+  EXPECT_EQ((ButtonForward::Typed(data.flags | test_flag)),
+            RunRewriter(ButtonForward::Typed(test_flag)));
 
-  continuation.reset();
-  rewriter_->RewriteEvent(
-      CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED,
-                             test_flag | ui::EF_FORWARD_MOUSE_BUTTON,
-                             ui::EF_FORWARD_MOUSE_BUTTON),
-      continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(CreateMouseButtonEvent(
-                ui::ET_MOUSE_PRESSED, test_flag | ui::EF_FORWARD_MOUSE_BUTTON,
-                ui::EF_FORWARD_MOUSE_BUTTON)),
-            ConvertToString(*continuation.passthrough_event));
+  // Release the remapped modifier button.
+  EXPECT_EQ(std::vector<TestEventVariant>{modifier_released_event},
+            RunRewriter(std::vector<TestEventVariant>{KeyDigit0::Released()}));
+
+  // Other pressed button should no longer have the remapped flag.
+  EXPECT_EQ((KeyA::Typed(test_flag)), RunRewriter(KeyA::Typed(test_flag)));
 }
 
 class StaticShortcutActionRewritingTest
     : public PeripheralCustomizationEventRewriterTest,
       public testing::WithParamInterface<
-          std::tuple<mojom::StaticShortcutAction, ui::KeyEvent>> {};
+          std::tuple<mojom::StaticShortcutAction, TestKeyEvent>> {};
 
 INSTANTIATE_TEST_SUITE_P(
     All,
     StaticShortcutActionRewritingTest,
-    testing::ValuesIn(
-        std::vector<std::tuple<mojom::StaticShortcutAction, ui::KeyEvent>>({
-            {mojom::StaticShortcutAction::kCopy,
-             CreateKeyButtonEvent(ui::ET_KEY_PRESSED,
-                                  ui::VKEY_C,
-                                  ui::EF_CONTROL_DOWN,
-                                  ui::DomCode::US_C,
-                                  ui::DomKey::Constant<'c'>::Character)},
-            {mojom::StaticShortcutAction::kPaste,
-             CreateKeyButtonEvent(ui::ET_KEY_PRESSED,
-                                  ui::VKEY_V,
-                                  ui::EF_CONTROL_DOWN,
-                                  ui::DomCode::US_V,
-                                  ui::DomKey::Constant<'v'>::Character)},
-            {mojom::StaticShortcutAction::kUndo,
-             CreateKeyButtonEvent(ui::ET_KEY_PRESSED,
-                                  ui::VKEY_Z,
-                                  ui::EF_CONTROL_DOWN,
-                                  ui::DomCode::US_Z,
-                                  ui::DomKey::Constant<'z'>::Character)},
-            {mojom::StaticShortcutAction::kRedo,
-             CreateKeyButtonEvent(ui::ET_KEY_PRESSED,
-                                  ui::VKEY_Z,
-                                  ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN,
-                                  ui::DomCode::US_Z,
-                                  ui::DomKey::Constant<'Z'>::Character)},
-            {mojom::StaticShortcutAction::kZoomIn,
-             CreateKeyButtonEvent(ui::ET_KEY_PRESSED,
-                                  ui::VKEY_OEM_PLUS,
-                                  ui::EF_CONTROL_DOWN,
-                                  ui::DomCode::EQUAL,
-                                  ui::DomKey::Constant<'='>::Character)},
-            {mojom::StaticShortcutAction::kZoomOut,
-             CreateKeyButtonEvent(ui::ET_KEY_PRESSED,
-                                  ui::VKEY_OEM_MINUS,
-                                  ui::EF_CONTROL_DOWN,
-                                  ui::DomCode::MINUS,
-                                  ui::DomKey::Constant<'-'>::Character)},
-            {mojom::StaticShortcutAction::kPreviousPage,
-             CreateKeyButtonEvent(ui::ET_KEY_PRESSED,
-                                  ui::VKEY_BROWSER_BACK,
-                                  ui::EF_NONE,
-                                  ui::DomCode::BROWSER_BACK,
-                                  ui::DomKey::BROWSER_BACK)},
-            {mojom::StaticShortcutAction::kNextPage,
-             CreateKeyButtonEvent(ui::ET_KEY_PRESSED,
-                                  ui::VKEY_BROWSER_FORWARD,
-                                  ui::EF_NONE,
-                                  ui::DomCode::BROWSER_FORWARD,
-                                  ui::DomKey::BROWSER_FORWARD)},
-        })));
+    testing::ValuesIn(std::vector<
+                      std::tuple<mojom::StaticShortcutAction, TestKeyEvent>>({
+        {mojom::StaticShortcutAction::kCopy,
+         KeyC::Pressed(ui::EF_CONTROL_DOWN)},
+        {mojom::StaticShortcutAction::kPaste,
+         KeyV::Pressed(ui::EF_CONTROL_DOWN)},
+        {mojom::StaticShortcutAction::kUndo,
+         KeyZ::Pressed(ui::EF_CONTROL_DOWN)},
+        {mojom::StaticShortcutAction::kRedo,
+         KeyZ::Pressed(ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN)},
+        {mojom::StaticShortcutAction::kZoomIn,
+         KeyEqual::Pressed(ui::EF_CONTROL_DOWN)},
+        {mojom::StaticShortcutAction::kZoomOut,
+         KeyMinus::Pressed(ui::EF_CONTROL_DOWN)},
+        {mojom::StaticShortcutAction::kPreviousPage, KeyBrowserBack::Pressed()},
+        {mojom::StaticShortcutAction::kNextPage, KeyBrowserForward::Pressed()},
+    })));
 
 TEST_F(StaticShortcutActionRewritingTest, StaticShortcutDisableMouseRewriting) {
   mouse_settings_->button_remappings.push_back(mojom::ButtonRemapping::New(
@@ -1645,25 +1550,14 @@
       mojom::Button::NewCustomizableButton(mojom::CustomizableButton::kForward),
       mojom::RemappingAction::NewStaticShortcutAction(
           mojom::StaticShortcutAction::kDisable)));
+  mouse_settings_->button_remappings.push_back(mojom::ButtonRemapping::New(
+      "", mojom::Button::NewVkey(ui::VKEY_A),
+      mojom::RemappingAction::NewStaticShortcutAction(
+          mojom::StaticShortcutAction::kDisable)));
 
-  TestEventRewriterContinuation continuation;
-  ui::MouseEvent mouse_pressed_event =
-      CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED, ui::EF_FORWARD_MOUSE_BUTTON,
-                             ui::EF_FORWARD_MOUSE_BUTTON, kMouseDeviceId);
-  rewriter_->RewriteEvent(mouse_pressed_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.discarded());
-  EXPECT_EQ(nullptr, continuation.passthrough_event);
-
-  ui::MouseEvent mouse_release_event =
-      CreateMouseButtonEvent(ui::ET_MOUSE_RELEASED, ui::EF_FORWARD_MOUSE_BUTTON,
-                             ui::EF_FORWARD_MOUSE_BUTTON, kMouseDeviceId);
-
-  continuation.reset();
-  rewriter_->RewriteEvent(mouse_release_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-  ASSERT_TRUE(continuation.discarded());
-  EXPECT_EQ(nullptr, continuation.passthrough_event);
+  EXPECT_EQ(std::vector<TestEventVariant>{}, RunRewriter({KeyA::Typed()}));
+  EXPECT_EQ(std::vector<TestEventVariant>{},
+            RunRewriter({ButtonForward::Typed()}));
 }
 
 TEST_P(StaticShortcutActionRewritingTest, StaticShortcutMouseRewriting) {
@@ -1674,35 +1568,11 @@
       mojom::Button::NewCustomizableButton(mojom::CustomizableButton::kForward),
       mojom::RemappingAction::NewStaticShortcutAction(static_shortcut_action)));
 
-  TestEventRewriterContinuation continuation;
-  ui::MouseEvent mouse_pressed_event =
-      CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED, ui::EF_FORWARD_MOUSE_BUTTON,
-                             ui::EF_FORWARD_MOUSE_BUTTON, kMouseDeviceId);
-  ui::KeyEvent expected_mouse_pressed_event = expected_key_event;
-  expected_mouse_pressed_event.set_source_device_id(kMouseDeviceId);
-
-  rewriter_->RewriteEvent(mouse_pressed_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(expected_mouse_pressed_event),
-            ConvertToString(*continuation.passthrough_event));
-  ui::MouseEvent mouse_release_event =
-      CreateMouseButtonEvent(ui::ET_MOUSE_RELEASED, ui::EF_FORWARD_MOUSE_BUTTON,
-                             ui::EF_FORWARD_MOUSE_BUTTON, kMouseDeviceId);
-
-  continuation.reset();
-  rewriter_->RewriteEvent(mouse_release_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-
-  ASSERT_TRUE(continuation.passthrough_event);
-  ui::KeyEvent expected_mouse_release_event = CreateKeyButtonEvent(
-      ui::ET_KEY_RELEASED, expected_key_event.key_code(),
-      expected_key_event.flags(), expected_key_event.code(),
-      expected_key_event.GetDomKey());
-  expected_mouse_release_event.set_source_device_id(kMouseDeviceId);
-  EXPECT_EQ(ConvertToString(expected_mouse_release_event),
-            ConvertToString(*continuation.passthrough_event));
+  auto expected_key_release_event = expected_key_event;
+  expected_key_release_event.type = ui::ET_KEY_RELEASED;
+  EXPECT_EQ((std::vector<TestEventVariant>{expected_key_event,
+                                           expected_key_release_event}),
+            (RunRewriter(ButtonForward::Typed())));
 }
 
 TEST_P(StaticShortcutActionRewritingTest,
@@ -1723,151 +1593,57 @@
           mojom::RemappingAction::NewStaticShortcutAction(
               static_shortcut_action)));
 
-  TestEventRewriterContinuation continuation;
+  auto expected_key_release_event = expected_key_event;
+  expected_key_release_event.type = ui::ET_KEY_RELEASED;
 
-  ui::MouseEvent pen_pressed_event = CreateMouseButtonEvent(
-      ui::ET_MOUSE_PRESSED, ui::EF_FORWARD_MOUSE_BUTTON,
-      ui::EF_FORWARD_MOUSE_BUTTON, kGraphicsTabletDeviceId);
-  ui::KeyEvent expected_pen_pressed_event = expected_key_event;
-  expected_pen_pressed_event.set_source_device_id(kGraphicsTabletDeviceId);
-
-  rewriter_->RewriteEvent(pen_pressed_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(expected_pen_pressed_event),
-            ConvertToString(*continuation.passthrough_event));
-
-  ui::MouseEvent pen_release_event = CreateMouseButtonEvent(
-      ui::ET_MOUSE_RELEASED, ui::EF_FORWARD_MOUSE_BUTTON,
-      ui::EF_FORWARD_MOUSE_BUTTON, kGraphicsTabletDeviceId);
-  ui::KeyEvent expected_pen_release_event = CreateKeyButtonEvent(
-      ui::ET_KEY_RELEASED, expected_key_event.key_code(),
-      expected_key_event.flags(), expected_key_event.code(),
-      expected_key_event.GetDomKey());
-  expected_pen_release_event.set_source_device_id(kGraphicsTabletDeviceId);
-
-  continuation.reset();
-  rewriter_->RewriteEvent(pen_release_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(expected_pen_release_event),
-            ConvertToString(*continuation.passthrough_event));
-
-  ui::MouseEvent tablet_pressed_event =
-      CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED, ui::EF_BACK_MOUSE_BUTTON,
-                             ui::EF_BACK_MOUSE_BUTTON, kGraphicsTabletDeviceId);
-  ui::KeyEvent expected_tablet_pressed_event = expected_key_event;
-  expected_tablet_pressed_event.set_source_device_id(kGraphicsTabletDeviceId);
-
-  continuation.reset();
-  rewriter_->RewriteEvent(tablet_pressed_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(expected_tablet_pressed_event),
-            ConvertToString(*continuation.passthrough_event));
-
-  ui::MouseEvent tablet_release_event =
-      CreateMouseButtonEvent(ui::ET_MOUSE_RELEASED, ui::EF_BACK_MOUSE_BUTTON,
-                             ui::EF_BACK_MOUSE_BUTTON, kGraphicsTabletDeviceId);
-  ui::KeyEvent expected_tablet_release_event = CreateKeyButtonEvent(
-      ui::ET_KEY_RELEASED, expected_key_event.key_code(),
-      expected_key_event.flags(), expected_key_event.code(),
-      expected_key_event.GetDomKey());
-  expected_tablet_release_event.set_source_device_id(kGraphicsTabletDeviceId);
-
-  continuation.reset();
-  rewriter_->RewriteEvent(tablet_release_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(expected_tablet_release_event),
-            ConvertToString(*continuation.passthrough_event));
+  EXPECT_EQ((std::vector<TestEventVariant>{expected_key_event,
+                                           expected_key_release_event}),
+            (RunRewriter(ButtonForward::Typed(), ui::EF_NONE,
+                         kGraphicsTabletDeviceId)));
+  EXPECT_EQ(
+      (std::vector<TestEventVariant>{expected_key_event,
+                                     expected_key_release_event}),
+      (RunRewriter(ButtonBack::Typed(), ui::EF_NONE, kGraphicsTabletDeviceId)));
 }
 
 class StaticShortcutActionMouseButtonRewritingTest
     : public PeripheralCustomizationEventRewriterTest,
       public testing::WithParamInterface<
-          std::tuple<mojom::StaticShortcutAction, ui::EventFlags>> {};
+          std::tuple<mojom::StaticShortcutAction,
+                     std::vector<TestEventVariant>>> {};
 
 INSTANTIATE_TEST_SUITE_P(
     All,
     StaticShortcutActionMouseButtonRewritingTest,
-    testing::ValuesIn(
-        std::vector<std::tuple<mojom::StaticShortcutAction, ui::EventFlags>>{
-            {mojom::StaticShortcutAction::kLeftClick, ui::EF_LEFT_MOUSE_BUTTON},
-            {mojom::StaticShortcutAction::kRightClick,
-             ui::EF_RIGHT_MOUSE_BUTTON},
-            {mojom::StaticShortcutAction::kMiddleClick,
-             ui::EF_MIDDLE_MOUSE_BUTTON},
-        }));
+    testing::ValuesIn(std::vector<std::tuple<mojom::StaticShortcutAction,
+                                             std::vector<TestEventVariant>>>{
+        {mojom::StaticShortcutAction::kLeftClick, ButtonLeft::Typed()},
+        {mojom::StaticShortcutAction::kRightClick, ButtonRight::Typed()},
+        {mojom::StaticShortcutAction::kMiddleClick, ButtonMiddle::Typed()},
+    }));
 
 TEST_P(StaticShortcutActionMouseButtonRewritingTest, RewriteEvent) {
-  const auto [static_shortcut_action, expected_flag] = GetParam();
+  const auto [static_shortcut_action, expected_events] = GetParam();
   mouse_settings_->button_remappings.push_back(mojom::ButtonRemapping::New(
       "",
       mojom::Button::NewCustomizableButton(mojom::CustomizableButton::kForward),
       mojom::RemappingAction::NewStaticShortcutAction(static_shortcut_action)));
 
-  TestEventRewriterContinuation continuation;
-  const ui::MouseEvent mouse_pressed_event =
-      CreateMouseButtonEvent(ui::ET_MOUSE_PRESSED, ui::EF_FORWARD_MOUSE_BUTTON,
-                             ui::EF_FORWARD_MOUSE_BUTTON, kMouseDeviceId);
-  const ui::MouseEvent expected_press_event = CreateMouseButtonEvent(
-      ui::ET_MOUSE_PRESSED, expected_flag, expected_flag);
-  rewriter_->RewriteEvent(mouse_pressed_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(expected_press_event),
-            ConvertToString(*continuation.passthrough_event));
-
-  continuation.reset();
-  const ui::MouseEvent mouse_released_event =
-      CreateMouseButtonEvent(ui::ET_MOUSE_RELEASED, ui::EF_FORWARD_MOUSE_BUTTON,
-                             ui::EF_FORWARD_MOUSE_BUTTON, kMouseDeviceId);
-  const ui::MouseEvent expected_release_event = CreateMouseButtonEvent(
-      ui::ET_MOUSE_RELEASED, expected_flag, expected_flag);
-  rewriter_->RewriteEvent(expected_release_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(expected_release_event),
-            ConvertToString(*continuation.passthrough_event));
+  EXPECT_EQ(expected_events, RunRewriter({ButtonForward::Typed()}));
 }
 
 TEST_P(StaticShortcutActionMouseButtonRewritingTest, KeyEventRewrite) {
-  const auto [static_shortcut_action, expected_flag] = GetParam();
+  auto [static_shortcut_action, expected_events] = GetParam();
   mouse_settings_->button_remappings.push_back(mojom::ButtonRemapping::New(
       "", mojom::Button::NewVkey(ui::VKEY_A),
       mojom::RemappingAction::NewStaticShortcutAction(static_shortcut_action)));
 
-  TestEventRewriterContinuation continuation;
-  const ui::KeyEvent key_pressed_event =
-      CreateKeyButtonEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_COMMAND_DOWN);
-  const ui::MouseEvent expected_pressed_event = CreateMouseButtonEvent(
-      ui::ET_MOUSE_PRESSED, ui::EF_COMMAND_DOWN | expected_flag, expected_flag);
-  rewriter_->RewriteEvent(key_pressed_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(expected_pressed_event),
-            ConvertToString(*continuation.passthrough_event));
-
-  continuation.reset();
-  const ui::KeyEvent key_released_event = CreateKeyButtonEvent(
-      ui::ET_KEY_RELEASED, ui::VKEY_A, ui::EF_COMMAND_DOWN);
-  const ui::MouseEvent expected_released_event = CreateMouseButtonEvent(
-      ui::ET_MOUSE_RELEASED, ui::EF_COMMAND_DOWN | expected_flag,
-      expected_flag);
-  rewriter_->RewriteEvent(key_released_event,
-                          continuation.weak_ptr_factory_.GetWeakPtr());
-
-  ASSERT_TRUE(continuation.passthrough_event);
-  EXPECT_EQ(ConvertToString(expected_released_event),
-            ConvertToString(*continuation.passthrough_event));
+  for (auto& event : expected_events) {
+    auto* test_mouse_event = std::get_if<TestMouseEvent>(&event);
+    ASSERT_TRUE(test_mouse_event);
+    test_mouse_event->flags = test_mouse_event->flags | ui::EF_COMMAND_DOWN;
+  }
+  EXPECT_EQ(expected_events, RunRewriter({KeyA::Typed(ui::EF_COMMAND_DOWN)}));
 }
 
 }  // namespace ash
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index 9d4a78d3..d54c5b3 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -468,6 +468,7 @@
   public_deps = [
     "//ash/components/arc/mojom:notifications",
     "//ash/public/mojom",
+    "//ash/webui/common/mojom:sea_pen",
     "//ash/webui/personalization_app/mojom:mojom_shared_cpp_sources",
     "//ash/webui/personalization_app/proto",
     "//base",
diff --git a/ash/public/cpp/external_arc/message_center/arc_notification_manager.cc b/ash/public/cpp/external_arc/message_center/arc_notification_manager.cc
index 8cd672f..1ceb010 100644
--- a/ash/public/cpp/external_arc/message_center/arc_notification_manager.cc
+++ b/ash/public/cpp/external_arc/message_center/arc_notification_manager.cc
@@ -636,10 +636,9 @@
     return true;
   }
 
-  // Media Notifications may be ignored if we have the native views based media
-  // session notifications enabled.
-  if (data->is_media_notification &&
-      features::IsHideArcMediaNotificationsEnabled()) {
+  // Media Notifications are ignored because we show native views-based media
+  // session notifications instead.
+  if (data->is_media_notification) {
     return true;
   }
 
diff --git a/ash/public/cpp/wallpaper/wallpaper_controller.h b/ash/public/cpp/wallpaper/wallpaper_controller.h
index 778fc898..579046cdb 100644
--- a/ash/public/cpp/wallpaper/wallpaper_controller.h
+++ b/ash/public/cpp/wallpaper/wallpaper_controller.h
@@ -15,6 +15,7 @@
 #include "ash/public/cpp/wallpaper/sea_pen_image.h"
 #include "ash/public/cpp/wallpaper/wallpaper_info.h"
 #include "ash/public/cpp/wallpaper/wallpaper_types.h"
+#include "ash/webui/common/mojom/sea_pen.mojom.h"
 #include "base/containers/lru_cache.h"
 #include "base/files/file_path.h"
 #include "base/functional/callback_helpers.h"
@@ -234,29 +235,15 @@
 
   // Sets `sea_pen_image` received from the Manta API as system wallpaper for
   // user with `account_id` and saves the image to disk with xmp metadata
-  // containing `query_info` data.
-  // `query_info` is a string constructed as XMP format (like XML standard
-  // format) which includes the query information used to generate the image and
-  // its creation time. For example:
-  //    <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 6.0.0">
-  //      <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-  //        <rdf:Description rdf:about=""
-  //        xmlns:dc="http://purl.org/dc/elements/1.1/">
-  //          <dc:description>{"creation_time":"123456789",
-  //          "user_visible_query_text":"some query",
-  //          "user_visible_query_template":"some template",
-  //          "options":{"6":"48","7":"61"}, "template_id":"3"}
-  //          </dc:description>
-  //        </rdf:Description>
-  //      </rdf:RDF>
-  //    </x:xmpmeta>
+  // containing query info from SeaPenQuery `query`.
   // @see //components/manta
   // Calls `callback` with boolean success. Can fail if `account_id` is not
   // allowed to set wallpaper, or the image failed to decode.
-  virtual void SetSeaPenWallpaper(const AccountId& account_id,
-                                  const SeaPenImage& sea_pen_image,
-                                  const std::string& query_info,
-                                  SetWallpaperCallback callback) = 0;
+  virtual void SetSeaPenWallpaper(
+      const AccountId& account_id,
+      const SeaPenImage& sea_pen_image,
+      const personalization_app::mojom::SeaPenQueryPtr& query,
+      SetWallpaperCallback callback) = 0;
 
   // Sets the recently used Sea Pen wallpaper as system wallpaper for
   // user with `account_id`.
diff --git a/ash/quick_pair/feature_status_tracker/BUILD.gn b/ash/quick_pair/feature_status_tracker/BUILD.gn
index 2f4ba74..9aae065 100644
--- a/ash/quick_pair/feature_status_tracker/BUILD.gn
+++ b/ash/quick_pair/feature_status_tracker/BUILD.gn
@@ -11,6 +11,8 @@
   sources = [
     "base_enabled_provider.cc",
     "base_enabled_provider.h",
+    "battery_saver_active_provider.cc",
+    "battery_saver_active_provider.h",
     "bluetooth_enabled_provider.cc",
     "bluetooth_enabled_provider.h",
     "fast_pair_enabled_provider.cc",
@@ -86,6 +88,7 @@
 
   sources = [
     "base_enabled_provider_unittest.cc",
+    "battery_saver_active_provider_unittest.cc",
     "bluetooth_enabled_provider_unittest.cc",
     "fast_pair_enabled_provider_unittest.cc",
     "fast_pair_support_utils_unittest.cc",
diff --git a/ash/quick_pair/feature_status_tracker/battery_saver_active_provider.cc b/ash/quick_pair/feature_status_tracker/battery_saver_active_provider.cc
new file mode 100644
index 0000000..50a917d
--- /dev/null
+++ b/ash/quick_pair/feature_status_tracker/battery_saver_active_provider.cc
@@ -0,0 +1,31 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/quick_pair/feature_status_tracker/battery_saver_active_provider.h"
+
+namespace ash {
+namespace quick_pair {
+
+BatterySaverActiveProvider::BatterySaverActiveProvider() {
+  if (PowerStatus::IsInitialized()) {
+    power_status_ = PowerStatus::Get();
+    power_status_->AddObserver(this);
+    SetEnabledAndInvokeCallback(
+        /*new_value=*/power_status_->IsBatterySaverActive());
+  }
+}
+
+BatterySaverActiveProvider::~BatterySaverActiveProvider() {
+  if (PowerStatus::IsInitialized()) {
+    power_status_->RemoveObserver(this);
+  }
+}
+
+void BatterySaverActiveProvider::OnPowerStatusChanged() {
+  SetEnabledAndInvokeCallback(
+      /*new_value=*/power_status_ && power_status_->IsBatterySaverActive());
+}
+
+}  // namespace quick_pair
+}  // namespace ash
diff --git a/ash/quick_pair/feature_status_tracker/battery_saver_active_provider.h b/ash/quick_pair/feature_status_tracker/battery_saver_active_provider.h
new file mode 100644
index 0000000..aeb83d43
--- /dev/null
+++ b/ash/quick_pair/feature_status_tracker/battery_saver_active_provider.h
@@ -0,0 +1,32 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_QUICK_PAIR_FEATURE_STATUS_TRACKER_BATTERY_SAVER_ACTIVE_PROVIDER_H_
+#define ASH_QUICK_PAIR_FEATURE_STATUS_TRACKER_BATTERY_SAVER_ACTIVE_PROVIDER_H_
+
+#include "ash/quick_pair/feature_status_tracker/base_enabled_provider.h"
+#include "ash/system/power/power_status.h"
+
+namespace ash {
+namespace quick_pair {
+
+class BatterySaverActiveProvider : public BaseEnabledProvider,
+                                   public PowerStatus::Observer {
+ public:
+  explicit BatterySaverActiveProvider();
+  ~BatterySaverActiveProvider() override;
+
+ private:
+  friend class BatterySaverActiveProviderTest;
+
+  // PowerStatus::Observer
+  void OnPowerStatusChanged() override;
+
+  raw_ptr<PowerStatus> power_status_;
+};
+
+}  // namespace quick_pair
+}  // namespace ash
+
+#endif  // ASH_QUICK_PAIR_FEATURE_STATUS_TRACKER_BATTERY_SAVER_ACTIVE_PROVIDER_H_
diff --git a/ash/quick_pair/feature_status_tracker/battery_saver_active_provider_unittest.cc b/ash/quick_pair/feature_status_tracker/battery_saver_active_provider_unittest.cc
new file mode 100644
index 0000000..2068be6
--- /dev/null
+++ b/ash/quick_pair/feature_status_tracker/battery_saver_active_provider_unittest.cc
@@ -0,0 +1,85 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/quick_pair/feature_status_tracker/battery_saver_active_provider.h"
+
+#include "ash/system/power/power_status.h"
+#include "ash/test/ash_test_base.h"
+
+namespace ash {
+namespace quick_pair {
+
+class BatterySaverActiveProviderTest : public AshTestBase {
+ public:
+  // PowerStatus must be initialized before AshTestBase::TearDown() because the
+  // latter calls PowerStatus::Shutdown, which crashes if the PowerStatus is not
+  // initialized.
+  void TearDown() override {
+    battery_saver_active_provider_.reset();
+    if (!PowerStatus::IsInitialized()) {
+      PowerStatus::Initialize();
+    }
+    AshTestBase::TearDown();
+  }
+
+  void SetBatterySaverStateAndNotifyProvider(bool is_enabled) {
+    if (!battery_saver_active_provider_) {
+      return;
+    }
+
+    if (!PowerStatus::IsInitialized()) {
+      return;
+    }
+
+    PowerStatus::Get()->SetBatterySaverStateForTesting(is_enabled);
+    battery_saver_active_provider_->OnPowerStatusChanged();
+  }
+
+  void InitializeProvider() {
+    battery_saver_active_provider_ =
+        std::make_unique<BatterySaverActiveProvider>();
+  }
+
+  void InitializePowerStatus(bool should_initialize) {
+    if (should_initialize && !PowerStatus::IsInitialized()) {
+      PowerStatus::Initialize();
+    } else if (!should_initialize && PowerStatus::IsInitialized()) {
+      PowerStatus::Shutdown();
+    }
+    CHECK(should_initialize == PowerStatus::IsInitialized());
+  }
+
+  bool IsBatterySaverActive() {
+    return battery_saver_active_provider_->is_enabled();
+  }
+
+ protected:
+  std::unique_ptr<BatterySaverActiveProvider> battery_saver_active_provider_;
+};
+
+TEST_F(BatterySaverActiveProviderTest,
+       ProviderDisabledIfPowerStatusNotInitialized) {
+  InitializePowerStatus(/*should_initialize=*/false);
+  InitializeProvider();
+  EXPECT_FALSE(IsBatterySaverActive());
+}
+
+TEST_F(BatterySaverActiveProviderTest,
+       ProviderEnabledOnBatterySaverModeEnabled) {
+  InitializePowerStatus(/*should_initialize=*/true);
+  InitializeProvider();
+  SetBatterySaverStateAndNotifyProvider(/*is_enabled=*/true);
+  EXPECT_TRUE(IsBatterySaverActive());
+}
+
+TEST_F(BatterySaverActiveProviderTest,
+       ProviderDisabledOnBatterySaverModeDisabled) {
+  InitializePowerStatus(/*should_initialize=*/true);
+  InitializeProvider();
+  SetBatterySaverStateAndNotifyProvider(/*is_enabled=*/false);
+  EXPECT_FALSE(IsBatterySaverActive());
+}
+
+}  // namespace quick_pair
+}  // namespace ash
diff --git a/ash/shelf/drag_handle.cc b/ash/shelf/drag_handle.cc
index 81e618f..8bda7b0e 100644
--- a/ash/shelf/drag_handle.cc
+++ b/ash/shelf/drag_handle.cc
@@ -136,8 +136,9 @@
   if (!show_drag_handle_nudge_timer_.IsRunning())
     overview_observation_.Reset();
 
-  if (!features::AreContextualNudgesEnabled())
+  if (!features::IsHideShelfControlsInTabletModeEnabled()) {
     return false;
+  }
 
   // Do not show drag handle nudge if it is already shown or drag handle is not
   // visible.
@@ -252,7 +253,7 @@
 }
 
 void DragHandle::OnGestureEvent(ui::GestureEvent* event) {
-  if (!features::AreContextualNudgesEnabled() ||
+  if (!features::IsHideShelfControlsInTabletModeEnabled() ||
       !gesture_nudge_target_visibility_) {
     return;
   }
diff --git a/ash/shelf/home_button.cc b/ash/shelf/home_button.cc
index 33a05de..d8ffb18 100644
--- a/ash/shelf/home_button.cc
+++ b/ash/shelf/home_button.cc
@@ -988,15 +988,13 @@
 }
 
 void HomeButton::RemoveNudgeLabel() {
-  RemoveChildViewT(expandable_container_.get());
-  expandable_container_ = nullptr;
   nudge_label_ = nullptr;
+  RemoveChildViewT(expandable_container_.ExtractAsDangling());
 }
 
 void HomeButton::RemoveQuickAppButton() {
-  RemoveChildViewT(expandable_container_.get());
-  expandable_container_ = nullptr;
   quick_app_button_ = nullptr;
+  RemoveChildViewT(expandable_container_.ExtractAsDangling());
 }
 
 bool HomeButton::DoesIntersectRect(const views::View* target,
diff --git a/ash/shelf/home_button.h b/ash/shelf/home_button.h
index 5d56faa..4039050 100644
--- a/ash/shelf/home_button.h
+++ b/ash/shelf/home_button.h
@@ -248,29 +248,29 @@
   // The container of `nudge_label_` or `quick_app_button_`. This is also
   // responsible for painting the background of the contents. This container can
   // expand visually by animation.
-  raw_ptr<views::View, DanglingUntriaged> expandable_container_ = nullptr;
+  raw_ptr<views::View> expandable_container_ = nullptr;
 
   // The app button which is shown next to the home button. Only shown when
   // set by SetQuickApp().
-  raw_ptr<views::ImageButton, DanglingUntriaged> quick_app_button_ = nullptr;
+  raw_ptr<views::ImageButton> quick_app_button_ = nullptr;
 
   // The controller used to determine the button's behavior.
   HomeButtonController controller_;
 
+  // The delegate used by |nudge_ripple_layer_|. Only exists during the
+  // nudge animation.
+  std::unique_ptr<views::CircleLayerDelegate> ripple_layer_delegate_;
+
   // The ripple layer in the launcher nudge animation. Only exists during the
   // nudge animation.
   ui::LayerOwner nudge_ripple_layer_;
 
   // The label view and for launcher nudge animation.
-  raw_ptr<views::Label, DanglingUntriaged> nudge_label_ = nullptr;
+  raw_ptr<views::Label> nudge_label_ = nullptr;
 
   // The timer that counts down to hide the nudge_label_ from showing state.
   base::OneShotTimer label_nudge_timer_;
 
-  // The delegate used by |nudge_ripple_layer_|. Only exists during the
-  // nudge animation.
-  std::unique_ptr<views::CircleLayerDelegate> ripple_layer_delegate_;
-
   std::unique_ptr<ScopedNoClipRect> scoped_no_clip_rect_;
 
   base::ObserverList<NudgeAnimationObserver> observers_;
diff --git a/ash/shelf/home_to_overview_nudge_controller_unittest.cc b/ash/shelf/home_to_overview_nudge_controller_unittest.cc
index 34c409f..3030762 100644
--- a/ash/shelf/home_to_overview_nudge_controller_unittest.cc
+++ b/ash/shelf/home_to_overview_nudge_controller_unittest.cc
@@ -52,7 +52,7 @@
  public:
   HomeToOverviewNudgeControllerWithNudgesDisabledTest() {
     scoped_feature_list_.InitAndDisableFeature(
-        ash::features::kContextualNudges);
+        features::kHideShelfControlsInTabletMode);
   }
   ~HomeToOverviewNudgeControllerWithNudgesDisabledTest() override = default;
 
@@ -70,9 +70,8 @@
 class HomeToOverviewNudgeControllerTest : public AshTestBase {
  public:
   HomeToOverviewNudgeControllerTest() {
-    scoped_feature_list_.InitWithFeatures(
-        {features::kContextualNudges, features::kHideShelfControlsInTabletMode},
-        {});
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kHideShelfControlsInTabletMode);
   }
   ~HomeToOverviewNudgeControllerTest() override = default;
 
diff --git a/ash/shelf/shelf_drag_handle_unittest.cc b/ash/shelf/shelf_drag_handle_unittest.cc
index 13d2620c..15aaa52 100644
--- a/ash/shelf/shelf_drag_handle_unittest.cc
+++ b/ash/shelf/shelf_drag_handle_unittest.cc
@@ -41,10 +41,8 @@
 class DragHandleContextualNudgeTest : public ShelfLayoutManagerTestBase {
  public:
   DragHandleContextualNudgeTest() {
-    scoped_feature_list_.InitWithFeatures(
-        {ash::features::kContextualNudges,
-         ash::features::kHideShelfControlsInTabletMode},
-        {});
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kHideShelfControlsInTabletMode);
   }
   ~DragHandleContextualNudgeTest() override = default;
 
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index cfbd2df..4f9f167 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -786,8 +786,9 @@
 }
 
 void ShelfLayoutManager::UpdateContextualNudges() {
-  if (!ash::features::AreContextualNudgesEnabled())
+  if (!features::IsHideShelfControlsInTabletModeEnabled()) {
     return;
+  }
 
   // Do not allow nudges outside of an active session.
   if (Shell::Get()->session_controller()->GetSessionState() !=
@@ -832,8 +833,9 @@
 }
 
 void ShelfLayoutManager::HideContextualNudges() {
-  if (!ash::features::AreContextualNudgesEnabled())
+  if (!features::IsHideShelfControlsInTabletModeEnabled()) {
     return;
+  }
 
   shelf_widget_->HideDragHandleNudge(
       contextual_tooltip::DismissNudgeReason::kOther);
diff --git a/ash/shelf/shelf_layout_manager_pixeltest.cc b/ash/shelf/shelf_layout_manager_pixeltest.cc
index 462f9343..0a001b4b 100644
--- a/ash/shelf/shelf_layout_manager_pixeltest.cc
+++ b/ash/shelf/shelf_layout_manager_pixeltest.cc
@@ -19,8 +19,9 @@
       public testing::WithParamInterface<bool /*is_tablet_mode=*/> {
  public:
   ShelfLayoutManagerPixelRTLTest() {
+    // Disable kHideShelfControlsInTabletMode to disable contextual nudges.
     scoped_feature_list_.InitWithFeatureStates(
-        {{features::kContextualNudges, false},
+        {{features::kHideShelfControlsInTabletMode, false},
          {chromeos::features::kJelly, true}});
   }
 
@@ -55,7 +56,7 @@
   shelf->SetAutoHideBehavior(ShelfAutoHideBehavior::kNever);
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "shelf_no_auto_hide",
-      /*revision_number=*/11, shelf->GetWindow(), shelf->hotseat_widget()));
+      /*revision_number=*/12, shelf->GetWindow(), shelf->hotseat_widget()));
 
   // When the auto-hide is set and a window is shown fullscreen, the shelf
   // should not be showing on the screen.
@@ -72,7 +73,7 @@
 
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "shelf_show_with_auto_hide",
-      /*revision_number=*/10, shelf->GetWindow(), shelf->hotseat_widget()));
+      /*revision_number=*/11, shelf->GetWindow(), shelf->hotseat_widget()));
 }
 
 }  // namespace ash
diff --git a/ash/shelf/shelf_widget.cc b/ash/shelf/shelf_widget.cc
index 826a7bd..db118ac 100644
--- a/ash/shelf/shelf_widget.cc
+++ b/ash/shelf/shelf_widget.cc
@@ -1035,7 +1035,7 @@
   // Tap on in-app shelf should show a contextual nudge for in-app to home
   // gesture.
   if (event->type() == ui::ET_GESTURE_TAP && ShelfConfig::Get()->is_in_app() &&
-      features::AreContextualNudgesEnabled()) {
+      features::IsHideShelfControlsInTabletModeEnabled()) {
     if (delegate_view_->drag_handle()->MaybeShowDragHandleNudge()) {
       event->StopPropagation();
       return;
diff --git a/ash/shelf/swipe_home_to_overview_controller.cc b/ash/shelf/swipe_home_to_overview_controller.cc
index 99663a70..e1edd754 100644
--- a/ash/shelf/swipe_home_to_overview_controller.cc
+++ b/ash/shelf/swipe_home_to_overview_controller.cc
@@ -181,7 +181,7 @@
   state_ = State::kFinished;
   overview_transition_threshold_y_ = 0;
 
-  if (features::AreContextualNudgesEnabled()) {
+  if (features::IsHideShelfControlsInTabletModeEnabled()) {
     contextual_tooltip::HandleGesturePerformed(
         Shell::Get()->session_controller()->GetActivePrefService(),
         contextual_tooltip::TooltipType::kHomeToOverview);
diff --git a/ash/system/focus_mode/focus_mode_tray.cc b/ash/system/focus_mode/focus_mode_tray.cc
index 2dd7cb6a..87e1cac 100644
--- a/ash/system/focus_mode/focus_mode_tray.cc
+++ b/ash/system/focus_mode/focus_mode_tray.cc
@@ -151,7 +151,7 @@
   progress_indicator_ =
       ProgressIndicator::CreateDefaultInstance(base::BindRepeating(
           [](FocusModeTray* view) -> std::optional<float> {
-            if (!view->GetVisible() || view->is_active()) {
+            if (!view->visible_preferred() || view->is_active()) {
               return 0.0f;
             }
             if (view->show_progress_ring_after_animation_) {
diff --git a/ash/system/network/network_list_view_controller_impl.cc b/ash/system/network/network_list_view_controller_impl.cc
index d07fd0c..406f297 100644
--- a/ash/system/network/network_list_view_controller_impl.cc
+++ b/ash/system/network/network_list_view_controller_impl.cc
@@ -12,6 +12,7 @@
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
+#include "ash/shell_delegate.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
 #include "ash/style/typography.h"
@@ -27,6 +28,7 @@
 #include "base/timer/timer.h"
 #include "chromeos/ash/components/dbus/hermes/hermes_manager_client.h"
 #include "chromeos/ash/services/bluetooth_config/public/cpp/cros_bluetooth_config_util.h"
+#include "chromeos/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
 #include "chromeos/services/network_config/public/cpp/cros_network_config_util.h"
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
 #include "chromeos/services/network_config/public/mojom/network_types.mojom-shared.h"
@@ -151,6 +153,18 @@
   remote_cros_bluetooth_config_->ObserveSystemProperties(
       cros_system_properties_observer_receiver_.BindNewPipeAndPassRemote());
 
+  if (features::IsInstantHotspotRebrandEnabled()) {
+    Shell::Get()->shell_delegate()->BindMultiDeviceSetup(
+        multidevice_setup_remote_.BindNewPipeAndPassReceiver());
+
+    multidevice_setup_remote_->AddHostStatusObserver(
+        host_status_observer_receiver_.BindNewPipeAndPassRemote());
+
+    multidevice_setup_remote_->GetHostStatus(
+        base::BindOnce(&NetworkListViewControllerImpl::OnHostStatusChanged,
+                       weak_ptr_factory_.GetWeakPtr()));
+  }
+
   GetNetworkStateList();
 }
 
@@ -194,6 +208,15 @@
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
+void NetworkListViewControllerImpl::OnHostStatusChanged(
+    multidevice_setup::mojom::HostStatus host_status,
+    const std::optional<multidevice::RemoteDevice>& host_device) {
+  has_phone_eligible_for_setup_ =
+      (host_status ==
+       multidevice_setup::mojom::HostStatus::kEligibleHostExistsButNoHostSet);
+  GetNetworkStateList();
+}
+
 void NetworkListViewControllerImpl::OnGetNetworkStateList(
     std::vector<NetworkStatePropertiesPtr> networks) {
   int old_position = network_detailed_network_view()->GetScrollPosition();
@@ -439,7 +462,6 @@
 
 void NetworkListViewControllerImpl::MaybeShowConnectionWarningManagedIcon(
     bool using_proxy) {
-
   // If the proxy is set, check if it's a managed setting.
   const NetworkStateProperties* default_network = model()->default_network();
   if (using_proxy && default_network) {
@@ -580,7 +602,16 @@
 
 bool NetworkListViewControllerImpl::ShouldTetherHostsSectionBeShown() {
   // The section should never be shown if the feature flag is disabled.
-  DCHECK(features::IsInstantHotspotRebrandEnabled());
+  if (!features::IsInstantHotspotRebrandEnabled()) {
+    return false;
+  }
+
+  // The Tether Hosts section should always be shown if there is an eligible
+  // device which has not been set up yet.
+  if (has_phone_eligible_for_setup_) {
+    return true;
+  }
+
   const DeviceStateType tether_state =
       model()->GetDeviceState(NetworkType::kTether);
 
diff --git a/ash/system/network/network_list_view_controller_impl.h b/ash/system/network/network_list_view_controller_impl.h
index 7b65603..2603c37bb 100644
--- a/ash/system/network/network_list_view_controller_impl.h
+++ b/ash/system/network/network_list_view_controller_impl.h
@@ -25,6 +25,7 @@
 #include "base/memory/raw_ptr_exclusion.h"
 #include "base/timer/timer.h"
 #include "chromeos/ash/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom.h"
+#include "chromeos/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -43,6 +44,7 @@
 class ASH_EXPORT NetworkListViewControllerImpl
     : public TrayNetworkStateObserver,
       public NetworkListViewController,
+      public multidevice_setup::mojom::HostStatusObserver,
       public bluetooth_config::mojom::SystemPropertiesObserver {
  public:
   NetworkListViewControllerImpl(
@@ -82,6 +84,11 @@
   using NetworkIdToViewMap =
       base::flat_map<std::string, NetworkListNetworkItemView*>;
 
+  // multidevice_setup::mojom::HostStatusObserver:
+  void OnHostStatusChanged(
+      multidevice_setup::mojom::HostStatus host_status,
+      const std::optional<multidevice::RemoteDevice>& host_device) override;
+
   // TrayNetworkStateObserver:
   void ActiveNetworkStateChanged() override;
   void NetworkListChanged() override;
@@ -217,6 +224,10 @@
       remote_cros_bluetooth_config_;
   mojo::Receiver<bluetooth_config::mojom::SystemPropertiesObserver>
       cros_system_properties_observer_receiver_{this};
+  mojo::Remote<multidevice_setup::mojom::MultiDeviceSetup>
+      multidevice_setup_remote_;
+  mojo::Receiver<multidevice_setup::mojom::HostStatusObserver>
+      host_status_observer_receiver_{this};
 
   bluetooth_config::mojom::BluetoothSystemState bluetooth_system_state_ =
       bluetooth_config::mojom::BluetoothSystemState::kUnavailable;
@@ -284,6 +295,10 @@
   // managed.
   bool is_vpn_managed_ = false;
 
+  // Indicates whether the user has a phone which could be set up via the
+  // cross-device suite of features.
+  bool has_phone_eligible_for_setup_ = false;
+
   raw_ptr<NetworkDetailedNetworkView, DanglingUntriaged>
       network_detailed_network_view_;
   NetworkIdToViewMap network_id_to_view_map_;
diff --git a/ash/system/network/network_list_view_controller_unittest.cc b/ash/system/network/network_list_view_controller_unittest.cc
index eefefa9..769a42a 100644
--- a/ash/system/network/network_list_view_controller_unittest.cc
+++ b/ash/system/network/network_list_view_controller_unittest.cc
@@ -27,6 +27,8 @@
 #include "ash/system/tray/tri_view.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/ash_test_helper.h"
+#include "ash/test_shell_delegate.h"
+#include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
 #include "base/test/bind.h"
@@ -36,6 +38,9 @@
 #include "chromeos/ash/services/bluetooth_config/fake_adapter_state_controller.h"
 #include "chromeos/ash/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom.h"
 #include "chromeos/ash/services/bluetooth_config/scoped_bluetooth_config_test_helper.h"
+#include "chromeos/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup.h"
+#include "chromeos/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom-shared.h"
+#include "chromeos/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
 #include "chromeos/ash/services/network_config/public/cpp/cros_network_config_test_helper.h"
 #include "chromeos/services/network_config/public/cpp/cros_network_config_util.h"
 #include "chromeos/services/network_config/public/cpp/fake_cros_network_config.h"
@@ -193,7 +198,14 @@
     }
     feature_list_.InitWithFeatures(enabled_features, disabled_features);
 
-    AshTestBase::SetUp();
+    fake_multidevice_setup_ =
+        std::make_unique<multidevice_setup::FakeMultiDeviceSetup>();
+    auto delegate = std::make_unique<TestShellDelegate>();
+    delegate->SetMultiDeviceSetupBinder(base::BindRepeating(
+        &multidevice_setup::MultiDeviceSetupBase::BindReceiver,
+        base::Unretained(fake_multidevice_setup_.get())));
+
+    AshTestBase::SetUp(std::move(delegate));
 
     cros_network_ = std::make_unique<FakeCrosNetworkConfig>();
     Shell::Get()
@@ -451,6 +463,9 @@
 
   FakeCrosNetworkConfig* cros_network() { return cros_network_.get(); }
 
+  std::unique_ptr<multidevice_setup::FakeMultiDeviceSetup>
+      fake_multidevice_setup_;
+
   base::HistogramTester histogram_tester;
 
  private:
@@ -839,6 +854,39 @@
                        /*guid=*/kCellularName);
 }
 
+TEST_P(NetworkListViewControllerTest,
+       WillShowTetherHostsNetworkListWhenHostIsAvailable) {
+  for (const auto& host_status :
+       {multidevice_setup::mojom::HostStatus::kNoEligibleHosts,
+        multidevice_setup::mojom::HostStatus::kHostVerified,
+        multidevice_setup::mojom::HostStatus::
+            kHostSetLocallyButWaitingForBackendConfirmation,
+        multidevice_setup::mojom::HostStatus::kHostSetButNotYetVerified}) {
+    fake_multidevice_setup_->NotifyHostStatusChanged(host_status, std::nullopt);
+    fake_multidevice_setup_->FlushForTesting();
+    base::RunLoop().RunUntilIdle();
+
+    // Since we didn't send a notification that the host is ready and not set,
+    // the Tether section shouldn't be shown regardless of the value of the
+    // feature flag.
+    EXPECT_THAT(GetTetherHostsSubHeader(), IsNull());
+  }
+
+  fake_multidevice_setup_->NotifyHostStatusChanged(
+      multidevice_setup::mojom::HostStatus::kEligibleHostExistsButNoHostSet,
+      std::nullopt);
+  fake_multidevice_setup_->FlushForTesting();
+  base::RunLoop().RunUntilIdle();
+
+  // If the rebrand is enabled, the tether section should be shown. If not, it
+  // shouldn't be
+  if (IsInstantHotspotRebrandEnabled()) {
+    EXPECT_THAT(GetTetherHostsSubHeader(), NotNull());
+  } else {
+    EXPECT_THAT(GetTetherHostsSubHeader(), IsNull());
+  }
+}
+
 TEST_P(NetworkListViewControllerTest, HasCorrectTetherHostsNetworkList) {
   EXPECT_EQ(0u, network_list(NetworkType::kTether)->children().size());
   EXPECT_THAT(GetTetherHostsSubHeader(), IsNull());
diff --git a/ash/system/palette/palette_tray.cc b/ash/system/palette/palette_tray.cc
index 4904be6..dd227e43 100644
--- a/ash/system/palette/palette_tray.cc
+++ b/ash/system/palette/palette_tray.cc
@@ -181,13 +181,11 @@
                                           *title_label);
     layout_ptr->SetFlexForView(title_label, 1);
 
-    if (ash::features::IsStylusBatteryStatusEnabled()) {
-      AddChildView(std::make_unique<BatteryView>());
+    AddChildView(std::make_unique<BatteryView>());
 
-      auto* separator = AddChildView(std::make_unique<views::Separator>());
-      separator->SetPreferredLength(GetPreferredSize().height());
-      separator->SetColorId(ui::kColorAshSystemUIMenuSeparator);
-    }
+    auto* separator = AddChildView(std::make_unique<views::Separator>());
+    separator->SetPreferredLength(GetPreferredSize().height());
+    separator->SetColorId(ui::kColorAshSystemUIMenuSeparator);
 
     help_button_ = AddChildView(std::make_unique<IconButton>(
         base::BindRepeating(
diff --git a/ash/system/power/peripheral_battery_notifier.cc b/ash/system/power/peripheral_battery_notifier.cc
index 7c12541..7e202b4 100644
--- a/ash/system/power/peripheral_battery_notifier.cc
+++ b/ash/system/power/peripheral_battery_notifier.cc
@@ -124,17 +124,6 @@
 
 void PeripheralBatteryNotifier::OnUpdatedBatteryLevel(
     const PeripheralBatteryListener::BatteryInfo& battery_info) {
-  if ((battery_info.type == PeripheralBatteryListener::BatteryInfo::
-                                PeripheralType::kStylusViaCharger ||
-       battery_info.type == PeripheralBatteryListener::BatteryInfo::
-                                PeripheralType::kStylusViaScreen) &&
-      !ash::features::IsStylusBatteryStatusEnabled()) {
-    VLOG(1)
-        << "PeripheralBatteryNotifier: Notification not shown, PeripheralType: "
-        << static_cast<int>(battery_info.type);
-    return;
-  }
-
   // TODO(b/187703348): it is worth listening to charger events if they
   // might remove the notification: we want to clear it as soon as
   // we believe the battery to have been charged, or at least starting
diff --git a/ash/system/power/peripheral_battery_notifier_listener_integration_test.cc b/ash/system/power/peripheral_battery_notifier_listener_integration_test.cc
index 9782c009..fca0a23 100644
--- a/ash/system/power/peripheral_battery_notifier_listener_integration_test.cc
+++ b/ash/system/power/peripheral_battery_notifier_listener_integration_test.cc
@@ -300,8 +300,6 @@
   const std::string kTestStylusBatteryPath =
       "/sys/class/power_supply/hid-AAAA:BBBB:CCCC.DDDD-battery";
   const std::string kTestStylusName = "test_stylus";
-  base::test::ScopedFeatureList flags;
-  flags.InitAndEnableFeature(features::kStylusBatteryStatus);
 
   // Add an external stylus to our test device manager.
   ui::TouchscreenDevice stylus(/*id=*/0, ui::INPUT_DEVICE_USB, kTestStylusName,
@@ -341,58 +339,10 @@
       message_center_->FindVisibleNotificationById(kTestBatteryAddress));
 }
 
-TEST_F(PeripheralBatteryNotifierListenerIncompleteDevicesTest,
-       StylusNotificationDisabled) {
-  const std::string kTestStylusBatteryPath =
-      "/sys/class/power_supply/hid-AAAA:BBBB:CCCC.DDDD-battery";
-  const std::string kTestStylusName = "test_stylus";
-  base::test::ScopedFeatureList flags;
-  flags.InitAndDisableFeature(features::kStylusBatteryStatus);
-
-  // Add an external stylus to our test device manager.
-  ui::TouchscreenDevice stylus(/*id=*/0, ui::INPUT_DEVICE_USB, kTestStylusName,
-                               gfx::Size(),
-                               /*touch_points=*/1, /*has_stylus=*/true);
-  stylus.sys_path = base::FilePath(kTestStylusBatteryPath);
-
-  ui::DeviceDataManagerTestApi().SetTouchscreenDevices({stylus});
-
-  // Verify that when the battery level is 5, a stylus low battery notification
-  // is not shown due to input device list not being complete. Also check that
-  // a non stylus device low battery notification will not show up.
-  SendBatteryUpdate(
-      kTestStylusBatteryPath, kTestStylusName, 5,
-      power_manager::
-          PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_DISCHARGING,
-      /*serial_number=*/"", kBatteryEventUpdate);
-  EXPECT_FALSE(message_center_->FindVisibleNotificationById(
-      PeripheralBatteryNotifier::kStylusNotificationId));
-  EXPECT_FALSE(
-      message_center_->FindVisibleNotificationById(kTestBatteryAddress));
-
-  // Complete devices
-  ui::DeviceDataManagerTestApi().OnDeviceListsComplete();
-
-  // Verify that when the battery level is 5, a stylus low battery notification
-  // is now shown. Also check that a non stylus device low battery notification
-  // will still not show up.
-  SendBatteryUpdate(
-      kTestStylusBatteryPath, kTestStylusName, 5,
-      power_manager::
-          PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_DISCHARGING,
-      /*serial_number=*/"", kBatteryEventUpdate);
-  EXPECT_FALSE(message_center_->FindVisibleNotificationById(
-      PeripheralBatteryNotifier::kStylusNotificationId));
-  EXPECT_FALSE(
-      message_center_->FindVisibleNotificationById(kTestBatteryAddress));
-}
-
 TEST_F(PeripheralBatteryNotifierListenerTest, StylusNotification) {
   const std::string kTestStylusBatteryPath =
       "/sys/class/power_supply/hid-AAAA:BBBB:CCCC.DDDD-battery";
   const std::string kTestStylusName = "test_stylus";
-  base::test::ScopedFeatureList flags;
-  flags.InitAndEnableFeature(features::kStylusBatteryStatus);
 
   // Add an external stylus to our test device manager.
   ui::TouchscreenDevice stylus(/*id=*/0, ui::INPUT_DEVICE_USB, kTestStylusName,
@@ -432,43 +382,6 @@
       PeripheralBatteryNotifier::kStylusNotificationId));
 }
 
-TEST_F(PeripheralBatteryNotifierListenerTest, StylusNotificationDisabled) {
-  const std::string kTestStylusBatteryPath =
-      "/sys/class/power_supply/hid-AAAA:BBBB:CCCC.DDDD-battery";
-  const std::string kTestStylusName = "test_stylus";
-  base::test::ScopedFeatureList flags;
-  flags.InitAndDisableFeature(features::kStylusBatteryStatus);
-
-  // Add an external stylus to our test device manager.
-  ui::TouchscreenDevice stylus(/*id=*/0, ui::INPUT_DEVICE_USB, kTestStylusName,
-                               gfx::Size(),
-                               /*touch_points=*/1, /*has_stylus=*/true);
-  stylus.sys_path = base::FilePath(kTestStylusBatteryPath);
-
-  ui::DeviceDataManagerTestApi().SetTouchscreenDevices({stylus});
-
-  // Verify that when the battery level is 50, no stylus low battery
-  // notification is shown.
-  SendBatteryUpdate(kTestStylusBatteryPath, kTestStylusName, 50);
-  EXPECT_FALSE(message_center_->FindVisibleNotificationById(
-      PeripheralBatteryNotifier::kStylusNotificationId));
-
-  // Verify that when the battery level is 5, a stylus low battery notification
-  // is shown. Also check that a non stylus device low battery notification will
-  // not show up.
-  SendBatteryUpdate(kTestStylusBatteryPath, kTestStylusName, 5);
-  EXPECT_FALSE(message_center_->FindVisibleNotificationById(
-      PeripheralBatteryNotifier::kStylusNotificationId));
-  EXPECT_FALSE(
-      message_center_->FindVisibleNotificationById(kTestBatteryAddress));
-
-  // Verify that when the battery level is -1, the previous stylus low battery
-  // notification is cancelled.
-  SendBatteryUpdate(kTestStylusBatteryPath, kTestStylusName, -1);
-  EXPECT_FALSE(message_center_->FindVisibleNotificationById(
-      PeripheralBatteryNotifier::kStylusNotificationId));
-}
-
 TEST_F(PeripheralBatteryNotifierListenerTest,
        Bluetooth_OnlyShowNotificationForLowBatteryLevels) {
   // Should not create a notification for battery changes above the threshold.
diff --git a/ash/system/power/peripheral_battery_notifier_unittest.cc b/ash/system/power/peripheral_battery_notifier_unittest.cc
index 705cb73..ac766a8 100644
--- a/ash/system/power/peripheral_battery_notifier_unittest.cc
+++ b/ash/system/power/peripheral_battery_notifier_unittest.cc
@@ -235,8 +235,6 @@
       "???hxxxxid-AAAA:BBBB:CCCC.DDDD-battery";
   const std::string kTestStylusName = "test_stylus";
   const std::u16string kTestStylusName16 = u"test_stylus";
-  base::test::ScopedFeatureList flags;
-  flags.InitAndEnableFeature(features::kStylusBatteryStatus);
 
   // Add an external stylus to our test device manager.
   ui::TouchscreenDevice stylus(
@@ -275,53 +273,6 @@
       PeripheralBatteryNotifier::kStylusNotificationId));
 }
 
-TEST_F(PeripheralBatteryNotifierTest, StylusNotificationDisabled) {
-  const std::string kTestStylusBatteryPath =
-      "/sys/class/power_supply/hid-AAAA:BBBB:CCCC.DDDD-battery";
-  const std::string kTestStylusBatteryId =
-      "???hxxxxid-AAAA:BBBB:CCCC.DDDD-battery";
-  const std::string kTestStylusName = "test_stylus";
-  const std::u16string kTestStylusName16 = u"test_stylus";
-  base::test::ScopedFeatureList flags;
-  flags.InitAndDisableFeature(features::kStylusBatteryStatus);
-
-  // Add an external stylus to our test device manager.
-  ui::TouchscreenDevice stylus(
-      /*id=*/0, ui::INPUT_DEVICE_USB, kTestStylusName, gfx::Size(),
-      /*touch_points=*/1,
-      /*has_stylus=*/true);
-  stylus.sys_path = base::FilePath(kTestStylusBatteryPath);
-
-  ui::DeviceDataManagerTestApi().SetTouchscreenDevices({stylus});
-
-  // Verify that when the battery level is 50, no stylus low battery
-  // notification is shown.
-  UpdateBatteryLevel(true, kTestStylusBatteryId, kTestStylusName16, 50,
-                     /*battery_report_eligible=*/true,
-                     BI::PeripheralType::kStylusViaScreen, "");
-  EXPECT_FALSE(message_center_->FindVisibleNotificationById(
-      PeripheralBatteryNotifier::kStylusNotificationId));
-
-  // Verify that when the battery level is 5, a stylus low battery notification
-  // is shown. Also check that a non stylus device low battery notification will
-  // not show up.
-  UpdateBatteryLevel(false, kTestStylusBatteryId, kTestStylusName16, 5,
-                     /*battery_report_eligible=*/true,
-                     BI::PeripheralType::kStylusViaScreen, "");
-  EXPECT_FALSE(message_center_->FindVisibleNotificationById(
-      PeripheralBatteryNotifier::kStylusNotificationId));
-  EXPECT_FALSE(
-      message_center_->FindVisibleNotificationById(kTestBatteryAddress));
-
-  // Verify that when the battery level is -1, the previous stylus low battery
-  // notification is cancelled.
-  UpdateBatteryLevel(false, kTestStylusBatteryId, kTestStylusName16,
-                     std::nullopt, /*battery_report_eligible=*/true,
-                     BI::PeripheralType::kStylusViaScreen, "");
-  EXPECT_FALSE(message_center_->FindVisibleNotificationById(
-      PeripheralBatteryNotifier::kStylusNotificationId));
-}
-
 TEST_F(PeripheralBatteryNotifierTest,
        Bluetooth_CreatesANotificationForEachDevice) {
   UpdateBatteryLevel(true, kBluetoothDeviceId1, kBluetoothDeviceName116, 5,
diff --git a/ash/user_education/holding_space_wallpaper_nudge/holding_space_wallpaper_nudge_controller.cc b/ash/user_education/holding_space_wallpaper_nudge/holding_space_wallpaper_nudge_controller.cc
index 2d86a5d..e185c70 100644
--- a/ash/user_education/holding_space_wallpaper_nudge/holding_space_wallpaper_nudge_controller.cc
+++ b/ash/user_education/holding_space_wallpaper_nudge/holding_space_wallpaper_nudge_controller.cc
@@ -29,6 +29,7 @@
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/holding_space/holding_space_tray.h"
 #include "ash/system/status_area_widget.h"
+#include "ash/user_education/holding_space_wallpaper_nudge/holding_space_wallpaper_nudge_metrics.h"
 #include "ash/user_education/holding_space_wallpaper_nudge/holding_space_wallpaper_nudge_prefs.h"
 #include "ash/user_education/user_education_controller.h"
 #include "ash/user_education/user_education_help_bubble_controller.h"
@@ -521,10 +522,28 @@
     // See `holding_space_wallpaper_nudge_metrics::RecordInteraction()`.
   }
 
+  // TODO(http://b/311411775): Relocate recording wallpaper nudge histograms
+  // into the production metrics code path when cleaning up the experiment.
   void OnHoldingSpacePodActionRecorded(
       holding_space_metrics::PodAction action) override {
-    // TODO(http://b/311411775): Record wallpaper nudge experiment metrics.
-    // See `holding_space_wallpaper_nudge_metrics::RecordInteraction()`.
+    using holding_space_metrics::PodAction;
+    using holding_space_wallpaper_nudge_metrics::Interaction;
+
+    switch (action) {
+      case PodAction::kDragAndDropToPin:
+        RecordInteraction(Interaction::kDroppedFileOnHoldingSpace);
+        break;
+      case PodAction::kShowBubble:
+        RecordInteraction(Interaction::kOpenedHoldingSpace);
+        break;
+      case PodAction::kCloseBubble:
+      case PodAction::kHidePod:
+      case PodAction::kHidePreviews:
+      case PodAction::kShowContextMenu:
+      case PodAction::kShowPod:
+      case PodAction::kShowPreviews:
+        break;
+    }
   }
 
   // SessionObserver:
diff --git a/ash/user_education/holding_space_wallpaper_nudge/holding_space_wallpaper_nudge_controller_unittest.cc b/ash/user_education/holding_space_wallpaper_nudge/holding_space_wallpaper_nudge_controller_unittest.cc
index ce44e82..ad7c233 100644
--- a/ash/user_education/holding_space_wallpaper_nudge/holding_space_wallpaper_nudge_controller_unittest.cc
+++ b/ash/user_education/holding_space_wallpaper_nudge/holding_space_wallpaper_nudge_controller_unittest.cc
@@ -31,6 +31,7 @@
 #include "ash/system/holding_space/holding_space_tray.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/test/test_widget_builder.h"
+#include "ash/user_education/holding_space_wallpaper_nudge/holding_space_wallpaper_nudge_metrics.h"
 #include "ash/user_education/holding_space_wallpaper_nudge/holding_space_wallpaper_nudge_prefs.h"
 #include "ash/user_education/mock_user_education_delegate.h"
 #include "ash/user_education/user_education_ash_test_base.h"
@@ -45,6 +46,7 @@
 #include "base/scoped_observation.h"
 #include "base/strings/strcat.h"
 #include "base/test/bind.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/timer/timer.h"
 #include "base/values.h"
@@ -73,12 +75,18 @@
 namespace {
 
 // Aliases.
+using ::ash::holding_space_wallpaper_nudge_metrics::Interaction;
+using ::base::Bucket;
+using ::base::BucketsAre;
 using ::testing::_;
 using ::testing::AnyOf;
+using ::testing::Conditional;
 using ::testing::ElementsAre;
 using ::testing::Eq;
 using ::testing::Invoke;
+using ::testing::IsEmpty;
 using ::testing::Pair;
+using ::testing::ReturnRefOfCopy;
 using ::testing::WithArgs;
 
 // Helpers ---------------------------------------------------------------------
@@ -1466,6 +1474,100 @@
       account_id, /*client=*/nullptr, /*model=*/nullptr);
 }
 
+// HoldingSpaceWallpaperNudgeMetricsTest ---------------------------------------
+
+// Base class for tests of the `HoldingSpaceWallpaperNudgeController` which are
+// concerned with metrics, parameterized by whether the holding space wallpaper
+// nudge is enabled.
+class HoldingSpaceWallpaperNudgeMetricsTest
+    : public UserEducationAshTestBase,
+      public ::testing::WithParamInterface</*nudge_enabled=*/bool> {
+ public:
+  HoldingSpaceWallpaperNudgeMetricsTest() {
+    scoped_feature_list_.InitWithFeatureState(
+        features::kHoldingSpaceWallpaperNudge,
+        IsHoldingSpaceWallpaperNudgeEnabled());
+  }
+
+  // Whether the holding space wallpaper nudge is enabled given test
+  // parameterization.
+  bool IsHoldingSpaceWallpaperNudgeEnabled() const { return GetParam(); }
+
+ private:
+  // UserEducationAshTestBase:
+  void SetUp() override {
+    UserEducationAshTestBase::SetUp();
+
+    // When the holding space wallpaper nudge is enabled, provide an
+    // implementation of `IsNewUser()` which always returns `true`, thereby
+    // indicating that the user is new cross-device and therefore eligible for
+    // the experiment.
+    if (IsHoldingSpaceWallpaperNudgeEnabled()) {
+      ON_CALL(*user_education_delegate(), IsNewUser)
+          .WillByDefault(ReturnRefOfCopy(std::make_optional<bool>(true)));
+    }
+
+    // Log in a regular user.
+    const AccountId& account_id = AccountId::FromUserEmail("user@test");
+    SimulateNewUserFirstLogin(account_id.GetUserEmail());
+  }
+
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         HoldingSpaceWallpaperNudgeMetricsTest,
+                         /*nudge_enabled=*/testing::Bool());
+
+// Tests -----------------------------------------------------------------------
+
+// Verifies that when the production holding space metrics code path records a
+// histogram indicating that a file was dropped on holding space, the
+// corresponding interaction histogram in the wallpaper nudge experiment
+// metrics code path is also recorded.
+TEST_P(HoldingSpaceWallpaperNudgeMetricsTest,
+       RecordsInteraction_DroppedFileOnHoldingSpace) {
+  base::HistogramTester histogram_tester;
+
+  // Whenever the production holding space metrics code path records a histogram
+  // to reflect that a file was dropped on holding space...
+  holding_space_metrics::RecordPodAction(
+      holding_space_metrics::PodAction::kDragAndDropToPin);
+
+  // ...record the appropriate interaction histogram in the wallpaper nudge
+  // experiment metrics code path iff the nudge is enabled.
+  EXPECT_THAT(
+      histogram_tester.GetAllSamples(
+          "Ash.HoldingSpaceWallpaperNudge.Interaction.Count"),
+      Conditional(IsHoldingSpaceWallpaperNudgeEnabled(),
+                  BucketsAre(Bucket(Interaction::kDroppedFileOnHoldingSpace,
+                                    /*count=*/1u)),
+                  IsEmpty()));
+}
+
+// Verifies that when the production holding space metrics code path records a
+// histogram indicating that holding space was opened, the corresponding
+// interaction histogram in the wallpaper nudge experiment metrics code path is
+// also recorded.
+TEST_P(HoldingSpaceWallpaperNudgeMetricsTest,
+       RecordsInteraction_OpenedHoldingSpace) {
+  base::HistogramTester histogram_tester;
+
+  // Whenever the production holding space metrics code path records a histogram
+  // to reflect that holding space was opened...
+  holding_space_metrics::RecordPodAction(
+      holding_space_metrics::PodAction::kShowBubble);
+
+  // ...record the appropriate interaction histogram in the wallpaper nudge
+  // experiment metrics code path iff the nudge is enabled.
+  EXPECT_THAT(histogram_tester.GetAllSamples(
+                  "Ash.HoldingSpaceWallpaperNudge.Interaction.Count"),
+              Conditional(IsHoldingSpaceWallpaperNudgeEnabled(),
+                          BucketsAre(Bucket(Interaction::kOpenedHoldingSpace,
+                                            /*count=*/1u)),
+                          IsEmpty()));
+}
+
 // HoldingSpaceWallpaperNudgePlaceholderTest -----------------------------------
 
 // Base class for tests of the `HoldingSpaceWallpaperNudgeController` which are
diff --git a/ash/wallpaper/sea_pen_wallpaper_manager.cc b/ash/wallpaper/sea_pen_wallpaper_manager.cc
index d6ec609..ef089ea 100644
--- a/ash/wallpaper/sea_pen_wallpaper_manager.cc
+++ b/ash/wallpaper/sea_pen_wallpaper_manager.cc
@@ -8,6 +8,7 @@
 
 #include "ash/public/cpp/image_util.h"
 #include "ash/public/cpp/wallpaper/sea_pen_image.h"
+#include "ash/wallpaper/wallpaper_utils/sea_pen_metadata_utils.h"
 #include "base/files/file_path.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
@@ -25,20 +26,20 @@
 void SeaPenWallpaperManager::DecodeAndSaveSeaPenImage(
     const SeaPenImage& sea_pen_image,
     const base::FilePath& wallpaper_dir,
-    const std::string& query_info,
+    const personalization_app::mojom::SeaPenQueryPtr& query,
     DecodeAndSaveSeaPenImageCallback callback) {
   // TODO(b/307591556) also save metadata to a file.
   image_util::DecodeImageData(
-      base::BindOnce(base::BindOnce(
-          &SeaPenWallpaperManager::SaveSeaPenImage, weak_factory_.GetWeakPtr(),
-          sea_pen_image.id, wallpaper_dir, query_info, std::move(callback))),
+      base::BindOnce(&SeaPenWallpaperManager::SaveSeaPenImage,
+                     weak_factory_.GetWeakPtr(), sea_pen_image.id,
+                     wallpaper_dir, query.Clone(), std::move(callback)),
       data_decoder::mojom::ImageCodec::kDefault, sea_pen_image.jpg_bytes);
 }
 
 void SeaPenWallpaperManager::SaveSeaPenImage(
     uint32_t sea_pen_image_id,
     const base::FilePath& wallpaper_dir,
-    const std::string& query_info,
+    const personalization_app::mojom::SeaPenQueryPtr& query,
     DecodeAndSaveSeaPenImageCallback callback,
     const gfx::ImageSkia& image_skia) {
   if (image_skia.isNull()) {
@@ -48,12 +49,13 @@
   }
   DVLOG(2) << __func__ << " image_skia.size()=" << image_skia.size().ToString();
   std::string file_name = base::NumberToString(sea_pen_image_id) + ".jpg";
+  const std::string metadata = QueryDictToXmpString(SeaPenQueryToDict(query));
   auto on_saved = base::BindOnce(&SeaPenWallpaperManager::OnSeaPenImageSaved,
                                  weak_factory_.GetWeakPtr(), image_skia,
                                  std::move(callback));
   wallpaper_file_manager_->SaveWallpaperToDisk(
       WallpaperType::kSeaPen, wallpaper_dir, file_name,
-      WallpaperLayout::WALLPAPER_LAYOUT_CENTER_CROPPED, image_skia, query_info,
+      WallpaperLayout::WALLPAPER_LAYOUT_CENTER_CROPPED, image_skia, metadata,
       std::move(on_saved));
 }
 
diff --git a/ash/wallpaper/sea_pen_wallpaper_manager.h b/ash/wallpaper/sea_pen_wallpaper_manager.h
index cd71b4e..46bd2d16 100644
--- a/ash/wallpaper/sea_pen_wallpaper_manager.h
+++ b/ash/wallpaper/sea_pen_wallpaper_manager.h
@@ -9,6 +9,7 @@
 
 #include "ash/public/cpp/wallpaper/sea_pen_image.h"
 #include "ash/wallpaper/wallpaper_file_manager.h"
+#include "ash/webui/common/mojom/sea_pen.mojom.h"
 #include "base/files/file_path.h"
 #include "base/functional/callback_forward.h"
 #include "base/memory/raw_ptr.h"
@@ -33,15 +34,16 @@
   // Decodes Sea Pen image then save the decoded image into disk. Calls
   // `callback` with the image id and the decoded image. Responds with an empty
   // `ImageSkia` on decoding failure.
-  void DecodeAndSaveSeaPenImage(const SeaPenImage& sea_pen_image,
-                                const base::FilePath& wallpaper_dir,
-                                const std::string& query_info,
-                                DecodeAndSaveSeaPenImageCallback callback);
+  void DecodeAndSaveSeaPenImage(
+      const SeaPenImage& sea_pen_image,
+      const base::FilePath& wallpaper_dir,
+      const personalization_app::mojom::SeaPenQueryPtr& query,
+      DecodeAndSaveSeaPenImageCallback callback);
 
  private:
   void SaveSeaPenImage(uint32_t sea_pen_image_id,
                        const base::FilePath& wallpaper_dir,
-                       const std::string& query_info,
+                       const personalization_app::mojom::SeaPenQueryPtr& query,
                        DecodeAndSaveSeaPenImageCallback callback,
                        const gfx::ImageSkia& image_skia);
 
diff --git a/ash/wallpaper/sea_pen_wallpaper_manager_unittest.cc b/ash/wallpaper/sea_pen_wallpaper_manager_unittest.cc
index 097c59f..1aa14e5b 100644
--- a/ash/wallpaper/sea_pen_wallpaper_manager_unittest.cc
+++ b/ash/wallpaper/sea_pen_wallpaper_manager_unittest.cc
@@ -88,7 +88,8 @@
   ASSERT_FALSE(base::PathExists(file_path));
   sea_pen_wallpaper_manager().DecodeAndSaveSeaPenImage(
       {CreateJpgBytes(), /*id=*/111}, GetTempFileDirectory(),
-      /*query_info=*/"test query", decode_sea_pen_image_future.GetCallback());
+      personalization_app::mojom::SeaPenQuery::NewTextQuery("search query"),
+      decode_sea_pen_image_future.GetCallback());
 
   // Use `AreBitmapsClose` because JPG encoding/decoding can alter the color
   // slightly.
@@ -119,7 +120,8 @@
   // Decode and save the 11th sea pen image in the temp directory.
   sea_pen_wallpaper_manager().DecodeAndSaveSeaPenImage(
       {CreateJpgBytes(), /*id=*/11}, GetTempFileDirectory(),
-      /*query_info=*/"test query", decode_sea_pen_image_future.GetCallback());
+      personalization_app::mojom::SeaPenQuery::NewTextQuery("test query"),
+      decode_sea_pen_image_future.GetCallback());
 
   // Use `AreBitmapsClose` because JPG encoding/decoding can alter the color
   // slightly.
diff --git a/ash/wallpaper/test_wallpaper_controller_client.cc b/ash/wallpaper/test_wallpaper_controller_client.cc
index bcd2c3c8..8d559be 100644
--- a/ash/wallpaper/test_wallpaper_controller_client.cc
+++ b/ash/wallpaper/test_wallpaper_controller_client.cc
@@ -89,6 +89,7 @@
     const AccountId& account_id,
     const std::string& id,
     FetchGooglePhotosPhotoCallback callback) {
+  fetch_google_photos_photo_id_ = id;
   auto iter = wallpaper_google_photos_integration_enabled_.find(account_id);
   if (iter != wallpaper_google_photos_integration_enabled_.end() &&
       !iter->second) {
diff --git a/ash/wallpaper/test_wallpaper_controller_client.h b/ash/wallpaper/test_wallpaper_controller_client.h
index 759d11f..2e155cf 100644
--- a/ash/wallpaper/test_wallpaper_controller_client.h
+++ b/ash/wallpaper/test_wallpaper_controller_client.h
@@ -38,6 +38,9 @@
   size_t fetch_images_for_collection_count() const {
     return fetch_images_for_collection_count_;
   }
+  std::string fetch_google_photos_photo_id() const {
+    return fetch_google_photos_photo_id_;
+  }
   std::string get_fetch_daily_refresh_wallpaper_param() const {
     return fetch_daily_refresh_wallpaper_param_;
   }
@@ -97,6 +100,7 @@
  private:
   size_t open_count_ = 0;
   size_t fetch_images_for_collection_count_ = 0;
+  std::string fetch_google_photos_photo_id_;
   std::string fetch_daily_refresh_wallpaper_param_;
   bool fetch_daily_refresh_info_fails_ = false;
   std::unordered_map<AccountId, std::string> fake_files_ids_;
diff --git a/ash/wallpaper/wallpaper_controller_impl.cc b/ash/wallpaper/wallpaper_controller_impl.cc
index fba2fc3..2e46f6f 100644
--- a/ash/wallpaper/wallpaper_controller_impl.cc
+++ b/ash/wallpaper/wallpaper_controller_impl.cc
@@ -1107,7 +1107,7 @@
 void WallpaperControllerImpl::SetSeaPenWallpaper(
     const AccountId& account_id,
     const SeaPenImage& sea_pen_image,
-    const std::string& query_info,
+    const personalization_app::mojom::SeaPenQueryPtr& query,
     SetWallpaperCallback callback) {
   CHECK(features::IsSeaPenEnabled());
   DCHECK(callback);
@@ -1126,7 +1126,7 @@
       GetUserSeaPenWallpaperDir(account_id).Append(sea_pen_file_name);
 
   sea_pen_wallpaper_manager_.DecodeAndSaveSeaPenImage(
-      sea_pen_image, GetUserSeaPenWallpaperDir(account_id), query_info,
+      sea_pen_image, GetUserSeaPenWallpaperDir(account_id), query,
       base::BindOnce(&WallpaperControllerImpl::OnSeaPenWallpaperDecoded,
                      set_wallpaper_weak_factory_.GetWeakPtr(), account_id,
                      sea_pen_wallpaper_path, std::move(callback)));
@@ -2145,15 +2145,17 @@
 
   if (photo.is_null()) {
     // The photo doesn't exist, or has been deleted. If this photo is the
-    // current wallpaper, we need to reset to the default.
-    if (current_wallpaper_ &&
-        current_wallpaper_->wallpaper_info().location == params.id) {
+    // wallpaper for `params.account_id`, we need to reset to the default.
+    WallpaperInfo wallpaper_info;
+    if (GetUserWallpaperInfo(params.account_id, &wallpaper_info) &&
+        wallpaper_info.location == params.id) {
       sequenced_task_runner_->PostTask(
           FROM_HERE,
           base::BindOnce(&DeleteGooglePhotosCache, params.account_id));
       wallpaper_cache_map_.erase(params.account_id);
-      SetDefaultWallpaperImpl(GetUserType(params.account_id),
-                              /*show_wallpaper=*/true, base::DoNothing());
+      SetDefaultWallpaper(params.account_id,
+                          /*show_wallpaper=*/IsActiveUser(params.account_id),
+                          base::DoNothing());
       return;
     }
     wallpaper_metrics_manager_->LogWallpaperResult(
@@ -2191,7 +2193,8 @@
             SetWallpaperResult::kFileNotFound);
         // If the request succeeded, but no photos came back, then the album is
         // empty or deleted. Reset to default as a fallback.
-        SetDefaultWallpaper(params.account_id, /*show_wallpaper=*/true,
+        SetDefaultWallpaper(params.account_id,
+                            /*show_wallpaper=*/IsActiveUser(params.account_id),
                             base::DoNothing());
       } else {
         wallpaper_metrics_manager_->LogWallpaperResult(
diff --git a/ash/wallpaper/wallpaper_controller_impl.h b/ash/wallpaper/wallpaper_controller_impl.h
index 98a6eeae..357abac 100644
--- a/ash/wallpaper/wallpaper_controller_impl.h
+++ b/ash/wallpaper/wallpaper_controller_impl.h
@@ -34,6 +34,7 @@
 #include "ash/wallpaper/wallpaper_file_manager.h"
 #include "ash/wallpaper/wallpaper_time_of_day_scheduler.h"
 #include "ash/wallpaper/wallpaper_utils/wallpaper_calculated_colors.h"
+#include "ash/webui/common/mojom/sea_pen.mojom.h"
 #include "ash/webui/personalization_app/mojom/personalization_app.mojom-forward.h"
 #include "ash/wm/overview/overview_observer.h"
 #include "base/containers/flat_map.h"
@@ -297,10 +298,11 @@
                               const std::string& file_name,
                               WallpaperLayout layout,
                               const gfx::ImageSkia& image) override;
-  void SetSeaPenWallpaper(const AccountId& account_id,
-                          const SeaPenImage& sea_pen_image,
-                          const std::string& query_info,
-                          SetWallpaperCallback callback) override;
+  void SetSeaPenWallpaper(
+      const AccountId& account_id,
+      const SeaPenImage& sea_pen_image,
+      const personalization_app::mojom::SeaPenQueryPtr& query,
+      SetWallpaperCallback callback) override;
 
   void SetSeaPenWallpaperFromFile(const AccountId& account_id,
                                   const base::FilePath& file_path,
diff --git a/ash/wallpaper/wallpaper_controller_unittest.cc b/ash/wallpaper/wallpaper_controller_unittest.cc
index 0b082aa..79a6c283 100644
--- a/ash/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/wallpaper/wallpaper_controller_unittest.cc
@@ -852,7 +852,8 @@
     base::test::TestFuture<bool> set_wallpaper_future;
     controller_->SetSeaPenWallpaper(
         kAccountId1, {std::move(jpg_bytes), /*id=*/5},
-        /*query_info=*/"test query", set_wallpaper_future.GetCallback());
+        personalization_app::mojom::SeaPenQuery::NewTextQuery("search_query"),
+        set_wallpaper_future.GetCallback());
 
     EXPECT_TRUE(set_wallpaper_future.Take());
     EXPECT_EQ(1, observer.wallpaper_changed_count());
@@ -5479,6 +5480,33 @@
   EXPECT_EQ(controller_->GetWallpaperType(), WallpaperType::kDefault);
 }
 
+TEST_P(WallpaperControllerTest, HandleSyncDeletedGooglePhotosPhoto) {
+  WallpaperInfo local_info = InfoWithType(WallpaperType::kOnline);
+  local_info.date -= base::Days(2);
+  pref_manager_->SetUserWallpaperInfo(kAccountId1, local_info);
+
+  WallpaperInfo synced_info = InfoWithType(WallpaperType::kOnceGooglePhotos);
+  pref_manager_->SetSyncedWallpaperInfo(kAccountId1, synced_info);
+
+  // Just started and still loading wallpaper.
+  ASSERT_FALSE(controller_->HasShownAnyWallpaper());
+  ASSERT_THAT(client_.fetch_google_photos_photo_id(), testing::IsEmpty());
+  client_.set_google_photo_has_been_deleted(true);
+
+  SimulateUserLogin(kAccountId1);
+  EXPECT_EQ(synced_info.location, client_.fetch_google_photos_photo_id());
+  RunAllTasksUntilIdle();
+
+  WallpaperInfo final_local_info;
+  ASSERT_TRUE(
+      pref_manager_->GetLocalWallpaperInfo(kAccountId1, &final_local_info));
+
+  EXPECT_TRUE(final_local_info.MatchesAsset(local_info));
+  histogram_tester().ExpectUniqueSample(
+      "Ash.Wallpaper.OnceGooglePhotos.Result2",
+      SetWallpaperResult::kFileNotFound, 1);
+}
+
 TEST_P(WallpaperControllerTest, GooglePhotosAreCachedOnDisk) {
   SimulateUserLogin(kAccountId1);
 
diff --git a/ash/webui/common/mojom/BUILD.gn b/ash/webui/common/mojom/BUILD.gn
index 94b45f5..55c87e2 100644
--- a/ash/webui/common/mojom/BUILD.gn
+++ b/ash/webui/common/mojom/BUILD.gn
@@ -56,7 +56,6 @@
       traits_headers = [ "sea_pen_mojom_traits.h" ]
       traits_sources = [ "sea_pen_mojom_traits.cc" ]
       traits_public_deps = [
-        "//ash/public/cpp",
         "//ash/webui/personalization_app/proto",
         "//components/manta",
       ]
diff --git a/ash/webui/common/resources/bluetooth/bluetooth_utils.ts b/ash/webui/common/resources/bluetooth/bluetooth_utils.ts
index b371a3a..dfe2f7f0 100644
--- a/ash/webui/common/resources/bluetooth/bluetooth_utils.ts
+++ b/ash/webui/common/resources/bluetooth/bluetooth_utils.ts
@@ -7,16 +7,20 @@
 
 import {BatteryType} from './bluetooth_types.js';
 
-export function getDeviceName(device: PairedBluetoothDeviceProperties | null): string {
-  if (!device) {
+
+/**
+ *  WARNING: The returned string may contain malicious HTML and should not be
+ *  used for Polymer bindings in CSS code. For additional information see
+ *  b/298724102.
+ */
+export function getDeviceNameUnsafe(device: PairedBluetoothDeviceProperties|
+                                    null): string {
+  if (!device || (!device.nickname && !device.deviceProperties?.publicName)) {
     return '';
   }
 
-  if (device.nickname) {
-    return device.nickname;
-  }
-
-  return mojoString16ToString(device.deviceProperties.publicName);
+  return device.nickname ||
+      mojoString16ToString(device.deviceProperties.publicName);
 }
 
 /**
diff --git a/ash/webui/common/resources/sea_pen/sea_pen_recent_wallpapers_element.html b/ash/webui/common/resources/sea_pen/sea_pen_recent_wallpapers_element.html
index 9a9475d8..a53b59ad 100644
--- a/ash/webui/common/resources/sea_pen/sea_pen_recent_wallpapers_element.html
+++ b/ash/webui/common/resources/sea_pen/sea_pen_recent_wallpapers_element.html
@@ -144,12 +144,17 @@
         <template is="dom-if" if="[[shouldShowWallpaperInfoDialog_(index, currentShowWallpaperInfoDialog_)]]" restamp>
           <cr-dialog id="wallpaperInfoDialog" data-id$="[[index]]" on-close="onCloseDialog_" show-on-attach>
             <div slot="body">
-              <h2>Wallpaper Info</h2>
-              <p>[[getWallpaperInfoMessage_(image, recentImageData_, recentImageDataLoading_)]]</p>
+              <h2>[[i18n('seaPenAboutDialogTitle')]]</h2>
+              <p>
+                [[getWallpaperInfoPromptMessage_(image, recentImageData_, recentImageDataLoading_)]]
+              </p>
+              <p>
+                [[getWallpaperInfoDateMessage_(image, recentImageData_, recentImageDataLoading_)]]
+              </p>
             </div>
             <div slot="button-container">
               <cr-button id="wallpaperInfoCloseButton" class="action-button primary" on-click="onCloseDialog_">
-                Close
+                [[i18n('seaPenAboutDialogClose')]]
               </cr-button>
             </div>
           </cr-dialog>
diff --git a/ash/webui/common/resources/sea_pen/sea_pen_recent_wallpapers_element.ts b/ash/webui/common/resources/sea_pen/sea_pen_recent_wallpapers_element.ts
index 7d38c13..883870e7 100644
--- a/ash/webui/common/resources/sea_pen/sea_pen_recent_wallpapers_element.ts
+++ b/ash/webui/common/resources/sea_pen/sea_pen_recent_wallpapers_element.ts
@@ -162,17 +162,31 @@
     return data.url;
   }
 
-  private getWallpaperInfoMessage_(
+  private getWallpaperInfoPromptMessage_(
       recentImage: FilePath,
-      recentImageData: Record<FilePath['path'], RecentSeaPenData>,
+      _recentImageData: Record<FilePath['path'], RecentSeaPenData>,
       recentImageDataLoading: Record<FilePath['path'], boolean>): string|null {
     if (!recentImage ||
         this.isRecentImageLoading_(recentImage, recentImageDataLoading)) {
       return null;
     }
-    return recentImageData[recentImage.path].queryInfo;
+    // TODO(b/323597008): Replace with the actual prompt.
+    return this.i18n('seaPenAboutDialogPrompt', 'A radiant flower in bloom');
   }
 
+  private getWallpaperInfoDateMessage_(
+      recentImage: FilePath,
+      _recentImageData: Record<FilePath['path'], RecentSeaPenData>,
+      recentImageDataLoading: Record<FilePath['path'], boolean>): string|null {
+    if (!recentImage ||
+        this.isRecentImageLoading_(recentImage, recentImageDataLoading)) {
+      return null;
+    }
+    // TODO(b/323597008): Replace with the actual date.
+    return this.i18n('seaPenAboutDialogDate', 'Aug 25, 2023');
+  }
+
+
   private getAriaIndex_(i: number): number {
     return i + 1;
   }
diff --git a/ash/webui/common/sea_pen_resources.cc b/ash/webui/common/sea_pen_resources.cc
index fa19bd9..d968dc6 100644
--- a/ash/webui/common/sea_pen_resources.cc
+++ b/ash/webui/common/sea_pen_resources.cc
@@ -28,6 +28,10 @@
       {"seaPenDeleteWallpaper", IDS_SEA_PEN_DELETE_WALLPAPER},
       {"seaPenCreateMore", IDS_SEA_PEN_CREATE_MORE},
       {"seaPenAbout", IDS_SEA_PEN_ABOUT},
+      {"seaPenAboutDialogTitle", IDS_SEA_PEN_ABOUT_DIALOG_TITLE},
+      {"seaPenAboutDialogPrompt", IDS_SEA_PEN_ABOUT_DIALOG_PROMPT},
+      {"seaPenAboutDialogDate", IDS_SEA_PEN_ABOUT_DIALOG_DATE},
+      {"seaPenAboutDialogClose", IDS_SEA_PEN_ABOUT_DIALOG_CLOSE},
       {"seaPenErrorNoInternet", IDS_SEA_PEN_ERROR_NO_INTERNET},
       {"seaPenErrorResourceExhausted", IDS_SEA_PEN_ERROR_RESOURCE_EXHAUSTED},
       {"seaPenErrorGeneric", IDS_SEA_PEN_ERROR_GENERIC},
diff --git a/ash/webui/media_app_ui/BUILD.gn b/ash/webui/media_app_ui/BUILD.gn
index 05d9d61..8eca315 100644
--- a/ash/webui/media_app_ui/BUILD.gn
+++ b/ash/webui/media_app_ui/BUILD.gn
@@ -109,7 +109,7 @@
   root_dir = ts_root_dir
 
   in_files = media_app_converted_ts_sources + media_app_unconverted_js_sources +
-             [ "piex_loader.js" ]
+             [ "piex_loader.ts" ]
 
   # Add all Mojo TS as inputs to the TS compiler.
   target_outputs = get_target_outputs(":copy_mojo")
diff --git a/ash/webui/media_app_ui/resources/js/BUILD.gn b/ash/webui/media_app_ui/resources/js/BUILD.gn
index 49bbb85..d9e0cc2 100644
--- a/ash/webui/media_app_ui/resources/js/BUILD.gn
+++ b/ash/webui/media_app_ui/resources/js/BUILD.gn
@@ -18,10 +18,8 @@
 # JS bundling with rollup.
 # This is for files direct from the src/:  input_folder="./".
 copy("stage_static") {
-  sources = media_app_unconverted_js_sources + [
-              "//ash/webui/system_apps/public/js/sandboxed_load_time_data.js",
-              "//ui/file_manager/image_loader/piex_loader.js",
-            ]
+  sources = media_app_unconverted_js_sources +
+            [ "//ash/webui/system_apps/public/js/sandboxed_load_time_data.js" ]
   outputs = [ stage_folder + "/{{source_file_part}}" ]
 }
 
@@ -45,7 +43,8 @@
 # Takes the JS emitted by the `ts_library` targets and places it with other JS
 # files in the staging folder for `rollup` to consume.
 copy("stage_compiled_ts") {
-  sources = media_app_compiled_sources
+  sources = media_app_compiled_sources +
+            [ "$root_gen_dir/ash/webui/media_app_ui/tsc/piex_loader.js" ]
   outputs = [ stage_folder + "/{{source_file_part}}" ]
   deps = [ "//ash/webui/media_app_ui:build_ts" ]
 }
@@ -58,7 +57,7 @@
   # dependencies.
   sources = media_app_converted_ts_sources + media_app_static_defs +
             media_app_unconverted_js_sources +
-            [ "//ui/file_manager/image_loader/piex_loader.js" ]
+            [ "//ui/file_manager/image_loader/piex_loader.ts" ]
   outputs = [ ts_root_dir + "/{{source_file_part}}" ]
 }
 
diff --git a/ash/wm/desks/desk_button/desk_button.cc b/ash/wm/desks/desk_button/desk_button.cc
index 7807bb6..2aaa7eb 100644
--- a/ash/wm/desks/desk_button/desk_button.cc
+++ b/ash/wm/desks/desk_button/desk_button.cc
@@ -186,15 +186,6 @@
   views::Button::OnGestureEvent(event);
 }
 
-void DeskButton::StateChanged(ButtonState old_state) {
-  if (GetState() != ButtonState::STATE_NORMAL &&
-      GetState() != ButtonState::STATE_HOVERED) {
-    return;
-  }
-
-  UpdateShelfAutoHideDisabler(disable_shelf_auto_hide_hover_, !GetHovered());
-}
-
 void DeskButton::AboutToRequestFocusFromTabTraversal(bool reverse) {
   desk_button_container_->desk_button_widget()->MaybeFocusOut(reverse);
 }
@@ -244,10 +235,6 @@
       desk_button_container_->shelf()->hotseat_widget()->GetShelfView());
 }
 
-bool DeskButton::GetHovered() const {
-  return GetState() == ButtonState::STATE_HOVERED;
-}
-
 void DeskButton::SetActivation(bool is_activated) {
   if (is_activated_ == is_activated) {
     return;
diff --git a/ash/wm/desks/desk_button/desk_button.h b/ash/wm/desks/desk_button/desk_button.h
index e86492c..f84f2a6 100644
--- a/ash/wm/desks/desk_button/desk_button.h
+++ b/ash/wm/desks/desk_button/desk_button.h
@@ -51,15 +51,12 @@
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   void OnMouseEvent(ui::MouseEvent* event) override;
   void OnGestureEvent(ui::GestureEvent* event) override;
-  void StateChanged(ButtonState old_state) override;
   void AboutToRequestFocusFromTabTraversal(bool reverse) override;
 
   // Initializes the view. Must be called before any meaningful UIs can be laid
   // out.
   void Init(DeskButtonContainer* desk_button_container);
 
-  bool GetHovered() const;
-
   void SetActivation(bool is_activated);
 
   std::u16string GetTitle() const;
@@ -115,11 +112,9 @@
   // button has been pressed).
   bool is_activated_ = false;
 
-  // Used to suspend the shelf from auto-hiding when the button is activated or
-  // hovered.
+  // Used to suspend the shelf from auto-hiding when the button is activated.
   std::optional<Shelf::ScopedDisableAutoHide>
       disable_shelf_auto_hide_activation_;
-  std::optional<Shelf::ScopedDisableAutoHide> disable_shelf_auto_hide_hover_;
 };
 
 BEGIN_VIEW_BUILDER(VIEWS_EXPORT, DeskButton, views::Button)
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index b139bad..e425e8e 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -11208,37 +11208,16 @@
   ClickDeskButton();
   UnhoverDeskButton();
   EXPECT_TRUE(desk_button->is_activated());
-  EXPECT_FALSE(desk_button->GetHovered());
+  EXPECT_NE(desk_button->GetState(), views::Button::ButtonState::STATE_HOVERED);
   EXPECT_TRUE(shelf->disable_auto_hide());
 
   ClickDeskButton();
   UnhoverDeskButton();
   EXPECT_FALSE(desk_button->is_activated());
-  EXPECT_FALSE(desk_button->GetHovered());
+  EXPECT_NE(desk_button->GetState(), views::Button::ButtonState::STATE_HOVERED);
   ASSERT_FALSE(shelf->disable_auto_hide());
 }
 
-// Tests that when the desk button is hovered, shelf auto-hide should be
-// suspended.
-TEST_P(DeskButtonTest, SuspendShelfAutoHideWhenHovered) {
-  auto* shelf = GetPrimaryShelf();
-  shelf->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlways);
-  ASSERT_FALSE(shelf->disable_auto_hide());
-
-  auto* desk_button = GetDeskButton();
-  ASSERT_TRUE(desk_button);
-
-  HoverDeskButton();
-  EXPECT_FALSE(desk_button->is_activated());
-  EXPECT_TRUE(desk_button->GetHovered());
-  EXPECT_TRUE(shelf->disable_auto_hide());
-
-  UnhoverDeskButton();
-  EXPECT_FALSE(desk_button->is_activated());
-  EXPECT_FALSE(desk_button->GetHovered());
-  EXPECT_FALSE(shelf->disable_auto_hide());
-}
-
 // Tests that the desk button and its child components are correctly positioned
 // in each phase of the desk button's desk switch process.
 TEST_P(DeskButtonTest, ValidateDeskButtonPosition) {
diff --git a/ash/wm/gestures/back_gesture/back_gesture_contextual_nudge_controller_impl_unittest.cc b/ash/wm/gestures/back_gesture/back_gesture_contextual_nudge_controller_impl_unittest.cc
index 21017396..4c9b056 100644
--- a/ash/wm/gestures/back_gesture/back_gesture_contextual_nudge_controller_impl_unittest.cc
+++ b/ash/wm/gestures/back_gesture/back_gesture_contextual_nudge_controller_impl_unittest.cc
@@ -39,17 +39,15 @@
  public:
   explicit BackGestureContextualNudgeControllerTest(bool can_go_back = true)
       : can_go_back_(can_go_back) {
-    scoped_feature_list_.InitWithFeatures(
-        {features::kContextualNudges, features::kHideShelfControlsInTabletMode},
-        {});
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kHideShelfControlsInTabletMode);
   }
   BackGestureContextualNudgeControllerTest(
       base::test::TaskEnvironment::TimeSource time,
       bool can_go_back = true)
       : NoSessionAshTestBase(time), can_go_back_(can_go_back) {
-    scoped_feature_list_.InitWithFeatures(
-        {features::kContextualNudges, features::kHideShelfControlsInTabletMode},
-        {});
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kHideShelfControlsInTabletMode);
   }
   ~BackGestureContextualNudgeControllerTest() override = default;
 
diff --git a/ash/wm/gestures/back_gesture/back_gesture_event_handler.cc b/ash/wm/gestures/back_gesture/back_gesture_event_handler.cc
index fed3874..6d4b701 100644
--- a/ash/wm/gestures/back_gesture/back_gesture_event_handler.cc
+++ b/ash/wm/gestures/back_gesture/back_gesture_event_handler.cc
@@ -180,7 +180,7 @@
 }  // namespace
 
 BackGestureEventHandler::BackGestureEventHandler() {
-  if (features::AreContextualNudgesEnabled()) {
+  if (features::IsHideShelfControlsInTabletModeEnabled()) {
     nudge_controller_ =
         std::make_unique<BackGestureContextualNudgeControllerImpl>();
   }
@@ -320,7 +320,7 @@
         break;
       back_gesture_affordance_ = std::make_unique<BackGestureAffordance>(
           screen_location, dragged_from_splitview_divider_);
-      if (features::AreContextualNudgesEnabled()) {
+      if (features::IsHideShelfControlsInTabletModeEnabled()) {
         // Cancel the in-waiting or in-progress back nudge animation.
         nudge_controller_->OnBackGestureStarted();
         contextual_tooltip::HandleGesturePerformed(
diff --git a/ash/wm/snap_group/snap_group_unittest.cc b/ash/wm/snap_group/snap_group_unittest.cc
index 59b8af0..2a50999 100644
--- a/ash/wm/snap_group/snap_group_unittest.cc
+++ b/ash/wm/snap_group/snap_group_unittest.cc
@@ -132,13 +132,14 @@
 void SnapOneTestWindow(
     aura::Window* window,
     chromeos::WindowStateType state_type,
+    float snap_ratio,
     WindowSnapActionSource snap_action_source = WindowSnapActionSource::kTest) {
   WindowState* window_state = WindowState::Get(window);
   const WindowSnapWMEvent snap_event(
       state_type == chromeos::WindowStateType::kPrimarySnapped
           ? WM_EVENT_SNAP_PRIMARY
           : WM_EVENT_SNAP_SECONDARY,
-      snap_action_source);
+      snap_ratio, snap_action_source);
   window_state->OnWMEvent(&snap_event);
   EXPECT_EQ(state_type, window_state->GetStateType());
 }
@@ -280,7 +281,8 @@
 
   // Snap a window. Test we don't start overview.
   std::unique_ptr<aura::Window> w1(CreateAppWindow());
-  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
 }
 
@@ -289,7 +291,8 @@
   // overview while `w2` is in overview.
   std::unique_ptr<aura::Window> w1(CreateAppWindow());
   std::unique_ptr<aura::Window> w2(CreateAppWindow());
-  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   VerifySplitViewOverviewSession(w1.get());
   auto* overview_controller = Shell::Get()->overview_controller();
   EXPECT_TRUE(
@@ -309,12 +312,14 @@
 
   // Create a new `w3` and snap it to the left. Test it doesn't start overview.
   std::unique_ptr<aura::Window> w3(CreateTestWindow());
-  SnapOneTestWindow(w3.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(w3.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   EXPECT_FALSE(overview_controller->InOverviewSession());
 
   // Create a new `w4` and snap it to the right. Test it doesn't start overview.
   std::unique_ptr<aura::Window> w4(CreateTestWindow());
-  SnapOneTestWindow(w4.get(), chromeos::WindowStateType::kSecondarySnapped);
+  SnapOneTestWindow(w4.get(), chromeos::WindowStateType::kSecondarySnapped,
+                    chromeos::kDefaultSnapRatio);
   EXPECT_FALSE(overview_controller->InOverviewSession());
   EXPECT_EQ(chromeos::WindowStateType::kSecondarySnapped,
             WindowState::Get(w4.get())->GetStateType());
@@ -362,6 +367,7 @@
 
   for (const auto test_case : kTestCases) {
     SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kSecondarySnapped,
+                      chromeos::kDefaultSnapRatio,
                       test_case.snap_action_source);
     EXPECT_EQ(test_case.should_show_partial_overview, IsInOverviewSession());
     MaximizeToClearTheSession(w1.get());
@@ -386,7 +392,8 @@
 
 TEST_F(FasterSplitScreenTest, EndSplitViewOverviewSession) {
   std::unique_ptr<aura::Window> w1(CreateAppWindow());
-  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kSecondarySnapped);
+  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kSecondarySnapped,
+                    chromeos::kDefaultSnapRatio);
   VerifySplitViewOverviewSession(w1.get());
 
   // Drag `w1` out of split view. Test it ends overview.
@@ -399,14 +406,16 @@
   EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
 
   // Snap then minimize the window. Test it ends overview.
-  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   VerifySplitViewOverviewSession(w1.get());
   WMEvent minimize_event(WM_EVENT_MINIMIZE);
   WindowState::Get(w1.get())->OnWMEvent(&minimize_event);
   EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
 
   // Snap then close the window. Test it ends overview.
-  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   VerifySplitViewOverviewSession(w1.get());
   w1.reset();
   EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
@@ -414,7 +423,8 @@
 
 TEST_F(FasterSplitScreenTest, ResizeSplitViewOverviewAndWindow) {
   std::unique_ptr<aura::Window> w1(CreateAppWindow());
-  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   VerifySplitViewOverviewSession(w1.get());
   const gfx::Rect initial_bounds(w1->GetBoundsInScreen());
 
@@ -444,7 +454,8 @@
 
 TEST_F(FasterSplitScreenTest, ResizeAndAutoSnap) {
   std::unique_ptr<aura::Window> w1(CreateTestWindow());
-  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   const gfx::Rect initial_bounds(w1->GetBoundsInScreen());
   ASSERT_TRUE(OverviewController::Get()->InOverviewSession());
 
@@ -509,7 +520,8 @@
   std::unique_ptr<aura::Window> w1(CreateAppWindow());
   std::unique_ptr<aura::Window> w2(CreateAppWindow());
 
-  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   VerifySplitViewOverviewSession(w1.get());
   ASSERT_EQ(1u, GetOverviewSession()->grid_list().size());
 
@@ -530,7 +542,8 @@
 
   // Verify that tapping on an empty area in overview will exit the paring.
   MaximizeToClearTheSession(w1.get());
-  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   VerifySplitViewOverviewSession(w1.get());
   event_generator->MoveTouch(outside_point);
   event_generator->PressTouch();
@@ -552,7 +565,8 @@
   auto* event_generator = GetEventGenerator();
 
   // Snap `w1`. Test that moving the mouse around won't end overview
-  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   VerifySplitViewOverviewSession(w1.get());
   event_generator->MoveMouseTo(w1->GetBoundsInScreen().CenterPoint());
   EXPECT_TRUE(IsInOverviewSession());
@@ -571,7 +585,8 @@
   };
 
   for (const auto& test_case : kTestCases) {
-    SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped);
+    SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                      chromeos::kDefaultSnapRatio);
     VerifySplitViewOverviewSession(w1.get());
     delegate.set_window_component(test_case.window_component);
     if (test_case.is_click_event) {
@@ -589,7 +604,8 @@
   std::unique_ptr<aura::Window> w1(CreateTestWindow());
   std::unique_ptr<aura::Window> w2(CreateTestWindow());
 
-  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   VerifySplitViewOverviewSession(w1.get());
   ASSERT_EQ(1u, GetOverviewSession()->grid_list().size());
 
@@ -601,7 +617,8 @@
             chromeos::WindowStateType::kPrimarySnapped);
 
   // Test that Alt + Tab exits overview.
-  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   VerifySplitViewOverviewSession(w1.get());
   PressAndReleaseKey(ui::VKEY_TAB, ui::EF_ALT_DOWN);
   EXPECT_FALSE(overview_controller->InOverviewSession());
@@ -613,7 +630,8 @@
 TEST_F(FasterSplitScreenTest, SkipPairingToast) {
   std::unique_ptr<aura::Window> w1(CreateAppWindow());
   std::unique_ptr<aura::Window> w2(CreateAppWindow());
-  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   VerifySplitViewOverviewSession(w1.get());
 
   auto* overview_grid = GetOverviewGridForRoot(w1->GetRootWindow());
@@ -632,7 +650,8 @@
 TEST_F(FasterSplitScreenTest, DontStartPartialOverviewAfterSkippingPairing) {
   std::unique_ptr<aura::Window> w1(CreateAppWindow());
   std::unique_ptr<aura::Window> w2(CreateAppWindow());
-  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   VerifySplitViewOverviewSession(w1.get());
 
   // Press Esc key to skip pairing.
@@ -642,7 +661,8 @@
 
   // Snap `w2`. Since `w1` is snapped to primary, it doesn't start partial
   // overview. wm::ActivateWindow(w2.get());
-  SnapOneTestWindow(w2.get(), chromeos::WindowStateType::kSecondarySnapped);
+  SnapOneTestWindow(w2.get(), chromeos::WindowStateType::kSecondarySnapped,
+                    chromeos::kDefaultSnapRatio);
   EXPECT_FALSE(overview_controller->InOverviewSession());
   EXPECT_EQ(WindowState::Get(w1.get())->GetStateType(),
             chromeos::WindowStateType::kPrimarySnapped);
@@ -653,7 +673,8 @@
 TEST_F(FasterSplitScreenTest, DontStartPartialOverviewAfterClosingWindow) {
   std::unique_ptr<aura::Window> w1(CreateAppWindow());
   std::unique_ptr<aura::Window> w2(CreateAppWindow());
-  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   VerifySplitViewOverviewSession(w1.get());
 
   // Select `w2` to auto-snap it.
@@ -668,14 +689,16 @@
   // overview.
   w2.reset();
   std::unique_ptr<aura::Window> w3(CreateAppWindow());
-  SnapOneTestWindow(w3.get(), chromeos::WindowStateType::kSecondarySnapped);
+  SnapOneTestWindow(w3.get(), chromeos::WindowStateType::kSecondarySnapped,
+                    chromeos::kDefaultSnapRatio);
   EXPECT_FALSE(OverviewController::Get()->InOverviewSession());
 }
 
 TEST_F(FasterSplitScreenTest, StartPartialOverviewForMinimizedWindow) {
   std::unique_ptr<aura::Window> w1(CreateAppWindow());
   std::unique_ptr<aura::Window> w2(CreateAppWindow());
-  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   VerifySplitViewOverviewSession(w1.get());
 
   // Now minimize `w1`, so visually there is no primary snapped window.
@@ -683,7 +706,8 @@
 
   // Now snap `w2` to secondary. Since `w1` is minimized, it starts partial
   // overview.
-  SnapOneTestWindow(w2.get(), chromeos::WindowStateType::kSecondarySnapped);
+  SnapOneTestWindow(w2.get(), chromeos::WindowStateType::kSecondarySnapped,
+                    chromeos::kDefaultSnapRatio);
   VerifySplitViewOverviewSession(w2.get());
 }
 
@@ -694,11 +718,13 @@
   UpdateDisplay("800x600");
   std::unique_ptr<aura::Window> w1(CreateAppWindow());
   SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio,
                     WindowSnapActionSource::kKeyboardShortcutToSnap);
   ASSERT_TRUE(WindowState::Get(w1.get())->IsSnapped());
 
   std::unique_ptr<aura::Window> w2(CreateAppWindow());
   SnapOneTestWindow(w2.get(), chromeos::WindowStateType::kSecondarySnapped,
+                    chromeos::kDefaultSnapRatio,
                     WindowSnapActionSource::kDragWindowToEdgeToSnap);
   EXPECT_FALSE(IsInOverviewSession());
 
@@ -710,13 +736,15 @@
   // Snap 2 test windows in place.
   std::unique_ptr<aura::Window> w1(CreateAppWindow());
   std::unique_ptr<aura::Window> w2(CreateAppWindow());
-  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   VerifySplitViewOverviewSession(w1.get());
 
   // To simulate the CUJ when a user selects a window from overview, activate
   // and snap `w2`.
   wm::ActivateWindow(w2.get());
-  SnapOneTestWindow(w2.get(), chromeos::WindowStateType::kSecondarySnapped);
+  SnapOneTestWindow(w2.get(), chromeos::WindowStateType::kSecondarySnapped,
+                    chromeos::kDefaultSnapRatio);
   EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
 
   // Create a 3rd floated window on top of `w2`.
@@ -729,7 +757,8 @@
   // Open a 4th window and snap it on top of `w1`. Test we don't start partial
   // overview.
   std::unique_ptr<aura::Window> w3(CreateAppWindow());
-  SnapOneTestWindow(w3.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(w3.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
 }
 
@@ -758,6 +787,7 @@
   // screen to get window layout setup ready.
   std::unique_ptr<aura::Window> w2 = CreateAppWindow();
   SnapOneTestWindow(w2.get(), chromeos::WindowStateType::kSecondarySnapped,
+                    chromeos::kDefaultSnapRatio,
                     WindowSnapActionSource::kKeyboardShortcutToSnap);
   ASSERT_TRUE(w2->IsVisible());
 
@@ -771,6 +801,7 @@
   // the faster split screen setup.
   std::unique_ptr<aura::Window> w1 = CreateAppWindow();
   SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio,
                     WindowSnapActionSource::kSnapByWindowLayoutMenu);
   ASSERT_TRUE(w1->IsVisible());
   VerifySplitViewOverviewSession(w1.get());
@@ -780,6 +811,7 @@
   // the next round of testing. `w2` is fully visible now.
   wm::ActivateWindow(w2.get());
   SnapOneTestWindow(w2.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio,
                     WindowSnapActionSource::kKeyboardShortcutToSnap);
   EXPECT_FALSE(IsInOverviewSession());
 
@@ -787,6 +819,7 @@
   // trigger faster split screen setup, with `w1` occupying the primary snapped
   // position, partial overview shouldn't start.
   SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kSecondarySnapped,
+                    chromeos::kDefaultSnapRatio,
                     WindowSnapActionSource::kSnapByWindowLayoutMenu);
   EXPECT_FALSE(IsInOverviewSession());
 }
@@ -816,6 +849,7 @@
   // screen to get window layout setup ready.
   std::unique_ptr<aura::Window> w2 = CreateAppWindow();
   SnapOneTestWindow(w2.get(), chromeos::WindowStateType::kSecondarySnapped,
+                    chromeos::kDefaultSnapRatio,
                     WindowSnapActionSource::kKeyboardShortcutToSnap);
   ASSERT_TRUE(w2->IsVisible());
 
@@ -829,6 +863,7 @@
   // the faster split screen setup.
   std::unique_ptr<aura::Window> w1 = CreateAppWindow();
   SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio,
                     WindowSnapActionSource::kSnapByWindowLayoutMenu);
   ASSERT_TRUE(w1->IsVisible());
   VerifySplitViewOverviewSession(w1.get());
@@ -838,6 +873,7 @@
   // the next round of testing. `w2` is fully visible now.
   wm::ActivateWindow(w2.get());
   SnapOneTestWindow(w2.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio,
                     WindowSnapActionSource::kKeyboardShortcutToSnap);
   EXPECT_FALSE(IsInOverviewSession());
 
@@ -845,18 +881,20 @@
   // trigger faster split screen setup, with `w1` occupying the primary snapped
   // position, partial overview shouldn't start.
   SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kSecondarySnapped,
+                    chromeos::kDefaultSnapRatio,
                     WindowSnapActionSource::kSnapByWindowLayoutMenu);
   EXPECT_FALSE(IsInOverviewSession());
 }
 
-TEST_F(FasterSplitScreenTest, MultiDisplay) {
+TEST_F(FasterSplitScreenTest, OnCrashOnDisplayChange) {
   UpdateDisplay("800x600,1000x600");
   display::test::DisplayManagerTestApi display_manager_test(display_manager());
 
   // Snap `window` on the second display. Test its bounds are updated.
   std::unique_ptr<aura::Window> window(
       CreateTestWindowInShellWithBounds(gfx::Rect(900, 0, 100, 100)));
-  SnapOneTestWindow(window.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(window.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   ASSERT_EQ(
       display_manager_test.GetSecondaryDisplay().id(),
       display::Screen::GetScreen()->GetDisplayNearestWindow(window.get()).id());
@@ -871,11 +909,79 @@
   base::RunLoop().RunUntilIdle();
 }
 
+// Tests that the snapped window bounds will be refreshed on display changes to
+// preserve the snap ratio.
+TEST_F(FasterSplitScreenTest, WindowBoundsRefreshedOnDisplayChanges) {
+  UpdateDisplay("900x600");
+  std::unique_ptr<aura::Window> window(CreateAppWindow());
+  SnapOneTestWindow(window.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kTwoThirdSnapRatio,
+                    WindowSnapActionSource::kSnapByWindowLayoutMenu);
+  VerifySplitViewOverviewSession(window.get());
+  ASSERT_EQ(WindowState::Get(window.get())->snap_ratio(),
+            chromeos::kTwoThirdSnapRatio);
+  const auto work_area_bounds_1 = work_area_bounds();
+  ASSERT_EQ(
+      window->GetBoundsInScreen(),
+      gfx::Rect(0, 0, work_area_bounds_1.width() * chromeos::kTwoThirdSnapRatio,
+                work_area_bounds_1.height()));
+
+  UpdateDisplay("1200x600");
+  VerifySplitViewOverviewSession(window.get());
+  EXPECT_EQ(WindowState::Get(window.get())->snap_ratio(),
+            chromeos::kTwoThirdSnapRatio);
+  const auto work_area_bounds_2 = work_area_bounds();
+  EXPECT_EQ(
+      window->GetBoundsInScreen(),
+      gfx::Rect(0, 0, work_area_bounds_2.width() * chromeos::kTwoThirdSnapRatio,
+                work_area_bounds_2.height()));
+}
+
+// Test to verify that there will be no crash when dragging the snapped window
+// out without resizing the window see crash in b/321111182.
+TEST_F(FasterSplitScreenTest, NoCrashWhenDraggingTheSnappedWindow) {
+  std::unique_ptr<aura::Window> window(CreateAppWindow());
+  SnapOneTestWindow(window.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kTwoThirdSnapRatio,
+                    WindowSnapActionSource::kSnapByWindowLayoutMenu);
+  VerifySplitViewOverviewSession(window.get());
+
+  auto* event_generator = GetEventGenerator();
+  event_generator->set_current_screen_location(
+      window->GetBoundsInScreen().CenterPoint());
+  event_generator->PressLeftButton();
+  event_generator->DragMouseBy(50, 50);
+  event_generator->ReleaseLeftButton();
+}
+
+// Verifies the issue to snap a window in overview is working properly. see
+// b/322893408.
+TEST_F(FasterSplitScreenTest, EnterOverviewSnappingWindow) {
+  std::unique_ptr<aura::Window> window1(
+      CreateAppWindow(gfx::Rect(20, 20, 200, 100)));
+  std::unique_ptr<aura::Window> windo2(
+      CreateAppWindow(gfx::Rect(10, 10, 200, 100)));
+
+  OverviewController* overview_controller = OverviewController::Get();
+  overview_controller->StartOverview(OverviewStartAction::kOverviewButton);
+  ASSERT_TRUE(IsInOverviewSession());
+
+  auto* overview_item = GetOverviewItemForWindow(window1.get());
+  auto* event_generator = GetEventGenerator();
+  event_generator->MoveMouseTo(
+      gfx::ToRoundedPoint(overview_item->target_bounds().CenterPoint()));
+  event_generator->PressLeftButton();
+  event_generator->DragMouseTo(0, 0);
+  event_generator->ReleaseLeftButton();
+  EXPECT_TRUE(IsInOverviewSession());
+}
+
 // Verifies that there will be no crash when transitioning the
 // `SplitViewOverviewSession` between clamshell and tablet mode.
 TEST_F(FasterSplitScreenTest, ClamshellTabletTransitionOneSnappedWindow) {
   std::unique_ptr<aura::Window> w1(CreateTestWindow());
-  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   VerifySplitViewOverviewSession(w1.get());
 
   SwitchToTabletMode();
@@ -890,7 +996,8 @@
 TEST_F(FasterSplitScreenTest, ClamshellTabletTransitionTwoSnappedWindows) {
   std::unique_ptr<aura::Window> w1(CreateTestWindow());
   std::unique_ptr<aura::Window> w2(CreateTestWindow());
-  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   // Select the second window from overview to snap it.
   auto* event_generator = GetEventGenerator();
   event_generator->MoveMouseTo(
@@ -919,7 +1026,8 @@
   ui::ScopedAnimationDurationScaleMode test_duration_mode(
       ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
   std::unique_ptr<aura::Window> w1(CreateAppWindow());
-  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   SwitchToTabletMode();
   EXPECT_TRUE(split_view_divider()->divider_widget());
 
@@ -957,6 +1065,7 @@
   // Set up the splitview overview session and select a window in the partial
   // overview to complete the window layout.
   SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio,
                     WindowSnapActionSource::kDragWindowToEdgeToSnap);
   VerifySplitViewOverviewSession(w1.get());
 
@@ -981,6 +1090,7 @@
   // Set up the splitview overview session and click an empty area to skip the
   // pairing.
   SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio,
                     WindowSnapActionSource::kDragWindowToEdgeToSnap);
   VerifySplitViewOverviewSession(w1.get());
   item2 = GetOverviewItemForWindow(w2.get());
@@ -1004,6 +1114,7 @@
   // Set up the splitview overview session, create a 3rd window to be
   // auto-snapped and complete the window layout.
   SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio,
                     WindowSnapActionSource::kDragWindowToEdgeToSnap);
   VerifySplitViewOverviewSession(w1.get());
   std::unique_ptr<aura::Window> w3(CreateAppWindow());
@@ -1022,6 +1133,7 @@
 
   // Set up the splitview overview session and press escape key to skip pairing.
   SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio,
                     WindowSnapActionSource::kDragWindowToEdgeToSnap);
   VerifySplitViewOverviewSession(w1.get());
   event_generator->PressAndReleaseKey(ui::VKEY_ESCAPE);
@@ -1040,6 +1152,7 @@
   // Set up the splitview overview session and close the snapped window to exit
   // the session.
   SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio,
                     WindowSnapActionSource::kDragWindowToEdgeToSnap);
   VerifySplitViewOverviewSession(w1.get());
   w1.reset();
@@ -1196,7 +1309,8 @@
       OverviewStartAction::kFasterSplitScreenSetup,
       /*expected_count=*/0);
   std::unique_ptr<aura::Window> window(CreateAppWindow());
-  SnapOneTestWindow(window.get(), chromeos::WindowStateType::kPrimarySnapped);
+  SnapOneTestWindow(window.get(), chromeos::WindowStateType::kPrimarySnapped,
+                    chromeos::kDefaultSnapRatio);
   VerifySplitViewOverviewSession(window.get());
   histogram_tester_.ExpectBucketCount(
       kOverviewStartActionHistogram,
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 46e743a..ced73d45 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -1225,8 +1225,6 @@
       "android/thread_instruction_count.h",
       "android/timezone_utils.cc",
       "android/timezone_utils.h",
-      "android/token_android.cc",
-      "android/token_android.h",
       "android/unguessable_token_android.cc",
       "android/unguessable_token_android.h",
       "debug/stack_trace_android.cc",
@@ -1335,6 +1333,8 @@
       "android/library_loader/library_loader_hooks.h",
       "android/native_uma_recorder.cc",
       "android/scoped_java_ref.h",
+      "android/token_android.cc",
+      "android/token_android.h",
       "android/trace_event_binding.cc",
       "android/trace_event_binding.h",
     ]
@@ -4301,6 +4301,18 @@
   generate_jni("base_shared_preferences_jni") {
     sources = [ "android/java/src/org/chromium/base/shared_preferences/SharedPreferencesManager.java" ]
   }
+
+  shared_library_with_jni("libbase_junit_tests") {
+    testonly = true
+    enable_target = is_robolectric
+    sources = [ "android/robolectric_jni_onload.cc" ]
+    deps = [ ":base($robolectric_toolchain)" ]
+    java_targets = [ ":base_junit_tests" ]
+    visibility = [ ":*" ]
+
+    # Make jni.h available.
+    configs += [ "//third_party/jdk" ]
+  }
 }  # is_android || is_robolectric
 
 if (is_android) {
@@ -4619,7 +4631,6 @@
       "android/javatests/src/org/chromium/base/ObserverListTest.java",
       "android/javatests/src/org/chromium/base/PackageUtilsTest.java",
       "android/javatests/src/org/chromium/base/StrictModeContextTest.java",
-      "android/javatests/src/org/chromium/base/TokenTest.java",
       "android/javatests/src/org/chromium/base/UserDataHostTest.java",
       "android/javatests/src/org/chromium/base/jank_tracker/JankTrackerTest.java",
       "android/javatests/src/org/chromium/base/library_loader/EarlyNativeTest.java",
@@ -4852,6 +4863,7 @@
   }
 
   robolectric_binary("base_junit_tests") {
+    shared_libraries = [ ":libbase_junit_tests($robolectric_toolchain)" ]
     sources = [
       "android/junit/src/org/chromium/base/ApplicationStatusTest.java",
       "android/junit/src/org/chromium/base/BuildInfoTest.java",
diff --git a/base/allocator/partition_allocator/src/partition_alloc/BUILD.gn b/base/allocator/partition_allocator/src/partition_alloc/BUILD.gn
index 547d96a..b70fbcd 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/BUILD.gn
+++ b/base/allocator/partition_allocator/src/partition_alloc/BUILD.gn
@@ -337,7 +337,7 @@
       # This is already set when we compile libc++, see
       # buildtools/third_party/libc++/BUILD.gn. But it needs to be set here as
       # well, since the shim defines the symbols, to prevent them being exported.
-      cflags = [ "-fvisibility-global-new-delete=force-hidden" ]
+      cflags = [ "-fvisibility-global-new-delete-hidden" ]
     }
   }
 
diff --git a/base/android/java/src/org/chromium/base/Token.java b/base/android/java/src/org/chromium/base/Token.java
index 423fb1f..a8aa63a 100644
--- a/base/android/java/src/org/chromium/base/Token.java
+++ b/base/android/java/src/org/chromium/base/Token.java
@@ -33,6 +33,7 @@
      * @param high The high portion of the token.
      * @param low The low portion of the token.
      */
+    @CalledByNative
     public Token(long high, long low) {
         mHigh = high;
         mLow = low;
@@ -73,12 +74,6 @@
         return other.mHigh == mHigh && other.mLow == mLow;
     }
 
-    /** Wrapper around {@link Token} constructor for native. */
-    @CalledByNative
-    private static Token create(long high, long low) {
-        return new Token(high, low);
-    }
-
     @NativeMethods
     interface Natives {
         Token createRandom();
diff --git a/base/android/javatests/src/org/chromium/base/TokenTest.java b/base/android/javatests/src/org/chromium/base/TokenTest.java
deleted file mode 100644
index c41a72da..0000000
--- a/base/android/javatests/src/org/chromium/base/TokenTest.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.base;
-
-import static org.junit.Assert.assertFalse;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.library_loader.LibraryLoader;
-import org.chromium.base.test.BaseJUnit4ClassRunner;
-import org.chromium.base.test.util.Batch;
-
-/** Native tests for {@link Token}. This is inspired by GURLJavaTests. */
-@RunWith(BaseJUnit4ClassRunner.class)
-@Batch(Batch.UNIT_TESTS)
-public class TokenTest {
-    @Before
-    public void setUp() {
-        ThreadUtils.runOnUiThreadBlocking(
-                () -> {
-                    LibraryLoader.getInstance().ensureInitialized();
-                });
-    }
-
-    @SmallTest
-    @Test
-    public void testCreateRandom() {
-        Token token = Token.createRandom();
-        assertFalse(token.isZero());
-    }
-}
diff --git a/base/android/junit/src/org/chromium/base/TokenUnitTest.java b/base/android/junit/src/org/chromium/base/TokenUnitTest.java
index aa91703..53d65a38 100644
--- a/base/android/junit/src/org/chromium/base/TokenUnitTest.java
+++ b/base/android/junit/src/org/chromium/base/TokenUnitTest.java
@@ -57,6 +57,12 @@
                 new Token(0xfffffffffffffffdL, 0xfffffffffffffffeL).toString());
     }
 
+    @Test
+    public void testCreateRandom() {
+        Token token = Token.createRandom();
+        assertFalse(token.isZero());
+    }
+
     private void checkEquals(Token first, Token second) {
         assertEquals(first, second);
         assertEquals(first.hashCode(), second.hashCode());
diff --git a/base/android/robolectric_jni_onload.cc b/base/android/robolectric_jni_onload.cc
new file mode 100644
index 0000000..ba537e63
--- /dev/null
+++ b/base/android/robolectric_jni_onload.cc
@@ -0,0 +1,13 @@
+// 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 <jni.h>
+
+#include "base/android/base_jni_onload.h"
+#include "base/android/jni_android.h"
+
+extern "C" JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+  base::android::InitVM(vm);
+  base::android::OnJNIOnLoadInit();
+  return JNI_VERSION_1_4;
+}
diff --git a/base/android/token_android.cc b/base/android/token_android.cc
index 07d7491..34bec993 100644
--- a/base/android/token_android.cc
+++ b/base/android/token_android.cc
@@ -11,8 +11,8 @@
 
 ScopedJavaLocalRef<jobject> TokenAndroid::Create(JNIEnv* env,
                                                  const base::Token& token) {
-  return Java_Token_create(env, static_cast<jlong>(token.high()),
-                           static_cast<jlong>(token.low()));
+  return Java_Token_Constructor(env, static_cast<jlong>(token.high()),
+                                static_cast<jlong>(token.low()));
 }
 
 base::Token TokenAndroid::FromJavaToken(JNIEnv* env,
diff --git a/base/memory/weak_ptr.h b/base/memory/weak_ptr.h
index 40bd1ca2..4211c91 100644
--- a/base/memory/weak_ptr.h
+++ b/base/memory/weak_ptr.h
@@ -294,6 +294,16 @@
     ptr_ = nullptr;
   }
 
+  // Do not use this method. Almost all callers should instead use operator
+  // bool().
+  //
+  // There are a few rare cases where the caller intentionally needs to check
+  // validity of a base::WeakPtr on a sequence different from the bound sequence
+  // as an unavoidable performance optimization. This is the only valid use-case
+  // for this method. See
+  // https://docs.google.com/document/d/1IGzq9Nx69GS_2iynGmPWo5sFAD2DcCyBY1zIvZwF7k8
+  // for details.
+  //
   // Returns false if the WeakPtr is confirmed to be invalid. This call is safe
   // to make from any thread, e.g. to optimize away unnecessary work, but
   // RefIsValid() must always be called, on the correct sequence, before
diff --git a/base/win/windows_version.cc b/base/win/windows_version.cc
index 04bd7ae0..88cfcad5 100644
--- a/base/win/windows_version.cc
+++ b/base/win/windows_version.cc
@@ -349,6 +349,9 @@
   }
 
   if (major == 10) {
+    if (build >= 22631) {
+      return Version::WIN11_23H2;
+    }
     if (build >= 22621) {
       return Version::WIN11_22H2;
     }
diff --git a/base/win/windows_version.h b/base/win/windows_version.h
index 145ff47..1248047 100644
--- a/base/win/windows_version.h
+++ b/base/win/windows_version.h
@@ -58,6 +58,7 @@
   SERVER_2022 = 21,  // Server 2022: Build 20348.
   WIN11 = 22,        // Win11 21H2: Build 22000.
   WIN11_22H2 = 23,   // Win11 22H2: Build 22621.
+  WIN11_23H2 = 24,   // Win11 23H2: Build 22631.
   WIN_LAST,          // Indicates error condition.
 };
 
diff --git a/base/win/windows_version_unittest.cc b/base/win/windows_version_unittest.cc
index 0ef9e75..490ce77 100644
--- a/base/win/windows_version_unittest.cc
+++ b/base/win/windows_version_unittest.cc
@@ -46,7 +46,9 @@
 TEST(OSInfo, MajorMinorBuildToVersion) {
   EXPECT_EQ(OSInfo::MajorMinorBuildToVersion(11, 0, 0), Version::WIN11);
   EXPECT_EQ(OSInfo::MajorMinorBuildToVersion(10, 0, 32767),
-            Version::WIN11_22H2);
+            Version::WIN11_23H2);
+  EXPECT_EQ(OSInfo::MajorMinorBuildToVersion(10, 0, 22631),
+            Version::WIN11_23H2);
   EXPECT_EQ(OSInfo::MajorMinorBuildToVersion(10, 0, 22621),
             Version::WIN11_22H2);
   EXPECT_EQ(OSInfo::MajorMinorBuildToVersion(10, 0, 22000), Version::WIN11);
diff --git a/build/config/compiler/pgo/pgo.gni b/build/config/compiler/pgo/pgo.gni
index a85334d8..008a5752 100644
--- a/build/config/compiler/pgo/pgo.gni
+++ b/build/config/compiler/pgo/pgo.gni
@@ -5,6 +5,7 @@
 import("//build/config/cast.gni")
 import("//build/config/chrome_build.gni")
 import("//build/config/chromeos/ui_mode.gni")
+import("//build/config/cronet/config.gni")
 import("//build/config/dcheck_always_on.gni")
 import("//build/config/features.gni")
 import("//build/config/ios/config.gni")
@@ -20,7 +21,7 @@
   # to think some code is hotter than it actually is, potentially causing very
   # bad compile times.
   chrome_pgo_phase = 0
-  if (!dcheck_always_on && is_official_build &&
+  if (!is_cronet_build && !dcheck_always_on && is_official_build &&
       # TODO(crbug.com/1336055): Update this now-outdated condition with regard
       # to chromecast and determine whether chromeos_is_browser_only is
       # obsolete.
diff --git a/build/config/fuchsia/gn_configs.gni b/build/config/fuchsia/gn_configs.gni
index ca9e2c6..23864de 100644
--- a/build/config/fuchsia/gn_configs.gni
+++ b/build/config/fuchsia/gn_configs.gni
@@ -9,7 +9,7 @@
   # value to specify the API level the packages produced from this repository
   # should be targeting, e.g. in their top-level //.gn file. A value of -1
   # means that no API level will be passed to the tools that consumes it.
-  fuchsia_target_api_level = 12
+  fuchsia_target_api_level = 16
 
   # Path to the fuchsia SDK. This is intended for use in other templates &
   # rules to reference the contents of the fuchsia SDK.
diff --git a/build/rust/std/rules/BUILD.gn b/build/rust/std/rules/BUILD.gn
index 00f8cf4..41b496d4 100644
--- a/build/rust/std/rules/BUILD.gn
+++ b/build/rust/std/rules/BUILD.gn
@@ -632,7 +632,6 @@
     "//third_party/rust-toolchain/lib/rustlib/src/rust/library/core/src/num/uint_macros.rs",
     "//third_party/rust-toolchain/lib/rustlib/src/rust/library/core/src/num/wrapping.rs",
     "//third_party/rust-toolchain/lib/rustlib/src/rust/library/core/src/ops/arith.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/library/core/src/ops/async_function.rs",
     "//third_party/rust-toolchain/lib/rustlib/src/rust/library/core/src/ops/bit.rs",
     "//third_party/rust-toolchain/lib/rustlib/src/rust/library/core/src/ops/control_flow.rs",
     "//third_party/rust-toolchain/lib/rustlib/src/rust/library/core/src/ops/coroutine.rs",
@@ -1571,82 +1570,80 @@
 }
 cargo_crate("object") {
   crate_type = "rlib"
-  crate_root = "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/lib.rs"
+  crate_root = "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/lib.rs"
   sources = [
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/archive.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/common.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/elf.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/endian.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/lib.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/macho.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/pe.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/pod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/any.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/archive.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/coff/comdat.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/coff/file.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/coff/import.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/coff/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/coff/relocation.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/coff/section.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/coff/symbol.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/elf/attributes.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/elf/comdat.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/elf/compression.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/elf/dynamic.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/elf/file.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/elf/hash.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/elf/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/elf/note.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/elf/relocation.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/elf/section.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/elf/segment.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/elf/symbol.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/elf/version.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/macho/dyld_cache.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/macho/fat.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/macho/file.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/macho/load_command.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/macho/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/macho/relocation.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/macho/section.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/macho/segment.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/macho/symbol.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/pe/data_directory.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/pe/export.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/pe/file.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/pe/import.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/pe/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/pe/relocation.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/pe/resource.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/pe/rich.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/pe/section.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/read_cache.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/read_ref.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/traits.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/util.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/wasm.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/xcoff/comdat.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/xcoff/file.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/xcoff/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/xcoff/relocation.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/xcoff/section.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/xcoff/segment.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/read/xcoff/symbol.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/write/coff/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/write/coff/object.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/write/coff/writer.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/write/elf/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/write/elf/object.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/write/elf/writer.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/write/macho.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/write/mod.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/write/pe.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/write/string.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/write/util.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/write/xcoff.rs",
-    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.2/src/xcoff.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/archive.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/common.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/elf.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/endian.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/lib.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/macho.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/pe.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/pod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/any.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/archive.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/coff/comdat.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/coff/file.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/coff/import.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/coff/mod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/coff/relocation.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/coff/section.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/coff/symbol.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/elf/attributes.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/elf/comdat.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/elf/compression.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/elf/dynamic.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/elf/file.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/elf/hash.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/elf/mod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/elf/note.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/elf/relocation.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/elf/section.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/elf/segment.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/elf/symbol.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/elf/version.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/macho/dyld_cache.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/macho/fat.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/macho/file.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/macho/load_command.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/macho/mod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/macho/relocation.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/macho/section.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/macho/segment.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/macho/symbol.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/mod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/pe/data_directory.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/pe/export.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/pe/file.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/pe/import.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/pe/mod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/pe/relocation.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/pe/resource.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/pe/rich.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/pe/section.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/read_cache.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/read_ref.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/traits.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/util.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/wasm.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/xcoff/comdat.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/xcoff/file.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/xcoff/mod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/xcoff/relocation.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/xcoff/section.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/xcoff/segment.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/read/xcoff/symbol.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/write/coff.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/write/elf/mod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/write/elf/object.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/write/elf/writer.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/write/macho.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/write/mod.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/write/pe.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/write/string.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/write/util.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/write/xcoff.rs",
+    "//third_party/rust-toolchain/lib/rustlib/src/rust/vendor/object-0.32.1/src/xcoff.rs",
   ]
   inputs = []
   no_std = true
@@ -1654,7 +1651,7 @@
   # Unit tests skipped. Generate with --with-tests to include them.
   build_native_rust_unit_tests = false
   edition = "2018"
-  cargo_pkg_version = "0.32.2"
+  cargo_pkg_version = "0.32.1"
   cargo_pkg_name = "object"
   cargo_pkg_description =
       "A unified interface for reading and writing object file formats."
diff --git a/build/toolchain/toolchain.gni b/build/toolchain/toolchain.gni
index ad587d7c..1515e866 100644
--- a/build/toolchain/toolchain.gni
+++ b/build/toolchain/toolchain.gni
@@ -46,8 +46,11 @@
 declare_args() {
   if (llvm_android_mainline) {  # https://crbug.com/1481060
     clang_version = "17"
-  } else {
+  } else if (llvm_force_head_revision) {
     clang_version = "19"
+  } else {
+    # TODO(crbug.com/1517549): Remove in the next Clang roll.
+    clang_version = "18"
   }
 }
 
diff --git a/buildtools/third_party/libc++/BUILD.gn b/buildtools/third_party/libc++/BUILD.gn
index d425af6..9a09b6f 100644
--- a/buildtools/third_party/libc++/BUILD.gn
+++ b/buildtools/third_party/libc++/BUILD.gn
@@ -196,7 +196,12 @@
       # specified in the C++ spec 3.7.4p2, which makes them always have default
       # visibility.  This option is needed to force hidden visibility since
       # -fvisibility=hidden doesn't have the desired effect.
-      cflags = [ "-fvisibility-global-new-delete=force-hidden" ]
+      if (llvm_force_head_revision) {
+        cflags = [ "-fvisibility-global-new-delete=force-hidden" ]
+      } else {
+        # TODO(b/41496712): Remove when updating Clang.
+        cflags = [ "-fvisibility-global-new-delete-hidden" ]
+      }
     } else {
       defines += [
         # This resets the visibility to default only for the various
diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc
index d7d1fd0..471644c 100644
--- a/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -820,7 +820,7 @@
 
 // TODO(crbug.com/1521926): Test is flaky on Win asan.
 // TODO(crbug.com/1517753): Test is flaky on Mac asan.
-#if (BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)) && defined(LEAK_SANITIZER)
+#if (BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)) && defined(ADDRESS_SANITIZER)
 #define MAYBE_DeviceScaleFactor15_ScrollRootScrollLayer \
   DISABLED_DeviceScaleFactor15_ScrollRootScrollLayer
 #else
diff --git a/chrome/android/features/start_surface/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java b/chrome/android/features/start_surface/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
index 5e241a8a..966ecae 100644
--- a/chrome/android/features/start_surface/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
+++ b/chrome/android/features/start_surface/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
@@ -1879,6 +1879,11 @@
         return mStartMargin;
     }
 
+    @Override
+    public boolean isHomeSurface() {
+        return true;
+    }
+
     public FeedActionDelegate getFeedActionDelegateForTesting() {
         assert mPropertyModel.get(EXPLORE_SURFACE_COORDINATOR) != null;
         return mPropertyModel
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerCoordinator.java
index ea763cf..52445a2 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerCoordinator.java
@@ -7,6 +7,7 @@
 import android.content.Context;
 import android.view.LayoutInflater;
 
+import androidx.annotation.LayoutRes;
 import androidx.annotation.NonNull;
 
 import org.chromium.base.supplier.ObservableSupplier;
@@ -22,13 +23,13 @@
     public ColorPickerCoordinator(
             @NonNull Context context,
             @NonNull List<Integer> colors,
-            @NonNull ColorPickerDelegate delegate) {
+            @NonNull @LayoutRes int colorPickerLayout,
+            @NonNull @ColorPickerType int colorPickerType) {
         mContainerView =
                 (ColorPickerContainer)
-                        LayoutInflater.from(context)
-                                .inflate(delegate.getColorPickerUIComponent(), /* root= */ null);
+                        LayoutInflater.from(context).inflate(colorPickerLayout, /* root= */ null);
 
-        mMediator = new ColorPickerMediator(colors);
+        mMediator = new ColorPickerMediator(colors, colorPickerType);
         mMediator.setColorListItems(mContainerView);
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerDelegate.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerDelegate.java
deleted file mode 100644
index 6a3689b..0000000
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerDelegate.java
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.tasks.tab_management;
-
-/**
- * The delegate responsible for handling UI-specific arrangements on each {@link ColorPicker}
- * implementation.
- */
-public interface ColorPickerDelegate {
-    /** Retrieve the UI component used for inflating the color picker. */
-    int getColorPickerUIComponent();
-}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerItemProperties.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerItemProperties.java
index 514ebbd..0929f307 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerItemProperties.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerItemProperties.java
@@ -18,18 +18,28 @@
     /** An indicator of whether this color is the currently selected one. */
     public static final WritableBooleanPropertyKey IS_SELECTED = new WritableBooleanPropertyKey();
 
+    /** The {@link ColorPickerType} that this color item corresponds to. */
+    public static final ReadableIntPropertyKey COLOR_PICKER_TYPE = new ReadableIntPropertyKey();
+
     /** The function to run when this color item is selected by the user. */
     public static final ReadableObjectPropertyKey<Runnable> ON_CLICK_LISTENER =
             new ReadableObjectPropertyKey<>();
 
     /** Creates a model for a color item. */
-    public static PropertyModel create(int color, boolean isSelected, Runnable onClickListener) {
+    public static PropertyModel create(
+            int color,
+            boolean isSelected,
+            @ColorPickerType int colorPickerType,
+            Runnable onClickListener) {
         return new PropertyModel.Builder(ALL_KEYS)
                 .with(COLOR_ID, color)
                 .with(IS_SELECTED, isSelected)
+                .with(COLOR_PICKER_TYPE, colorPickerType)
                 .with(ON_CLICK_LISTENER, onClickListener)
                 .build();
     }
 
-    public static final PropertyKey[] ALL_KEYS = {COLOR_ID, IS_SELECTED, ON_CLICK_LISTENER};
+    public static final PropertyKey[] ALL_KEYS = {
+        COLOR_ID, IS_SELECTED, COLOR_PICKER_TYPE, ON_CLICK_LISTENER
+    };
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerItemViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerItemViewBinder.java
index 76f5fdbc..0cb85132 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerItemViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerItemViewBinder.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.tasks.tab_management;
 
 import static org.chromium.chrome.browser.tasks.tab_management.ColorPickerItemProperties.COLOR_ID;
+import static org.chromium.chrome.browser.tasks.tab_management.ColorPickerItemProperties.COLOR_PICKER_TYPE;
 import static org.chromium.chrome.browser.tasks.tab_management.ColorPickerItemProperties.IS_SELECTED;
 import static org.chromium.chrome.browser.tasks.tab_management.ColorPickerItemProperties.ON_CLICK_LISTENER;
 
@@ -47,17 +48,18 @@
     }
 
     private static void setColorOnColorIcon(PropertyModel model, View view) {
-        @TabGroupColorId int colorId = model.get(COLOR_ID);
+        @ColorPickerType int colorPickerType = model.get(COLOR_PICKER_TYPE);
+        int colorId = model.get(COLOR_ID);
 
         // Update the color icon with the indicated color id.
         ImageView colorIcon = view.findViewById(R.id.color_picker_icon);
         LayerDrawable layerDrawable = (LayerDrawable) colorIcon.getBackground();
         ((GradientDrawable) layerDrawable.getDrawable(OUTER_LAYER))
-                .setColor(getColorScheme(colorId));
+                .setColor(getColor(colorPickerType, colorId));
         ((GradientDrawable) layerDrawable.getDrawable(SELECTION_LAYER))
-                .setColor(getColorScheme(SELECTION_BG_COLOR));
+                .setColor(getColor(colorPickerType, SELECTION_BG_COLOR));
         ((GradientDrawable) layerDrawable.getDrawable(INNER_LAYER))
-                .setColor(getColorScheme(colorId));
+                .setColor(getColor(colorPickerType, colorId));
 
         // Refresh the color item view.
         colorIcon.invalidate();
@@ -75,9 +77,17 @@
         colorIcon.invalidate();
     }
 
+    private static @ColorInt int getColor(@ColorPickerType int colorPickerType, int colorId) {
+        if (colorPickerType == ColorPickerType.TAB_GROUP) {
+            return getTabGroupColor(colorId);
+        } else {
+            return Color.TRANSPARENT;
+        }
+    }
+
     // TODO(crbug.com/1517346): Replace temp colors with proper color palette, and add accessibility
     // strings for each view.
-    private static @ColorInt int getColorScheme(@TabGroupColorId int colorId) {
+    private static @ColorInt int getTabGroupColor(int colorId) {
         switch (colorId) {
             case TabGroupColorId.GREY:
                 return Color.GRAY;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerMediator.java
index b6712fe..33a6477 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerMediator.java
@@ -21,15 +21,20 @@
 public class ColorPickerMediator {
     private final @NonNull List<PropertyModel> mColorItems;
     private final @NonNull List<Integer> mColors;
+    private final @NonNull @ColorPickerType int mColorPickerType;
     private ObservableSupplierImpl<Integer> mSelectedColorSupplier = new ObservableSupplierImpl<>();
 
-    public ColorPickerMediator(List<Integer> colors) {
-        this(colors, new ArrayList<>());
+    public ColorPickerMediator(List<Integer> colors, @ColorPickerType int colorPickerType) {
+        this(colors, new ArrayList<>(), colorPickerType);
     }
 
-    protected ColorPickerMediator(List<Integer> colors, List<PropertyModel> colorItems) {
+    protected ColorPickerMediator(
+            List<Integer> colors,
+            List<PropertyModel> colorItems,
+            @ColorPickerType int colorPickerType) {
         mColors = colors;
         mColorItems = colorItems;
+        mColorPickerType = colorPickerType;
     }
 
     /**
@@ -39,7 +44,6 @@
      * @param containerView The parent container for all color items inflated by this component.
      */
     public void setColorListItems(ColorPickerContainer containerView) {
-        // The default selected color, which is the 0th item in the list.
         List<FrameLayout> colorViews = new ArrayList<>();
         Context context = containerView.getContext();
 
@@ -51,6 +55,7 @@
                     ColorPickerItemProperties.create(
                             /* color= */ color,
                             /* isSelected= */ false,
+                            /* colorPickerType= */ mColorPickerType,
                             /* onClickListener= */ () -> {
                                 setSelectedColorItem(color);
                             });
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerType.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerType.java
new file mode 100644
index 0000000..cd407d2
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerType.java
@@ -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.
+
+package org.chromium.chrome.browser.tasks.tab_management;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** The different color picker types. */
+@IntDef({ColorPickerType.TAB_GROUP})
+@Retention(RetentionPolicy.SOURCE)
+public @interface ColorPickerType {
+    /** The tab group color picker component. */
+    int TAB_GROUP = 1;
+}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoTabSwitcherPane.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoTabSwitcherPane.java
index 7e5c676..7506f8ef08 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoTabSwitcherPane.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoTabSwitcherPane.java
@@ -195,17 +195,32 @@
         @Nullable TabSwitcherPaneCoordinator coordinator = getTabSwitcherPaneCoordinator();
         if (coordinator == null) return false;
 
+        @Nullable TabModelFilter filter = mIncognitoTabModelFilterSupplier.get();
+        if (filter == null || !filter.isTabModelRestored()) {
+            // The tab list is trying to show without the filter being ready. This happens when
+            // first trying to show a the pane. If this happens an attempt to show will be made
+            // when the filter's restoreCompleted() method is invoked in TabSwitcherPaneMediator.
+            // Start a timer to measure how long it takes for tab state to be initialized and for
+            // this UI to show i.e. isTabModelRestored becomes true. This timer will emit a
+            // histogram when we successfully show. This timer is cancelled if: 1) the pane becomes
+            // invisible in TabSwitcherPaneBase#notifyLoadHint, or 2) the filter becomes ready and
+            // nothing gets shown.
+            startWaitForTabStateInitializedTimer();
+            return false;
+        }
+
         boolean isNotVisibleOrSelected =
-                !getIsVisibleSupplier().get()
-                        || !mIncognitoTabModelFilterSupplier.get().isCurrentlySelectedFilter();
+                !getIsVisibleSupplier().get() || !filter.isCurrentlySelectedFilter();
         boolean incognitoReauthShowing =
                 mIncognitoReauthController != null
                         && mIncognitoReauthController.isIncognitoReauthPending();
 
         if (isNotVisibleOrSelected || incognitoReauthShowing) {
             coordinator.resetWithTabList(null);
+            cancelWaitForTabStateInitializedTimer();
         } else {
             coordinator.resetWithTabList(tabList);
+            finishWaitForTabStateInitializedTimer();
         }
 
         setNewTabButtonEnabledState(/* enabled= */ !incognitoReauthShowing);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoTabSwitcherPaneUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoTabSwitcherPaneUnitTest.java
index af97dede..ce60e4d 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoTabSwitcherPaneUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoTabSwitcherPaneUnitTest.java
@@ -44,6 +44,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.browser.hub.DisplayButtonData;
 import org.chromium.chrome.browser.hub.FullButtonData;
 import org.chromium.chrome.browser.hub.HubFieldTrial;
@@ -105,6 +106,7 @@
                         anyBoolean());
 
         when(mTabModelFilter.getTabModel()).thenReturn(mIncognitoTabModel);
+        when(mTabModelFilter.isTabModelRestored()).thenReturn(true);
 
         mIncognitoTabSwitcherPane =
                 new IncognitoTabSwitcherPane(
@@ -295,6 +297,34 @@
 
     @Test
     @SmallTest
+    public void testLoadHintColdHot_TabStateNotInitialized() {
+        when(mTabModelFilter.isCurrentlySelectedFilter()).thenReturn(true);
+        when(mTabModelFilter.isTabModelRestored()).thenReturn(false);
+
+        mIncognitoTabSwitcherPane.notifyLoadHint(LoadHint.COLD);
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        assertNull(mIncognitoTabSwitcherPane.getTabSwitcherPaneCoordinator());
+
+        mIncognitoTabSwitcherPane.notifyLoadHint(LoadHint.HOT);
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        TabSwitcherPaneCoordinator coordinator =
+                mIncognitoTabSwitcherPane.getTabSwitcherPaneCoordinator();
+        assertNotNull(coordinator);
+        verify(coordinator, never()).resetWithTabList(mTabModelFilter);
+        verify(coordinator).setInitialScrollIndexOffset();
+        verify(coordinator).requestAccessibilityFocusOnCurrentTab();
+
+        when(mTabModelFilter.isTabModelRestored()).thenReturn(true);
+        var watcher =
+                HistogramWatcher.newSingleRecordWatcher(
+                        "Android.GridTabSwitcher.TimeToTabStateInitializedFromShown");
+        mIncognitoTabSwitcherPane.showAllTabs();
+        verify(coordinator).resetWithTabList(mTabModelFilter);
+        watcher.assertExpected();
+    }
+
+    @Test
+    @SmallTest
     public void testResetWithTabListReauthRequired() {
         mIncognitoReauthControllerSupplier.set(mIncognitoReauthController);
         ShadowLooper.runUiThreadTasks();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupCreationDialogDelegate.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupCreationDialogDelegate.java
index ff5bf1d..41f10fe 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupCreationDialogDelegate.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupCreationDialogDelegate.java
@@ -134,16 +134,12 @@
                                         tabCount));
 
         List<Integer> colors = ColorPickerUtils.getTabGroupColorIdList();
-        ColorPickerDelegate delegate =
-                new ColorPickerDelegate() {
-                    @Override
-                    public int getColorPickerUIComponent() {
-                        return R.layout.tab_group_color_picker_container;
-                    }
-                };
-
         ColorPickerCoordinator colorPickerCoordinator =
-                new ColorPickerCoordinator(mActivity, colors, delegate);
+                new ColorPickerCoordinator(
+                        mActivity,
+                        colors,
+                        R.layout.tab_group_color_picker_container,
+                        ColorPickerType.TAB_GROUP);
         colorPickerCoordinator.setSelectedColorItem(colors.get(1));
 
         TabGroupCreationTextInputLayout groupTitle = customView.findViewById(R.id.tab_group_title);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegate.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegate.java
index 2b75d96..370ee60 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegate.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegate.java
@@ -11,6 +11,7 @@
 import android.view.ViewGroup;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.LayoutRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
@@ -218,11 +219,12 @@
      *
      * @param activity The current Android's context.
      * @param colors The list of colors used for this color picker component.
-     * @param delegate The {@link ColorPickerDelegate} holding information regarding the UI layout
-     *     to inflate.
+     * @param colorPickerLayout The layout resource to be inflated.
+     * @param colorPickerType The {@link ColorPickerType} that this color picker use.
      */
     ColorPicker createColorPickerCoordinator(
             @NonNull Context context,
             @NonNull List<Integer> colors,
-            @NonNull ColorPickerDelegate delegate);
+            @NonNull @LayoutRes int colorPickerLayout,
+            @NonNull @ColorPickerType int colorPickerType);
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
index 3df42a06..b3f61fb 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
@@ -10,6 +10,7 @@
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 
+import androidx.annotation.LayoutRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
@@ -219,7 +220,8 @@
     public ColorPicker createColorPickerCoordinator(
             @NonNull Context context,
             @NonNull List<Integer> colors,
-            @NonNull ColorPickerDelegate delegate) {
-        return new ColorPickerCoordinator(context, colors, delegate);
+            @NonNull @LayoutRes int colorPickerLayout,
+            @NonNull @ColorPickerType int colorPickerType) {
+        return new ColorPickerCoordinator(context, colors, colorPickerLayout, colorPickerType);
     }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPane.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPane.java
index 2edb555..aed208df 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPane.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPane.java
@@ -140,15 +140,32 @@
     @Override
     public boolean resetWithTabList(@Nullable TabList tabList, boolean quickMode) {
         @Nullable TabSwitcherPaneCoordinator coordinator = getTabSwitcherPaneCoordinator();
-        if (coordinator == null) return false;
+        if (coordinator == null) {
+            return false;
+        }
+
+        @Nullable TabModelFilter filter = mTabModelFilterSupplier.get();
+        if (filter == null || !filter.isTabModelRestored()) {
+            // The tab list is trying to show without the filter being ready. This happens when
+            // first trying to show a the pane. If this happens an attempt to show will be made
+            // when the filter's restoreCompleted() method is invoked in TabSwitcherPaneMediator.
+            // Start a timer to measure how long it takes for tab state to be initialized and for
+            // this UI to show i.e. isTabModelRestored becomes true. This timer will emit a
+            // histogram when we successfully show. This timer is cancelled if: 1) the pane becomes
+            // invisible in TabSwitcherPaneBase#notifyLoadHint, or 2) the filter becomes ready and
+            // nothing gets shown.
+            startWaitForTabStateInitializedTimer();
+            return false;
+        }
 
         boolean isNotVisibleOrSelected =
-                !getIsVisibleSupplier().get()
-                        || !mTabModelFilterSupplier.get().isCurrentlySelectedFilter();
+                !getIsVisibleSupplier().get() || !filter.isCurrentlySelectedFilter();
 
         if (isNotVisibleOrSelected) {
+            cancelWaitForTabStateInitializedTimer();
             coordinator.resetWithTabList(null);
         } else {
+            finishWaitForTabStateInitializedTimer();
             coordinator.resetWithTabList(tabList);
         }
         return true;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneBase.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneBase.java
index ab22a82..3eef5b4 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneBase.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneBase.java
@@ -12,6 +12,7 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Handler;
+import android.os.SystemClock;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
@@ -21,6 +22,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.Log;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.ObservableSupplierImpl;
@@ -103,6 +105,7 @@
 
     private boolean mNativeInitialized;
     private @Nullable PaneHubController mPaneHubController;
+    private @Nullable Long mWaitForTabStateInitializedStartTimeMs;
 
     /**
      * @param context The activity context.
@@ -161,6 +164,8 @@
             // need to know an animation is going to play and when it is finished (possibly using
             // the isAnimatingSupplier?).
             requestAccessibilityFocusOnCurrentTab();
+        } else {
+            cancelWaitForTabStateInitializedTimer();
         }
 
         if (loadHint == LoadHint.WARM) {
@@ -446,6 +451,26 @@
         coordinator.destroy();
     }
 
+    protected void startWaitForTabStateInitializedTimer() {
+        if (mWaitForTabStateInitializedStartTimeMs != null) return;
+
+        mWaitForTabStateInitializedStartTimeMs = SystemClock.elapsedRealtime();
+    }
+
+    protected void finishWaitForTabStateInitializedTimer() {
+        if (mWaitForTabStateInitializedStartTimeMs != null) {
+            RecordHistogram.recordTimesHistogram(
+                    "Android.GridTabSwitcher.TimeToTabStateInitializedFromShown",
+                    SystemClock.elapsedRealtime()
+                            - mWaitForTabStateInitializedStartTimeMs.longValue());
+            mWaitForTabStateInitializedStartTimeMs = null;
+        }
+    }
+
+    protected void cancelWaitForTabStateInitializedTimer() {
+        mWaitForTabStateInitializedStartTimeMs = null;
+    }
+
     private void onTabClick(int tabId) {
         if (mPaneHubController == null) return;
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneUnitTest.java
index 70e3212..68e96ae 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneUnitTest.java
@@ -45,6 +45,7 @@
 import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.base.test.util.UserActionTester;
 import org.chromium.chrome.browser.hub.DisplayButtonData;
 import org.chromium.chrome.browser.hub.FullButtonData;
@@ -118,6 +119,7 @@
 
         mTabModel = new MockTabModel(mProfile, null);
         when(mTabModelFilter.getTabModel()).thenReturn(mTabModel);
+        when(mTabModelFilter.isTabModelRestored()).thenReturn(true);
 
         Supplier<Boolean> gridDialogVisibilitySupplier = () -> false;
         when(mTabSwitcherPaneCoordinator.getTabSwitcherCustomViewManagerDelegate())
@@ -258,6 +260,33 @@
 
     @Test
     @SmallTest
+    public void testLoadHintColdHot_TabStateNotInitialized() {
+        when(mTabModelFilter.isCurrentlySelectedFilter()).thenReturn(true);
+        when(mTabModelFilter.isTabModelRestored()).thenReturn(false);
+
+        mTabSwitcherPane.notifyLoadHint(LoadHint.COLD);
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        assertNull(mTabSwitcherPane.getTabSwitcherPaneCoordinator());
+
+        mTabSwitcherPane.notifyLoadHint(LoadHint.HOT);
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        TabSwitcherPaneCoordinator coordinator = mTabSwitcherPane.getTabSwitcherPaneCoordinator();
+        assertNotNull(coordinator);
+        verify(coordinator, never()).resetWithTabList(mTabModelFilter);
+        verify(coordinator).setInitialScrollIndexOffset();
+        verify(coordinator).requestAccessibilityFocusOnCurrentTab();
+
+        when(mTabModelFilter.isTabModelRestored()).thenReturn(true);
+        var watcher =
+                HistogramWatcher.newSingleRecordWatcher(
+                        "Android.GridTabSwitcher.TimeToTabStateInitializedFromShown");
+        mTabSwitcherPane.showAllTabs();
+        verify(coordinator).resetWithTabList(mTabModelFilter);
+        watcher.assertExpected();
+    }
+
+    @Test
+    @SmallTest
     public void testLoadHintColdWarmHotCold() {
         when(mTabModelFilter.isCurrentlySelectedFilter()).thenReturn(true);
 
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorPickerTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorPickerTest.java
index 1b028fe..ac88344 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorPickerTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorPickerTest.java
@@ -65,17 +65,12 @@
                         colors.add(i);
                     }
 
-                    ColorPickerDelegate delegate =
-                            new ColorPickerDelegate() {
-                                @Override
-                                public int getColorPickerUIComponent() {
-                                    return R.layout.tab_group_color_picker_container;
-                                }
-                            };
-
                     mCoordinator =
                             new ColorPickerCoordinator(
-                                    mActivityTestRule.getActivity(), colors, delegate);
+                                    mActivityTestRule.getActivity(),
+                                    colors,
+                                    R.layout.tab_group_color_picker_container,
+                                    ColorPickerType.TAB_GROUP);
                     mCoordinator.setSelectedColorItem(colors.get(selectedIndex));
                     mContainerView = (ColorPickerContainer) mCoordinator.getContainerView();
                     mColorList = colors;
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerItemViewBinderUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerItemViewBinderUnitTest.java
index 8d5f145..003894a 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerItemViewBinderUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerItemViewBinderUnitTest.java
@@ -53,6 +53,7 @@
                 ColorPickerItemProperties.create(
                         1,
                         false,
+                        ColorPickerType.TAB_GROUP,
                         () -> {
                             mModel.set(IS_SELECTED, !mModel.get(IS_SELECTED));
                         });
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerMediatorUnitTest.java
index 30c2be9..93e18875 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerMediatorUnitTest.java
@@ -49,7 +49,7 @@
         MockitoAnnotations.initMocks(this);
         mActivity = Robolectric.buildActivity(Activity.class).setup().get();
         mColorIds = ColorPickerUtils.getTabGroupColorIdList();
-        mMediator = new ColorPickerMediator(mColorIds, mColorItems);
+        mMediator = new ColorPickerMediator(mColorIds, mColorItems, ColorPickerType.TAB_GROUP);
     }
 
     @Test
diff --git a/chrome/android/features/tab_ui/tab_management_java_sources.gni b/chrome/android/features/tab_ui/tab_management_java_sources.gni
index 26ead1a..95d89837 100644
--- a/chrome/android/features/tab_ui/tab_management_java_sources.gni
+++ b/chrome/android/features/tab_ui/tab_management_java_sources.gni
@@ -10,7 +10,7 @@
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/CloseAllTabsDialog.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPicker.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerContainer.java",
-  "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerDelegate.java",
+  "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerType.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerUtils.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/RecyclerViewPosition.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupCreationDialog.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
index 044a163..5e16be4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -3071,7 +3071,10 @@
 
         PwaUniversalInstallBottomSheetCoordinator pwaUniversalInstallBottomSheetCoordinator =
                 new PwaUniversalInstallBottomSheetCoordinator(
-                        this, controller, R.drawable.ic_forward_arrow_black_24dp);
+                        this,
+                        currentTab.getWebContents(),
+                        controller,
+                        R.drawable.ic_forward_arrow_black_24dp);
         if (!pwaUniversalInstallBottomSheetCoordinator.show()) {
             // Fall back to install method for the PWA.
             return doAddToHomescreenOrInstallWebApp(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
index 38da5d3..3ad08ed 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
@@ -59,6 +59,8 @@
 import org.chromium.chrome.browser.readaloud.ReadAloudController;
 import org.chromium.chrome.browser.share.ShareHelper;
 import org.chromium.chrome.browser.share.ShareUtils;
+import org.chromium.chrome.browser.sync.SyncServiceFactory;
+import org.chromium.chrome.browser.sync.settings.SyncSettingsUtils;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tasks.ReturnToChromeUtil;
@@ -85,6 +87,7 @@
 import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.components.embedder_support.util.UrlUtilities;
+import org.chromium.components.sync.SyncService;
 import org.chromium.components.webapk.lib.client.WebApkValidator;
 import org.chromium.components.webapps.AppBannerManager;
 import org.chromium.components.webapps.WebappsUtils;
@@ -373,6 +376,8 @@
 
             PropertyModel propertyModel = AppMenuUtil.menuItemToPropertyModel(item);
             propertyModel.set(AppMenuItemProperties.ICON_COLOR_RES, getMenuItemIconColorRes(item));
+            propertyModel.set(
+                    AppMenuItemProperties.ICON_SHOW_BADGE, shouldShowBadgeOnMenuItemIcon(item));
             propertyModel.set(AppMenuItemProperties.SUPPORT_ENTER_ANIMATION, true);
             propertyModel.set(AppMenuItemProperties.MENU_ICON_AT_START, isMenuIconAtStart());
             if (item.hasSubMenu()) {
@@ -614,6 +619,7 @@
         boolean isMenuSelectTabsEnabled =
                 !isIncognitoReauthShowing
                         && isMenuSelectTabsVisible
+                        && mTabModelSelector.isTabStateInitialized()
                         && mTabModelSelector
                                         .getTabModelFilterProvider()
                                         .getCurrentTabModelFilter()
@@ -975,6 +981,32 @@
         menu.findItem(R.id.readaloud_menu_id).setVisible(visible);
     }
 
+    /** Returns true if a badge (i.e. a red-dot) should be shown on the menu item icon. */
+    protected boolean shouldShowBadgeOnMenuItemIcon(MenuItem item) {
+        if (item.getItemId() == R.id.preferences_id) {
+            if (!ChromeFeatureList.isEnabled(
+                    ChromeFeatureList.SYNC_SHOW_IDENTITY_ERRORS_FOR_SIGNED_IN_USERS)) {
+                return false;
+            }
+            // Theoretically mTabModelSelector could return a stub model.
+            Profile profile = mTabModelSelector.getCurrentModel().getProfile();
+            if (profile == null) {
+                return false;
+            }
+            SyncService syncService = SyncServiceFactory.getForProfile(profile);
+            if (syncService == null || syncService.isSyncDisabledByEnterprisePolicy()) {
+                return false;
+            }
+            // Return true if there is any identity error(for signed-in users) or sync error(for
+            // syncing users).
+            return SyncSettingsUtils.getIdentityError(syncService)
+                            != SyncSettingsUtils.SyncError.NO_ERROR
+                    || SyncSettingsUtils.getSyncError(syncService)
+                            != SyncSettingsUtils.SyncError.NO_ERROR;
+        }
+        return false;
+    }
+
     @Override
     public void loadingStateChanged(boolean isLoading) {
         if (mReloadPropertyModel != null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/toolbar/ToolbarDragDropCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/toolbar/ToolbarDragDropCoordinator.java
index f9a2163a..393d94e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/toolbar/ToolbarDragDropCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/toolbar/ToolbarDragDropCoordinator.java
@@ -18,6 +18,7 @@
 import org.chromium.chrome.browser.omnibox.OmniboxFocusReason;
 import org.chromium.chrome.browser.omnibox.OmniboxStub;
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteDelegate;
+import org.chromium.chrome.browser.omnibox.suggestions.OmniboxLoadUrlParams;
 import org.chromium.components.search_engines.TemplateUrlService;
 import org.chromium.ui.base.MimeTypeUtils;
 import org.chromium.ui.base.PageTransition;
@@ -147,12 +148,11 @@
             String[] postData = urlService.getImageUrlAndPostContent();
             // TODO(crbug.com/1473127): Pass in correct imageByteArray to AutocompleteDelegate
             byte[] imageByteArray = new byte[0];
-            mAutocompleteDelegate.loadUrlWithPostData(
-                    postData[0],
-                    PageTransition.GENERATED,
-                    SystemClock.uptimeMillis(),
-                    postData[1],
-                    imageByteArray);
+            mAutocompleteDelegate.loadUrl(
+                    new OmniboxLoadUrlParams.Builder(postData[0], PageTransition.GENERATED)
+                            .setInputStartTimestamp(SystemClock.uptimeMillis())
+                            .setpostDataAndType(imageByteArray, postData[1])
+                            .build());
             recordDropType(DropType.CHROME_IMAGE);
         } else if (event.getClipDescription().hasMimeType(MimeTypeUtils.CHROME_MIMETYPE_TEXT)) {
             mOmniboxStub.setUrlBarFocus(
@@ -173,10 +173,10 @@
              */
             String url = event.getClipData().getItemAt(0).getIntent().getData().toString();
             mAutocompleteDelegate.loadUrl(
-                    url,
-                    PageTransition.TYPED,
-                    SystemClock.uptimeMillis(),
-                    /* openInNewTab= */ false);
+                    new OmniboxLoadUrlParams.Builder(url, PageTransition.TYPED)
+                            .setInputStartTimestamp(SystemClock.uptimeMillis())
+                            .setOpenInNewTab(false)
+                            .build());
             recordDropType(DropType.CHROME_LINK);
         } else {
             // case where dragged object is not from Chrome
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerBase.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerBase.java
index 2b10c3e..2e75580 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerBase.java
@@ -59,8 +59,8 @@
     // by the Android system (this value is additive on top of the show duration imposed by
     // Android).
     protected static final long ANDROID_CONTROLS_SHOW_DURATION_MS = 200;
-    // Delay to allow a frame to render between getting the fullscreen layout update and clearing
-    // the SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN flag.
+    // Delay to allow a frame to render between getting the fullscreen layout update and leaving
+    // layout fullscreen mode.
     private static final long CLEAR_LAYOUT_FULLSCREEN_DELAY_MS = 20;
 
     protected final Activity mActivity;
@@ -149,11 +149,10 @@
 
                         if (!fullscreenHtmlApiHandlerBase.isLayoutFullscreen(contentView)) return;
 
-                        // Trigger a update to clear the SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN flag
-                        // once the view has been laid out after this system UI update.  Without
-                        // clearing this flag, the keyboard appearing will not trigger a relayout
-                        // of the contents, which prevents updating the overdraw amount to the
-                        // renderer.
+                        // Trigger an update to unset layout fullscreen mode once the view has been
+                        // laid out after this system UI update.  Without clearing this flag, the
+                        // keyboard appearing will not trigger a relayout of the contents, which
+                        // prevents updating the overdraw amount to the renderer.
                         contentView.addOnLayoutChangeListener(
                                 new OnLayoutChangeListener() {
                                     @Override
@@ -465,42 +464,42 @@
         if (DEBUG_LOGS) logExitFullscreen(contentView);
         resetExitFullscreenLayoutChangeListener(contentView);
         if (webContents != null && !webContents.isDestroyed()) webContents.exitFullscreen();
+
+        // Ensure that the layout change listener to bring back browser controls is called on
+        // automotive devices that never hide system bars.
+        if (BuildInfo.getInstance().isAutomotive) {
+            ViewUtils.requestLayout(contentView, "FullscreenHtmlApiHandler.exitFullScreen");
+        }
     }
 
     private void resetExitFullscreenLayoutChangeListener(View contentView) {
         if (mFullscreenOnLayoutChangeListener != null) {
             contentView.removeOnLayoutChangeListener(mFullscreenOnLayoutChangeListener);
         }
-        // Since automotive devices persist the system bars in full screen mode, onLayoutChange is
-        // not triggered by showing the system bars, and browser controls need to be re-added
-        // directly.
-        if (BuildInfo.getInstance().isAutomotive) {
-            TabBrowserControlsConstraintsHelper.update(mTab, BrowserControlsState.SHOWN, true);
-        } else {
-            mFullscreenOnLayoutChangeListener =
-                    new OnLayoutChangeListener() {
-                        @Override
-                        public void onLayoutChange(
-                                View v,
-                                int left,
-                                int top,
-                                int right,
-                                int bottom,
-                                int oldLeft,
-                                int oldTop,
-                                int oldRight,
-                                int oldBottom) {
-                            if ((bottom - top) <= (oldBottom - oldTop)) {
-                                // At this point, browser controls are hidden. Show browser controls
-                                // only if it's permitted.
-                                TabBrowserControlsConstraintsHelper.update(
-                                        mTab, BrowserControlsState.SHOWN, true);
-                                contentView.removeOnLayoutChangeListener(this);
-                            }
+        mFullscreenOnLayoutChangeListener =
+                new OnLayoutChangeListener() {
+                    @Override
+                    public void onLayoutChange(
+                            View v,
+                            int left,
+                            int top,
+                            int right,
+                            int bottom,
+                            int oldLeft,
+                            int oldTop,
+                            int oldRight,
+                            int oldBottom) {
+                        if ((bottom - top) <= (oldBottom - oldTop)
+                                || BuildInfo.getInstance().isAutomotive) {
+                            // At this point, browser controls are hidden. Show browser controls
+                            // only if it's permitted.
+                            TabBrowserControlsConstraintsHelper.update(
+                                    mTab, BrowserControlsState.SHOWN, true);
+                            contentView.removeOnLayoutChangeListener(this);
                         }
-                    };
+                    }
+                };
             contentView.addOnLayoutChangeListener(mFullscreenOnLayoutChangeListener);
-        }
     }
 
     private boolean isAlreadyInFullscreenOrNavigationHidden(View contentView) {
@@ -551,8 +550,9 @@
             // To avoid a double layout that is caused by the system when just hiding
             // the status bar set the status bar as translucent immediately. This causes
             // it not to take up space so the layout is stable. (See https://crbug.com/935015).
-            // Do not do this in multi-window mode or automotive devices since the status bar is
-            // forced to always be visible.
+            // Do not do this in multi-window mode or if the system bars can't be dismissed (i.e.
+            // on some automotive devices), since the status bar will be forced to always stay
+            // visible.
             if (!mFullscreenOptions.showStatusBar
                     && !isMultiWindow
                     && !BuildInfo.getInstance().isAutomotive) {
@@ -567,7 +567,7 @@
         if (DEBUG_LOGS) logEnterFullscreen(contentView);
 
         // Request a layout so the updated system visibility takes affect.
-        // The flow will continue in the handler of MSG_ID_SET_FULLSCREEN_SYSTEM_UI_FLAGS message.
+        // The flow will continue in the handler of MSG_ID_UNSET_FULLSCREEN_LAYOUT message.
         ViewUtils.requestLayout(contentView, "FullscreenHtmlApiHandler.enterFullScreen");
 
         mWebContentsInFullscreen = webContents;
@@ -595,9 +595,8 @@
                             int oldRight,
                             int oldBottom) {
                         // On certain sites playing embedded video (http://crbug.com/293782),
-                        // setting the SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN does not always trigger a
-                        // view-level layout
-                        // with an updated height.  To work around this, do not check for an
+                        // setting the layout as fullscreen does not always trigger a view-level
+                        // layout with an updated height. To work around this, do not check for an
                         // increased height and always just trigger the next step of the
                         // fullscreen initialization.
                         // Posting the message to set the fullscreen flag because setting it
@@ -606,6 +605,8 @@
 
                         if ((bottom - top) <= (oldBottom - oldTop)
                                 && (right - left) <= (oldRight - oldLeft)
+                                // Some automotive devices never hide the system bars, so Chrome
+                                // can't rely on detecting a change in insets.
                                 && !BuildInfo.getInstance().isAutomotive) {
                             return;
                         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerCompat.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerCompat.java
index 5a43ff7..3d19de1d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerCompat.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerCompat.java
@@ -15,6 +15,7 @@
 import androidx.core.view.WindowInsetsCompat;
 import androidx.core.view.WindowInsetsControllerCompat;
 
+import org.chromium.base.BuildInfo;
 import org.chromium.base.Log;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.components.embedder_support.view.ContentView;
@@ -158,6 +159,11 @@
     // TODO(crbug.com/1519669): Coordinate usage of #setDecorFitsSystemWindows
     @Override
     void setLayoutFullscreen(View contentView) {
+        // Avoid setting this on automotive, as automotive devices are inconsistent in their
+        // support for drawing edge-to-edge.
+        if (BuildInfo.getInstance().isAutomotive) {
+            return;
+        }
         // TODO(https://crbug.com/1519954): Account for floating windows.
         WindowCompat.setDecorFitsSystemWindows(mActivity.getWindow(), false);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
index bffd786..12d08d7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -1438,4 +1438,11 @@
 
         return mMostRecentTabSupplier.get();
     }
+
+    @Override
+    public boolean isHomeSurface() {
+        // Can only show a local tab to resume if we we have a tracked tab. The presence of the
+        // local tab to resume module is effectively what being a home surface is.
+        return mMostRecentTabSupplier.hasValue();
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java
index e969f0f..ec65487 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java
@@ -45,6 +45,8 @@
 import org.chromium.chrome.browser.layouts.animation.CompositorAnimationHandler;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.quick_delete.QuickDeleteMetricsDelegate;
+import org.chromium.chrome.browser.sync.FakeSyncServiceImpl;
+import org.chromium.chrome.browser.sync.SyncServiceFactory;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuHandler;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuItemProperties;
@@ -56,6 +58,7 @@
 import org.chromium.chrome.test.util.ChromeRenderTestRule;
 import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.chrome.test.util.MenuUtils;
+import org.chromium.chrome.test.util.browser.signin.SigninTestRule;
 import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridge;
 import org.chromium.components.content_settings.ContentSettingsType;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
@@ -74,10 +77,17 @@
     @Rule
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
 
+    @Rule public final SigninTestRule mSigninTestRule = new SigninTestRule();
+
+    private static final int RENDER_TEST_REVISION = 2;
+    private static final String RENDER_TEST_DESCRIPTION =
+            "Badge on settings menu item icon on identity and sync errors.";
+
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
             ChromeRenderTestRule.Builder.withPublicCorpus()
-                    .setRevision(1)
+                    .setRevision(RENDER_TEST_REVISION)
+                    .setDescription(RENDER_TEST_DESCRIPTION)
                     .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_MOBILE_APP_MENU)
                     .build();
 
@@ -475,6 +485,90 @@
                         mActivityTestRule.getAppMenuCoordinator(), R.id.quick_delete_menu_id));
     }
 
+    @Test
+    @LargeTest
+    @Feature({"Browser", "Main", "RenderTest"})
+    @EnableFeatures(ChromeFeatureList.SYNC_SHOW_IDENTITY_ERRORS_FOR_SIGNED_IN_USERS)
+    public void testSettingsMenuItem_NoBadgeShownForNotSignedInUsers() throws IOException {
+        mRenderTestRule.render(getSettingsMenuItemView(), "settings_menu_item_not_signed_in_user");
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"Browser", "Main", "RenderTest"})
+    @EnableFeatures(ChromeFeatureList.SYNC_SHOW_IDENTITY_ERRORS_FOR_SIGNED_IN_USERS)
+    public void testSettingsMenuItem_BadgeShownForSignedInUsersOnIdentityError()
+            throws IOException {
+        TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.hideAppMenu());
+
+        FakeSyncServiceImpl fakeSyncService =
+                TestThreadUtils.runOnUiThreadBlockingNoException(
+                        () -> {
+                            FakeSyncServiceImpl fakeSyncServiceImpl = new FakeSyncServiceImpl();
+                            SyncServiceFactory.setInstanceForTesting(fakeSyncServiceImpl);
+                            return fakeSyncServiceImpl;
+                        });
+        // Fake an identity error.
+        fakeSyncService.setRequiresClientUpgrade(true);
+        // Sign in and wait for sync machinery to be active.
+        mSigninTestRule.addTestAccountThenSignin();
+
+        showAppMenuAndAssertMenuShown();
+        mRenderTestRule.render(
+                getSettingsMenuItemView(), "settings_menu_item_signed_in_user_identity_error");
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"Browser", "Main", "RenderTest"})
+    @EnableFeatures(ChromeFeatureList.SYNC_SHOW_IDENTITY_ERRORS_FOR_SIGNED_IN_USERS)
+    public void testSettingsMenuItem_NoBadgeShownForSignedInUsersIfNoError() throws IOException {
+        TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.hideAppMenu());
+        // Sign in and wait for sync machinery to be active.
+        mSigninTestRule.addTestAccountThenSignin();
+
+        showAppMenuAndAssertMenuShown();
+        mRenderTestRule.render(
+                getSettingsMenuItemView(), "settings_menu_item_signed_in_user_no_error");
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"Browser", "Main", "RenderTest"})
+    @EnableFeatures(ChromeFeatureList.SYNC_SHOW_IDENTITY_ERRORS_FOR_SIGNED_IN_USERS)
+    public void testSettingsMenuItem_BadgeShownForSyncingUsersOnSyncError() throws IOException {
+        TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.hideAppMenu());
+        FakeSyncServiceImpl fakeSyncService =
+                TestThreadUtils.runOnUiThreadBlockingNoException(
+                        () -> {
+                            FakeSyncServiceImpl fakeSyncServiceImpl = new FakeSyncServiceImpl();
+                            SyncServiceFactory.setInstanceForTesting(fakeSyncServiceImpl);
+                            return fakeSyncServiceImpl;
+                        });
+        // Fake an identity error.
+        fakeSyncService.setRequiresClientUpgrade(true);
+        // Sign in and wait for sync machinery to be active.
+        mSigninTestRule.addTestAccountThenSigninAndEnableSync();
+
+        showAppMenuAndAssertMenuShown();
+        mRenderTestRule.render(
+                getSettingsMenuItemView(), "settings_menu_item_syncing_user_sync_error");
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"Browser", "Main", "RenderTest"})
+    @EnableFeatures(ChromeFeatureList.SYNC_SHOW_IDENTITY_ERRORS_FOR_SIGNED_IN_USERS)
+    public void testSettingsMenuItem_NoBadgeShownForSyncingUsersIfNoError() throws IOException {
+        TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.hideAppMenu());
+        // Sign in and wait for sync machinery to be active.
+        mSigninTestRule.addTestAccountThenSigninAndEnableSync();
+
+        showAppMenuAndAssertMenuShown();
+        mRenderTestRule.render(
+                getSettingsMenuItemView(), "settings_menu_item_syncing_user_no_error");
+    }
+
     private void showAppMenuAndAssertMenuShown() {
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> {
@@ -541,4 +635,34 @@
     private ListView getListView() {
         return AppMenuTestSupport.getListView(mActivityTestRule.getAppMenuCoordinator());
     }
+
+    private View getSettingsMenuItemView() {
+        int position =
+                AppMenuTestSupport.findIndexOfMenuItemById(
+                        mActivityTestRule.getAppMenuCoordinator(), R.id.preferences_id);
+        Assert.assertTrue("No settings menu item found.", position != -1);
+
+        CriteriaHelper.pollUiThread(() -> getListView().getChildAt(0) != null);
+
+        Callable<Boolean> isVisible =
+                () -> {
+                    int visibleStart = getListView().getFirstVisiblePosition();
+                    int visibleEnd = visibleStart + getListView().getChildCount() - 1;
+                    return position >= visibleStart && position <= visibleEnd;
+                };
+
+        if (!TestThreadUtils.runOnUiThreadBlockingNoException(isVisible)) {
+            TestThreadUtils.runOnUiThreadBlocking(() -> getListView().setSelection(position));
+            CriteriaHelper.pollUiThread(isVisible);
+        }
+
+        View view =
+                TestThreadUtils.runOnUiThreadBlockingNoException(
+                        () -> {
+                            return getListView()
+                                    .getChildAt(position - getListView().getFirstVisiblePosition());
+                        });
+        Assert.assertNotNull("No settings menu item view found.", view);
+        return view;
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateUnitTest.java
index 73d5f43b..b913e927 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateUnitTest.java
@@ -187,6 +187,7 @@
         when(mTab.getProfile()).thenReturn(mProfile);
         when(mWebContents.getNavigationController()).thenReturn(mNavigationController);
         when(mNavigationController.getUseDesktopUserAgent()).thenReturn(false);
+        when(mTabModelSelector.isTabStateInitialized()).thenReturn(true);
         when(mTabModelSelector.getCurrentModel()).thenReturn(mTabModel);
         when(mTabModelSelector.getModel(false)).thenReturn((mTabModel));
         when(mTabModelSelector.getModel(true)).thenReturn((mIncognitoTabModel));
@@ -250,6 +251,8 @@
         setShoppingListEligible(false);
         setShoppingListEligible(false);
         mTestValues.addFeatureFlagOverride(ChromeFeatureList.PWA_UNIVERSAL_INSTALL_UI, false);
+        mTestValues.addFeatureFlagOverride(
+                ChromeFeatureList.SYNC_SHOW_IDENTITY_ERRORS_FOR_SIGNED_IN_USERS, false);
         FeatureList.setTestValues(mTestValues);
     }
 
@@ -1030,6 +1033,24 @@
 
     @Test
     @SmallTest
+    public void testSelectTabsOption_IsDisabled_InRegularMode_TabStateNotInitialized() {
+        setUpMocksForOverviewMenu(LayoutType.TAB_SWITCHER);
+        when(mTabModelSelector.getCurrentModel()).thenReturn(mTabModel);
+        prepareMocksForGroupTabsOnTabModel(mTabModel);
+
+        when(mTabModelSelector.isTabStateInitialized()).thenReturn(false);
+
+        Menu menu = createTestMenu();
+        mAppMenuPropertiesDelegate.prepareMenu(menu, null);
+        // Check group tabs enabled decision in regular mode doesn't depend on re-auth.
+        verify(mIncognitoReauthControllerMock, times(0)).isReauthPageShowing();
+
+        MenuItem item = menu.findItem(R.id.menu_select_tabs);
+        assertFalse(item.isEnabled());
+    }
+
+    @Test
+    @SmallTest
     public void testSelectTabsOption_IsEnabledOneTab_InRegularMode_IndependentOfIncognitoReauth() {
         setUpMocksForOverviewMenu(LayoutType.TAB_SWITCHER);
         when(mTabModelSelector.getCurrentModel()).thenReturn(mTabModel);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/toolbar/ToolbarDragDropCoordinatorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/toolbar/ToolbarDragDropCoordinatorUnitTest.java
index 5705b5a..d033fcc3 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/toolbar/ToolbarDragDropCoordinatorUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/toolbar/ToolbarDragDropCoordinatorUnitTest.java
@@ -6,8 +6,8 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -28,11 +28,15 @@
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatchers;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 import org.robolectric.Robolectric;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
@@ -42,6 +46,7 @@
 import org.chromium.chrome.browser.omnibox.OmniboxFocusReason;
 import org.chromium.chrome.browser.omnibox.OmniboxStub;
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteDelegate;
+import org.chromium.chrome.browser.omnibox.suggestions.OmniboxLoadUrlParams;
 import org.chromium.components.search_engines.TemplateUrlService;
 import org.chromium.ui.base.PageTransition;
 import org.chromium.ui.dragdrop.DropDataAndroid;
@@ -51,9 +56,14 @@
 /** Basic test for creating, using drag and drop to omnibox with {@link ToolbarDragDropCoordinator}. */
 @RunWith(BaseRobolectricTestRunner.class)
 public class ToolbarDragDropCoordinatorUnitTest {
+    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
     @Mock private AutocompleteDelegate mAutocompleteDelegate;
     @Mock private OmniboxStub mOmniboxStub;
     @Mock private TemplateUrlService mTemplateUrlService;
+
+    @Captor private ArgumentCaptor<OmniboxLoadUrlParams> mOmniboxLoadUrlParamsCaptor;
+
     private Activity mActivity;
 
     private ToolbarDragDropCoordinator mToolbarDragDropCoordinator;
@@ -235,12 +245,13 @@
         assertTrue(
                 "DragEvent eventDragMultipleMimeTypes should be consumed by target view",
                 resultDragMultipleMimeTypes);
-        verify(mAutocompleteDelegate)
-                .loadUrl(
-                        eq(JUnitTestGURLs.EXAMPLE_URL.getSpec()),
-                        eq(PageTransition.TYPED),
-                        ArgumentMatchers.anyLong(),
-                        eq(false));
+
+        verify(mAutocompleteDelegate).loadUrl(mOmniboxLoadUrlParamsCaptor.capture());
+        assertEquals(
+                mOmniboxLoadUrlParamsCaptor.getValue().url, JUnitTestGURLs.EXAMPLE_URL.getSpec());
+        assertEquals(mOmniboxLoadUrlParamsCaptor.getValue().transitionType, PageTransition.TYPED);
+        assertFalse(mOmniboxLoadUrlParamsCaptor.getValue().openInNewTab);
+
         histogramExpectation.assertExpected();
     }
 
@@ -257,13 +268,11 @@
         doReturn(dropDataAndroid).when(eventDragImage).getLocalState();
         boolean resultDragImage = mTargetViewDragListener.onDrag(mTargetView, eventDragImage);
         assertTrue("DragEvent eventDragImage should be consumed by target view", resultDragImage);
-        verify(mAutocompleteDelegate)
-                .loadUrlWithPostData(
-                        eq(mMockPostData[0]),
-                        ArgumentMatchers.anyInt(),
-                        ArgumentMatchers.anyLong(),
-                        eq(mMockPostData[1]),
-                        ArgumentMatchers.notNull());
+
+        verify(mAutocompleteDelegate).loadUrl(mOmniboxLoadUrlParamsCaptor.capture());
+        assertEquals(mOmniboxLoadUrlParamsCaptor.getValue().url, mMockPostData[0]);
+        assertEquals(mOmniboxLoadUrlParamsCaptor.getValue().postDataType, mMockPostData[1]);
+        assertNotNull(mOmniboxLoadUrlParamsCaptor.getValue().postData);
         histogramExpectation.assertExpected();
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java
index cbfcf57..9765a27 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java
@@ -40,6 +40,7 @@
 import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Features;
+import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.R;
@@ -97,6 +98,7 @@
     ChromeFeatureList.WEB_FEED,
     UiAccessibilityFeatures.START_SURFACE_ACCESSIBILITY_CHECK
 })
+@DisableFeatures(ChromeFeatureList.SYNC_SHOW_IDENTITY_ERRORS_FOR_SIGNED_IN_USERS)
 public class TabbedAppMenuPropertiesDelegateUnitTest {
     // Constants defining flags that determines multi-window menu items visibility.
     private static final boolean TAB_M = true; // multiple tabs
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index a696a5e..2920788f 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -6057,19 +6057,19 @@
     Packaged web applications with enhanced capabilities. <ph name="BEGIN_LINK_LEARN_MORE">&lt;a target="_blank" href="$1"&gt;</ph>Learn more<ph name="END_LINK_LEARN_MORE">&lt;/a&gt;</ph>
   </message>
   <message name="IDS_OS_SETTINGS_REVAMP_APP_NOTIFICATIONS_LINK_DESCRIPTION" desc="The description for the settings row button in the App section of OS Settings which links to the App Notifications page.">
-    Manage app notifications, Do not disturb, and app badging
+    Manage app notifications, Do Not Disturb, and app badging
   </message>
   <message name="IDS_SETTINGS_APP_NOTIFICATIONS_SUBLABEL_TEXT" desc="The sublabel for the settings row button in the App section of OS Settings which links to the App Notifications page indicating the number of ChromeOS apps installed.">
     <ph name="APP_COUNT">$1<ex>3</ex></ph> apps
   </message>
-  <message name="IDS_SETTINGS_APP_NOTIFICATIONS_DND_ENABLED_SUBLABEL_TEXT" desc="The sublabel for the settings row button in the App section of OS Settings which links to the App Notifications page indicating that Do not disturb is on.">
-    Do not disturb enabled
+  <message name="IDS_SETTINGS_APP_NOTIFICATIONS_DND_ENABLED_SUBLABEL_TEXT" desc="The sublabel for the settings row button in the App section of OS Settings which links to the App Notifications page indicating that Do Not Disturb is on.">
+    Do Not Disturb enabled
   </message>
-  <message name="IDS_SETTINGS_APP_NOTIFICATIONS_DO_NOT_DISTURB_TOGGLE_TITLE" desc="The label for the notfications DoNotDisturb toggle that lets the user silence all app notifications.">
-    Do not disturb
+  <message name="IDS_SETTINGS_APP_NOTIFICATIONS_DO_NOT_DISTURB_TOGGLE_TITLE" desc="The label for the notfications Do Not Disturb toggle that lets the user silence all app notifications.">
+    Do Not Disturb
   </message>
-   <message name="IDS_OS_SETTINGS_REVAMP_APP_NOTIFICATIONS_DO_NOT_DISTURB_TOGGLE_DESCRIPTION" desc="The text description of the DoNotDisturb toggle within help icon tooltip.">
-    Notifications won't pop up on the screen. You can still see notifications by clicking the Do not disturb icon on the bottom right of your screen.
+   <message name="IDS_OS_SETTINGS_REVAMP_APP_NOTIFICATIONS_DO_NOT_DISTURB_TOGGLE_DESCRIPTION" desc="The text description of the Do Not Disturb toggle within help icon tooltip.">
+    Notifications won't pop up on the screen. You can still see notifications by clicking the Do Not Disturb icon on the bottom right of your screen.
   </message>
    <message name="IDS_SETTINGS_APP_NOTIFICATIONS_DO_NOT_DISTURB_TOGGLE_DESCRIPTION" desc="The text description of the DoNotDisturb toggle within help icon tooltip.">
     When turned on, all notifications will be silenced
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_REVAMP_APP_NOTIFICATIONS_DO_NOT_DISTURB_TOGGLE_DESCRIPTION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_REVAMP_APP_NOTIFICATIONS_DO_NOT_DISTURB_TOGGLE_DESCRIPTION.png.sha1
index d312dc43..b2ab422 100644
--- a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_REVAMP_APP_NOTIFICATIONS_DO_NOT_DISTURB_TOGGLE_DESCRIPTION.png.sha1
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_REVAMP_APP_NOTIFICATIONS_DO_NOT_DISTURB_TOGGLE_DESCRIPTION.png.sha1
@@ -1 +1 @@
-1b66d021ae577781dced9ace5e22a7f70d7d4c2a
\ No newline at end of file
+daedc490ec4253b640cfb7354e7fc67c838c5d0d
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_REVAMP_APP_NOTIFICATIONS_LINK_DESCRIPTION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_REVAMP_APP_NOTIFICATIONS_LINK_DESCRIPTION.png.sha1
index b9f826b..874ae4c 100644
--- a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_REVAMP_APP_NOTIFICATIONS_LINK_DESCRIPTION.png.sha1
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_REVAMP_APP_NOTIFICATIONS_LINK_DESCRIPTION.png.sha1
@@ -1 +1 @@
-06f07757e7f8b161ef96a7ea18b49b64686a39be
\ No newline at end of file
+3fd990a281d40c88db86313fd89a8779d08db482
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APP_NOTIFICATIONS_DND_ENABLED_SUBLABEL_TEXT.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APP_NOTIFICATIONS_DND_ENABLED_SUBLABEL_TEXT.png.sha1
index 736b194..26183bf 100644
--- a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APP_NOTIFICATIONS_DND_ENABLED_SUBLABEL_TEXT.png.sha1
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APP_NOTIFICATIONS_DND_ENABLED_SUBLABEL_TEXT.png.sha1
@@ -1 +1 @@
-745e410272de6810c461f5a04b90430a08644c26
\ No newline at end of file
+96d02e566a05b47ea1b7ad614e410a3dddb82821
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APP_NOTIFICATIONS_DO_NOT_DISTURB_TOGGLE_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APP_NOTIFICATIONS_DO_NOT_DISTURB_TOGGLE_TITLE.png.sha1
index 7a9920f..2d164d7b 100644
--- a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APP_NOTIFICATIONS_DO_NOT_DISTURB_TOGGLE_TITLE.png.sha1
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APP_NOTIFICATIONS_DO_NOT_DISTURB_TOGGLE_TITLE.png.sha1
@@ -1 +1 @@
-a466614011dd4b62a89ad71c63b430d4cab561da
\ No newline at end of file
+5895858e0482de07cc414e68cfa930d0bff693fa
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 8e919e7..e2033c2 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4063,6 +4063,8 @@
       "new_tab_page/modules/history_clusters/history_clusters_page_handler.cc",
       "new_tab_page/modules/history_clusters/history_clusters_page_handler.h",
       "new_tab_page/modules/history_clusters/ranking/history_cluster_metrics.h",
+      "new_tab_page/modules/history_clusters/ranking/history_clusters_category_metrics.cc",
+      "new_tab_page/modules/history_clusters/ranking/history_clusters_category_metrics.h",
       "new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranker.cc",
       "new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranker.h",
       "new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_metrics_logger.cc",
@@ -5316,6 +5318,7 @@
       "//chromeos/ash/components/dbus/update_engine",
       "//chromeos/ash/components/dbus/userdataauth:userdataauth",
       "//chromeos/ash/components/dbus/userdataauth:userdataauth_proto",
+      "//chromeos/ash/components/emoji:mojo_bindings",
       "//chromeos/ash/components/feature_usage",
       "//chromeos/ash/components/geolocation",
       "//chromeos/ash/components/install_attributes",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 15d1ba8..61353e1 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1455,11 +1455,12 @@
         {"enable_scoring_signals_annotators_for_ml_scoring", "true"},
         {"MlUrlScoringShortcutDocumentSignals", "true"},
 };
-// Sets Bookmark(1), History Quick(4), History URL(8), Shortcuts(64), and
-// History Fuzzy(65536) providers max matches to 10.
+// Sets Bookmark(1), History Quick(4), History URL(8), Shortcuts(64),
+// Document(512), and History Fuzzy(65536) providers max matches to 10.
 const FeatureEntry::FeatureParam kOmniboxMlUrlScoringMaxMatchesByProvider10[] =
     {
-        {"MlUrlScoringMaxMatchesByProvider", "1:10,4:10,8:10,64:10,65536:10"},
+        {"MlUrlScoringMaxMatchesByProvider",
+         "1:10,4:10,8:10,64:10,512:10,65536:10"},
         {"enable_scoring_signals_annotators_for_ml_scoring", "true"},
         {"MlUrlScoringShortcutDocumentSignals", "true"},
 };
@@ -3758,6 +3759,16 @@
         {"V2", kCompressionDictionaryTransportBackendVersionV2,
          std::size(kCompressionDictionaryTransportBackendVersionV2), nullptr}};
 
+#if BUILDFLAG(IS_ANDROID)
+const FeatureEntry::Choice kAccountBookmarksAndReadingListBehindOptInChoices[] =
+    {
+        {"Default", "", ""},
+        {"Enabled", switches::kEnableFeatures,
+         "EnableBookmarkFoldersForAccountStorage,"
+         "ReadingListEnableSyncTransportModeUponSignIn"},
+};
+#endif  // BUILDFLAG(IS_ANDROID)
+
 // RECORDING USER METRICS FOR FLAGS:
 // -----------------------------------------------------------------------------
 // The first line of the entry is the internal name.
@@ -4300,9 +4311,6 @@
     {"show-touch-hud", flag_descriptions::kShowTouchHudName,
      flag_descriptions::kShowTouchHudDescription, kOsCrOS,
      SINGLE_VALUE_TYPE(ash::switches::kAshTouchHud)},
-    {"stylus-battery-status", flag_descriptions::kStylusBatteryStatusName,
-     flag_descriptions::kStylusBatteryStatusDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(ash::features::kStylusBatteryStatus)},
     {"suppress-text-messages", flag_descriptions::kSuppressTextMessagesName,
      flag_descriptions::kSuppressTextMessagesDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ash::features::kSuppressTextMessages)},
@@ -4915,7 +4923,18 @@
      flag_descriptions::kWebAppSyncGeneratedIconUpdateFixDescription,
      kOsDesktop,
      FEATURE_VALUE_TYPE(features::kWebAppSyncGeneratedIconUpdateFix)},
+    {"web-app-universal-install",
+     flag_descriptions::kWebAppUniversalInstallName,
+     flag_descriptions::kWebAppUniversalInstallDescription, kOsDesktop,
+     FEATURE_VALUE_TYPE(features::kWebAppUniversalInstall)},
 #endif  // !BUILDFLAG(IS_ANDROID)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
+    BUILDFLAG(IS_FUCHSIA)
+    {"shortcuts-not-apps", flag_descriptions::kShortcutsNotAppsName,
+     flag_descriptions::kShortcutsNotAppsDescription,
+     kOsMac | kOsWin | kOsLinux | kOsFuchsia,
+     FEATURE_VALUE_TYPE(features::kShortcutsNotApps)},
+#endif
 #if BUILDFLAG(IS_CHROMEOS)
     {"web-app-user-display-mode-sync-browser-mitigation",
      flag_descriptions::kUserDisplayModeSyncBrowserMitigationName,
@@ -7872,10 +7891,6 @@
      FEATURE_VALUE_TYPE(features::kUseClientGmbInterface)},
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-    {"contextual-nudges", flag_descriptions::kContextualNudgesName,
-     flag_descriptions::kContextualNudgesDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(ash::features::kContextualNudges)},
-
     {"scalable-iph-debug", flag_descriptions::kScalableIphDebugName,
      flag_descriptions::kScalableIphDebugDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ash::features::kScalableIphDebug)},
@@ -8193,10 +8208,6 @@
      flag_descriptions::kClipboardHistoryUrlTitlesName,
      flag_descriptions::kClipboardHistoryUrlTitlesDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ash::features::kClipboardHistoryUrlTitles)},
-    {"clipboard-history-web-contents-paste",
-     flag_descriptions::kClipboardHistoryWebContentsPasteName,
-     flag_descriptions::kClipboardHistoryWebContentsPasteDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(ash::features::kClipboardHistoryWebContentsPaste)},
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if BUILDFLAG(IS_WIN)
@@ -11080,11 +11091,11 @@
 #endif  // BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(IS_ANDROID)
-    {"enable-bookmark-folders-for-account-storage",
-     flag_descriptions::kEnableBookmarkFoldersForAccountStorageName,
-     flag_descriptions::kEnableBookmarkFoldersForAccountStorageDescription,
+    {"bookmarks-and-reading-list-behind-opt-in",
+     flag_descriptions::kAccountBookmarksAndReadingListBehindOptInName,
+     flag_descriptions::kAccountBookmarksAndReadingListBehindOptInDescription,
      kOsAndroid,
-     FEATURE_VALUE_TYPE(syncer::kEnableBookmarkFoldersForAccountStorage)},
+     MULTI_VALUE_TYPE(kAccountBookmarksAndReadingListBehindOptInChoices)},
 #endif  // BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/android/send_tab_to_self/android_notification_handler.cc b/chrome/browser/android/send_tab_to_self/android_notification_handler.cc
index 230eb9bc..95df8162 100644
--- a/chrome/browser/android/send_tab_to_self/android_notification_handler.cc
+++ b/chrome/browser/android/send_tab_to_self/android_notification_handler.cc
@@ -92,7 +92,7 @@
 
 void AndroidNotificationHandler::DisplayNewEntries(
     const std::vector<const SendTabToSelfEntry*>& new_entries) {
-  std::vector<const SendTabToSelfEntry> vector_copy;
+  std::vector<SendTabToSelfEntry> vector_copy;
 
   for (const SendTabToSelfEntry* entry : new_entries) {
     vector_copy.push_back(*entry);
@@ -105,7 +105,7 @@
 }
 
 void AndroidNotificationHandler::DisplayNewEntriesOnUIThread(
-    const std::vector<const SendTabToSelfEntry>& new_entries) {
+    const std::vector<SendTabToSelfEntry>& new_entries) {
   for (const SendTabToSelfEntry& entry : new_entries) {
     if (base::FeatureList::IsEnabled(send_tab_to_self::kSendTabToSelfV2)) {
       if (profile_ != nullptr &&
diff --git a/chrome/browser/android/send_tab_to_self/android_notification_handler.h b/chrome/browser/android/send_tab_to_self/android_notification_handler.h
index 00ea3d9..5020de53 100644
--- a/chrome/browser/android/send_tab_to_self/android_notification_handler.h
+++ b/chrome/browser/android/send_tab_to_self/android_notification_handler.h
@@ -38,7 +38,7 @@
 
  private:
   void DisplayNewEntriesOnUIThread(
-      const std::vector<const SendTabToSelfEntry>& new_entries);
+      const std::vector<SendTabToSelfEntry>& new_entries);
 
   // ReceivingUiHandler implementation.
   void DisplayNewEntries(
diff --git a/chrome/browser/apps/app_service/metrics/app_discovery_metrics.cc b/chrome/browser/apps/app_service/metrics/app_discovery_metrics.cc
index 8b66ef2..d9d2d8f0 100644
--- a/chrome/browser/apps/app_service/metrics/app_discovery_metrics.cc
+++ b/chrome/browser/apps/app_service/metrics/app_discovery_metrics.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/apps/app_service/metrics/app_discovery_metrics.h"
 
+#include <utility>
+
 #include "base/logging.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_ash.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
@@ -14,6 +16,7 @@
 #include "chrome/browser/ash/guest_os/guest_os_registry_service_factory.h"
 #include "chrome/browser/sync/sync_service_factory.h"
 #include "components/metrics/structured/structured_events.h"
+#include "components/metrics/structured/structured_metrics_client.h"
 #include "components/prefs/pref_service.h"
 #include "components/services/app_service/public/cpp/instance.h"
 #include "components/services/app_service/public/cpp/package_id.h"
@@ -91,7 +94,7 @@
       .SetAppType(static_cast<int>(app_type))
       .SetInstallSource(static_cast<int>(app_install_source))
       .SetInstallReason(static_cast<int>(app_install_reason));
-  event.Record();
+  metrics::structured::StructuredMetricsClient::Record(std::move(event));
 }
 
 void AppDiscoveryMetrics::OnAppLaunched(const std::string& app_id,
@@ -106,7 +109,7 @@
   event.SetAppId(GetAppStringToRecord(app_id, app_type))
       .SetAppType(static_cast<int>(app_type))
       .SetLaunchSource(static_cast<int>(launch_source));
-  event.Record();
+  metrics::structured::StructuredMetricsClient::Record(std::move(event));
 }
 
 void AppDiscoveryMetrics::OnAppUninstalled(
@@ -129,7 +132,7 @@
   event.SetAppId(app_str_to_record)
       .SetAppType(static_cast<int>(app_type))
       .SetUninstallSource(static_cast<int>(app_uninstall_source));
-  event.Record();
+  metrics::structured::StructuredMetricsClient::Record(std::move(event));
 }
 
 void AppDiscoveryMetrics::OnAppPlatformMetricsDestroyed() {
@@ -281,22 +284,22 @@
 
 void AppDiscoveryMetrics::RecordAppActive(
     const InstanceUpdate& instance_update) {
-  cros_events::AppDiscovery_AppStateChanged()
-      .SetAppId(
-          GetAppStringToRecord(instance_update.AppId(),
-                               GetAppType(profile_, instance_update.AppId())))
-      .SetAppState(static_cast<int>(AppStateChange::kActive))
-      .Record();
+  metrics::structured::StructuredMetricsClient::Record(
+      std::move(cros_events::AppDiscovery_AppStateChanged()
+                    .SetAppId(GetAppStringToRecord(
+                        instance_update.AppId(),
+                        GetAppType(profile_, instance_update.AppId())))
+                    .SetAppState(static_cast<int>(AppStateChange::kActive))));
 }
 
 void AppDiscoveryMetrics::RecordAppInactive(
     const InstanceUpdate& instance_update) {
-  cros_events::AppDiscovery_AppStateChanged()
-      .SetAppId(
-          GetAppStringToRecord(instance_update.AppId(),
-                               GetAppType(profile_, instance_update.AppId())))
-      .SetAppState(static_cast<int>(AppStateChange::kInactive))
-      .Record();
+  metrics::structured::StructuredMetricsClient::Record(
+      std::move(cros_events::AppDiscovery_AppStateChanged()
+                    .SetAppId(GetAppStringToRecord(
+                        instance_update.AppId(),
+                        GetAppType(profile_, instance_update.AppId())))
+                    .SetAppState(static_cast<int>(AppStateChange::kInactive))));
 }
 
 void AppDiscoveryMetrics::RecordAppClosed(
@@ -306,12 +309,12 @@
 
   // If instance_update is the only instance of the app.
   if (prev_instances.size() == 1) {
-    cros_events::AppDiscovery_AppStateChanged()
-        .SetAppId(
-            GetAppStringToRecord(instance_update.AppId(),
-                                 GetAppType(profile_, instance_update.AppId())))
-        .SetAppState(static_cast<int>(AppStateChange::kClosed))
-        .Record();
+    metrics::structured::StructuredMetricsClient::Record(
+        std::move(cros_events::AppDiscovery_AppStateChanged()
+                      .SetAppId(GetAppStringToRecord(
+                          instance_update.AppId(),
+                          GetAppType(profile_, instance_update.AppId())))
+                      .SetAppState(static_cast<int>(AppStateChange::kClosed))));
   }
 }
 
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn
index 69df27ee..b06b3fa 100644
--- a/chrome/browser/ash/BUILD.gn
+++ b/chrome/browser/ash/BUILD.gn
@@ -1499,6 +1499,8 @@
     "input_method/component_extension_ime_manager_delegate_impl.h",
     "input_method/diacritics_insensitive_string_comparator.cc",
     "input_method/diacritics_insensitive_string_comparator.h",
+    "input_method/editor_announcer.cc",
+    "input_method/editor_announcer.h",
     "input_method/editor_client_connector.cc",
     "input_method/editor_client_connector.h",
     "input_method/editor_consent_enums.cc",
diff --git a/chrome/browser/ash/app_list/search/essential_search/essential_search_manager.cc b/chrome/browser/ash/app_list/search/essential_search/essential_search_manager.cc
index 92e190f..e2bb49f 100644
--- a/chrome/browser/ash/app_list/search/essential_search/essential_search_manager.cc
+++ b/chrome/browser/ash/app_list/search/essential_search/essential_search_manager.cc
@@ -18,6 +18,7 @@
 #include "content/public/browser/storage_partition.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "net/cookies/canonical_cookie.h"
+#include "net/cookies/cookie_access_result.h"
 #include "net/cookies/cookie_monster.h"
 #include "net/cookies/cookie_options.h"
 #include "services/network/public/mojom/cookie_manager.mojom.h"
@@ -96,26 +97,26 @@
   PrefService* prefs = primary_profile_->GetPrefs();
   if (prefs->GetBoolean(prefs::kEssentialSearchEnabled)) {
     // If the policy is enabled, cancel all active requests then fetch SOCS
-    // cookie and Update value of kLastEssentialSearchValue prefs.
+    // cookie.
     CancelPendingRequests();
     socs_cookie_fetcher_ = std::make_unique<SocsCookieFetcher>(
         primary_profile_->GetURLLoaderFactory(), this);
     socs_cookie_fetcher_->StartFetching();
-    prefs->SetBoolean(prefs::kLastEssentialSearchValue, true);
     return;
   }
 
   if (prefs->GetBoolean(prefs::kLastEssentialSearchValue)) {
-    // If the policy was disabled, cancel all active request then remove SOCS
-    // cookie from the user's profile.
-    CancelPendingRequests();
-    socs_cookie_fetcher_.reset();
+    // If the policy was disabled, remove SOCS cookie from the user's profile.
     RemoveSocsCookie();
     return;
   }
 }
 
 void EssentialSearchManager::RemoveSocsCookie() {
+  // Before removing the cookie, cancel all active request
+  CancelPendingRequests();
+  socs_cookie_fetcher_.reset();
+
   network::mojom::CookieDeletionFilterPtr filter =
       network::mojom::CookieDeletionFilter::New();
 
@@ -153,15 +154,40 @@
     return;
   }
 
-  retry_backoff_.InformOfRequest(true);
-  RefetchAfter(kOneDay);
   const net::CookieOptions options = net::CookieOptions::MakeAllInclusive();
 
   primary_profile_->GetDefaultStoragePartition()
       ->GetCookieManagerForBrowserProcess()
       ->SetCanonicalCookie(
           *cc, google_url, options,
-          network::mojom::CookieManager::SetCanonicalCookieCallback());
+          base::BindOnce(&EssentialSearchManager::OnCookieAddedToUserProfile,
+                         weak_ptr_factory_.GetWeakPtr()));
+}
+
+void EssentialSearchManager::OnCookieAddedToUserProfile(
+    net::CookieAccessResult result) {
+  if (!result.status.IsInclude()) {
+    // Handle SOCS cookie insertion failure.
+    LOG(WARNING) << "Failed to add SOCS cookie to user profile. Retrying.";
+    LOG(WARNING) << "Status=" << result.status;
+    OnApiCallFailed(SocsCookieFetcher::Status::kCookieInsertionFailure);
+    return;
+  }
+
+  // After the SOCS cookie is added to the user profile, Schedule a SOCS cookie
+  // refresh to ensure a valid SOCS cookie is maintained.
+  RefetchAfter(kOneDay);
+  retry_backoff_.InformOfRequest(true);
+  PrefService* prefs = primary_profile_->GetPrefs();
+  if (prefs->GetBoolean(prefs::kEssentialSearchEnabled)) {
+    // If the kEssentialSearchEnabled policy is still enabled, mark
+    // kLastEssentialSearchValue as enabled.
+    prefs->SetBoolean(prefs::kLastEssentialSearchValue, true);
+  } else {
+    // This would be triggered in case the policy got disabled while storing the
+    // cookie.
+    RemoveSocsCookie();
+  }
 }
 
 void EssentialSearchManager::RefetchAfter(base::TimeDelta delay) {
@@ -179,7 +205,6 @@
   fetch_requests_weak_factory_.InvalidateWeakPtrs();
 }
 
-
 void EssentialSearchManager::OnApiCallFailed(SocsCookieFetcher::Status status) {
   // TODO(b/312542928): collect UMA with the error type.
   retry_backoff_.InformOfRequest(false);
diff --git a/chrome/browser/ash/app_list/search/essential_search/essential_search_manager.h b/chrome/browser/ash/app_list/search/essential_search/essential_search_manager.h
index 124d010..5255c55 100644
--- a/chrome/browser/ash/app_list/search/essential_search/essential_search_manager.h
+++ b/chrome/browser/ash/app_list/search/essential_search/essential_search_manager.h
@@ -16,6 +16,7 @@
 #include "chrome/browser/ash/app_list/search/essential_search/socs_cookie_fetcher.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "net/base/backoff_entry.h"
+#include "net/cookies/cookie_access_result.h"
 
 class Profile;
 
@@ -54,6 +55,10 @@
  private:
   void MaybeFetchSocsCookie();
 
+  // Callback function to be called after when a SOCS cookie is added to a
+  // user's profile.
+  void OnCookieAddedToUserProfile(net::CookieAccessResult result);
+
   void RemoveSocsCookie();
 
   void OnCookieDeleted(uint32_t number_of_cookies_deleted);
@@ -79,7 +84,8 @@
 
   base::WeakPtrFactory<EssentialSearchManager> weak_ptr_factory_{this};
 
-  base::WeakPtrFactory<EssentialSearchManager> fetch_requests_weak_factory_{this};
+  base::WeakPtrFactory<EssentialSearchManager> fetch_requests_weak_factory_{
+      this};
 };
 
 }  // namespace app_list
diff --git a/chrome/browser/ash/app_list/search/essential_search/socs_cookie_fetcher.h b/chrome/browser/ash/app_list/search/essential_search/socs_cookie_fetcher.h
index 7c5e2e0..db190239 100644
--- a/chrome/browser/ash/app_list/search/essential_search/socs_cookie_fetcher.h
+++ b/chrome/browser/ash/app_list/search/essential_search/socs_cookie_fetcher.h
@@ -23,15 +23,18 @@
 // SocsCookieFetcher is still WIP.
 class SocsCookieFetcher final {
  public:
+  // TODO(b/312542928): use this enum to collect UMA.
   enum class Status {
-    kOk,
-    kRequestBodyNotSerialized,
-    kServerError,
-    kEmptyResponse,
-    kJsonParseFailure,
-    kNotJsonDict,
-    kFetchNoCookie,
-    kInvalidCookie
+    kOk = 0,
+    kRequestBodyNotSerialized = 1,
+    kServerError = 2,
+    kEmptyResponse = 3,
+    kJsonParseFailure = 4,
+    kNotJsonDict = 5,
+    kFetchNoCookie = 6,
+    kInvalidCookie = 7,
+    kCookieInsertionFailure = 8,
+    kMaxValue = kCookieInsertionFailure
   };
 
   class Consumer {
diff --git a/chrome/browser/ash/app_mode/repeating_time_interval_task_executor.cc b/chrome/browser/ash/app_mode/repeating_time_interval_task_executor.cc
index 63e4e1e8..9cb77849 100644
--- a/chrome/browser/ash/app_mode/repeating_time_interval_task_executor.cc
+++ b/chrome/browser/ash/app_mode/repeating_time_interval_task_executor.cc
@@ -4,10 +4,17 @@
 
 #include "chrome/browser/ash/app_mode/repeating_time_interval_task_executor.h"
 
+#include "base/functional/callback.h"
+
 namespace ash {
 
-RepeatingTimeIntervalTaskExecutor::RepeatingTimeIntervalTaskExecutor() =
-    default;
+RepeatingTimeIntervalTaskExecutor::RepeatingTimeIntervalTaskExecutor(
+    const policy::WeeklyTimeInterval& time_interval,
+    base::RepeatingClosure interval_start_callback,
+    base::RepeatingClosure interval_end_callback)
+    : time_interval_(time_interval),
+      interval_start_callback_(interval_start_callback),
+      interval_end_callback_(interval_end_callback) {}
 
 RepeatingTimeIntervalTaskExecutor::~RepeatingTimeIntervalTaskExecutor() =
     default;
diff --git a/chrome/browser/ash/app_mode/repeating_time_interval_task_executor.h b/chrome/browser/ash/app_mode/repeating_time_interval_task_executor.h
index 35331b9..670c6f6 100644
--- a/chrome/browser/ash/app_mode/repeating_time_interval_task_executor.h
+++ b/chrome/browser/ash/app_mode/repeating_time_interval_task_executor.h
@@ -5,20 +5,27 @@
 #ifndef CHROME_BROWSER_ASH_APP_MODE_REPEATING_TIME_INTERVAL_TASK_EXECUTOR_H_
 #define CHROME_BROWSER_ASH_APP_MODE_REPEATING_TIME_INTERVAL_TASK_EXECUTOR_H_
 
+#include "base/functional/callback.h"
+#include "chromeos/ash/components/policy/weekly_time/weekly_time_interval.h"
+
 namespace ash {
 
-// `RepeatingTimeIntervalTaskExecutor` takes a time interval and a set of
-// repeating callbacks. When the device enters and exits the specified time
-// interval, this class invokes the provided start and end callbacks. This class
-// schedules the time interval using the system timezone. Changes to the system
-// timezone will make it reprogram the time interval.
-// TODO(b/319083748) Add constructor to accept intervals and callbacks.
+// When the device enters and exits the specified time interval, this class
+// invokes the provided `interval_start_callback` callback and
+// `interval_end_callback` callback respectively. This class schedules the time
+// interval using the system timezone. Changes to the system timezone will make
+// it reprogram the time interval.
 // TODO(b/319087271) Implement case when current time falls in interval.
 // TODO(b/319086751) Implement case when next interval is in the future.
 // TODO(b/319083880) Observe time zone changes and cancel pending executors.
 class RepeatingTimeIntervalTaskExecutor {
  public:
-  RepeatingTimeIntervalTaskExecutor();
+  RepeatingTimeIntervalTaskExecutor() = delete;
+
+  RepeatingTimeIntervalTaskExecutor(
+      const policy::WeeklyTimeInterval& time_interval,
+      base::RepeatingClosure interval_start_callback,
+      base::RepeatingClosure interval_end_callback);
 
   RepeatingTimeIntervalTaskExecutor(const RepeatingTimeIntervalTaskExecutor&) =
       delete;
@@ -26,6 +33,11 @@
       const RepeatingTimeIntervalTaskExecutor&) = delete;
 
   ~RepeatingTimeIntervalTaskExecutor();
+
+ private:
+  const policy::WeeklyTimeInterval time_interval_;
+  const base::RepeatingClosure interval_start_callback_;
+  const base::RepeatingClosure interval_end_callback_;
 };
 
 }  // namespace ash
diff --git a/chrome/browser/ash/arc/input_overlay/ui/target_view.cc b/chrome/browser/ash/arc/input_overlay/ui/target_view.cc
index 041fbe1..5a06e16 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/target_view.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/target_view.cc
@@ -118,13 +118,17 @@
   }
 }
 
+int TargetView::GetPadding() const {
+  return TouchPoint::GetEdgeLength(action_type_) / 2;
+}
+
 void TargetView::ClampCenter() {
-  const int circle_ring_radius = GetCircleRadius();
+  const int padding = GetPadding();
   const auto& view_size = size();
-  center_.set_x(std::clamp(center_.x(), /*lo=*/circle_ring_radius,
-                           view_size.width() - circle_ring_radius));
-  center_.set_y(std::clamp(center_.y(), /*lo=*/circle_ring_radius,
-                           view_size.height() - circle_ring_radius));
+  center_.set_x(std::clamp(center_.x(), /*lo=*/padding,
+                           /*hi=*/view_size.width() - padding));
+  center_.set_y(std::clamp(center_.y(), /*lo=*/padding,
+                           /*hi=*/view_size.height() - padding));
 }
 
 void TargetView::OnCenterChanged() {
diff --git a/chrome/browser/ash/arc/input_overlay/ui/target_view.h b/chrome/browser/ash/arc/input_overlay/ui/target_view.h
index 2c80c69d3..eb9a9ec9 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/target_view.h
+++ b/chrome/browser/ash/arc/input_overlay/ui/target_view.h
@@ -39,6 +39,8 @@
   int GetCircleRadius() const;
   // The target circle ring radius excluding the ring thickness.
   int GetCircleRingRadius() const;
+  // The padding that `center_` can't access.
+  int GetPadding() const;
 
   // Clamps `center_` so that the target circle can show completely and
   // constraint the action position.
diff --git a/chrome/browser/ash/arc/input_overlay/ui/target_view_unittest.cc b/chrome/browser/ash/arc/input_overlay/ui/target_view_unittest.cc
index 8efaaf92..199351a 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/target_view_unittest.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/target_view_unittest.cc
@@ -27,9 +27,9 @@
     return target->center_;
   }
 
-  int GetTargetRadius(TargetView* target) {
+  int GetTargetPadding(TargetView* target) {
     DCHECK(target);
-    return target->GetCircleRadius();
+    return target->GetPadding();
   }
 
   // Convert the point in `TargetView` coordinates to screen coordinates.
@@ -118,7 +118,7 @@
   // inside and show the complete circle.
   target_view = GetTargetView();
   EXPECT_TRUE(target_view);
-  EXPECT_EQ(GetTargetCenter(target_view).x(), GetTargetRadius(target_view));
+  EXPECT_EQ(GetTargetCenter(target_view).x(), GetTargetPadding(target_view));
 }
 
 TEST_F(TargetViewTest, TestKeyboardSupport) {
diff --git a/chrome/browser/ash/arc/input_overlay/ui/touch_point.cc b/chrome/browser/ash/arc/input_overlay/ui/touch_point.cc
index 0044d70c..916b2e36 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/touch_point.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/touch_point.cc
@@ -292,21 +292,28 @@
   return touch_point_ptr;
 }
 
-gfx::Size TouchPoint::GetSize(ActionType action_type) {
-  int size = 0;
+// static
+int TouchPoint::GetEdgeLength(ActionType action_type) {
+  int length = 0;
   switch (action_type) {
     case ActionType::TAP:
-      size = kDotCenterDiameter + kDotInsideStrokeThickness * 2 +
-             kDotOutsideStrokeThickness * 2;
+      length = kDotCenterDiameter + kDotInsideStrokeThickness * 2 +
+               kDotOutsideStrokeThickness * 2;
       break;
     case ActionType::MOVE:
-      size = kCrossCenterLength + kCrossInsideStrokeThickness * 2 +
-             kCrossOutsideStrokeThickness * 2;
+      length = kCrossCenterLength + kCrossInsideStrokeThickness * 2 +
+               kCrossOutsideStrokeThickness * 2;
       break;
     default:
       NOTREACHED();
   }
-  return gfx::Size(size, size);
+  return length;
+}
+
+// static
+gfx::Size TouchPoint::GetSize(ActionType action_type) {
+  const int edge_length = GetEdgeLength(action_type);
+  return gfx::Size(edge_length, edge_length);
 }
 
 // static
diff --git a/chrome/browser/ash/arc/input_overlay/ui/touch_point.h b/chrome/browser/ash/arc/input_overlay/ui/touch_point.h
index b550dc3..3f3605f0 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/touch_point.h
+++ b/chrome/browser/ash/arc/input_overlay/ui/touch_point.h
@@ -29,6 +29,10 @@
   static TouchPoint* Show(views::View* parent,
                           ActionType action_type,
                           const gfx::Point& center_pos);
+
+  // Gets the overall edge length of the squared view of `action_type`.
+  static int GetEdgeLength(ActionType action_type);
+  // Gets the overall size of the squared view of `action_type`.
   static gfx::Size GetSize(ActionType action_type);
 
   // Draws TouchPoint of `action_type` on `canvas`. `ui_state` is related
diff --git a/chrome/browser/ash/input_method/editor_announcer.cc b/chrome/browser/ash/input_method/editor_announcer.cc
new file mode 100644
index 0000000..60022bc
--- /dev/null
+++ b/chrome/browser/ash/input_method/editor_announcer.cc
@@ -0,0 +1,53 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/input_method/editor_announcer.h"
+
+#include "ash/public/cpp/shell_window_ids.h"
+#include "ash/shell.h"
+#include "ash/wm/window_util.h"
+
+namespace ash::input_method {
+namespace {
+
+gfx::NativeView GetParentViewFromRootWindow() {
+  aura::Window* active_window = ash::window_util::GetActiveWindow();
+  return ash::Shell::GetContainer(
+      active_window ? active_window->GetRootWindow()
+                    : ash::Shell::GetRootWindowForNewWindows(),
+      ash::kShellWindowId_MenuContainer);
+}
+
+}  // namespace
+
+void EditorLiveRegionAnnouncer::Announce(const std::u16string& message) {
+  live_region_.Announce(message);
+}
+
+void EditorLiveRegionAnnouncer::LiveRegion::Announce(
+    const std::u16string& message) {
+  if (announcement_view_ == nullptr) {
+    CreateAnnouncementView();
+  }
+  announcement_view_->Announce(message);
+}
+
+void EditorLiveRegionAnnouncer::LiveRegion::OnWidgetDestroying(
+    views::Widget* widget) {
+  if (announcement_view_ != nullptr &&
+      widget == announcement_view_->GetWidget()) {
+    widget->RemoveObserver(this);
+    announcement_view_ = nullptr;
+  }
+}
+
+void EditorLiveRegionAnnouncer::LiveRegion::CreateAnnouncementView() {
+  // This view's lifetime is handled by DialogDelegateView which it inherits
+  // from and thus will not leak here without a corresponding delete.
+  announcement_view_ =
+      new ui::ime::AnnouncementView(GetParentViewFromRootWindow());
+  announcement_view_->GetWidget()->AddObserver(this);
+}
+
+}  // namespace ash::input_method
diff --git a/chrome/browser/ash/input_method/editor_announcer.h b/chrome/browser/ash/input_method/editor_announcer.h
new file mode 100644
index 0000000..07bd0f0
--- /dev/null
+++ b/chrome/browser/ash/input_method/editor_announcer.h
@@ -0,0 +1,50 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_INPUT_METHOD_EDITOR_ANNOUNCER_H_
+#define CHROME_BROWSER_ASH_INPUT_METHOD_EDITOR_ANNOUNCER_H_
+
+#include <string>
+
+#include "base/memory/raw_ptr.h"
+#include "chrome/browser/ash/input_method/ui/announcement_view.h"
+#include "ui/views/widget/widget_observer.h"
+
+namespace ash::input_method {
+
+// An interface used to trigger ChromeVox announcements.
+class EditorAnnouncer {
+ public:
+  virtual void Announce(const std::u16string& message) = 0;
+};
+
+class EditorLiveRegionAnnouncer : public EditorAnnouncer {
+ public:
+  // EditorAnnouncer overrides
+  void Announce(const std::u16string& message) override;
+
+ private:
+  class LiveRegion : public views::WidgetObserver {
+   public:
+    // Triggers a ChromeVox announcement via the live region view.
+    void Announce(const std::u16string& message);
+
+    // WidgetObserver overrides
+    void OnWidgetDestroying(views::Widget* widget) override;
+
+   private:
+    void CreateAnnouncementView();
+
+    // Holds the view used to trigger announcements with ChromeVox. This is
+    // a raw_ptr due to the lifetime of the instance being handled by the
+    // DialogDelegateView the class inherits from.
+    raw_ptr<ui::ime::AnnouncementView> announcement_view_ = nullptr;
+  };
+
+  LiveRegion live_region_;
+};
+
+}  // namespace ash::input_method
+
+#endif  // CHROME_BROWSER_ASH_INPUT_METHOD_EDITOR_ANNOUNCER_H_
diff --git a/chrome/browser/ash/input_method/editor_mediator.cc b/chrome/browser/ash/input_method/editor_mediator.cc
index 112bcea..8583782 100644
--- a/chrome/browser/ash/input_method/editor_mediator.cc
+++ b/chrome/browser/ash/input_method/editor_mediator.cc
@@ -150,6 +150,10 @@
   editor_switch_->OnTextSelectionLengthChanged(selected_length);
 }
 
+void EditorMediator::Announce(const std::u16string& message) {
+  announcer_.Announce(message);
+}
+
 void EditorMediator::ProcessConsentAction(ConsentAction consent_action) {
   consent_store_->ProcessConsentAction(consent_action);
   HandleTrigger(/*preset_query_id=*/std::nullopt,
diff --git a/chrome/browser/ash/input_method/editor_mediator.h b/chrome/browser/ash/input_method/editor_mediator.h
index d446190b..1a4da1f 100644
--- a/chrome/browser/ash/input_method/editor_mediator.h
+++ b/chrome/browser/ash/input_method/editor_mediator.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_ASH_INPUT_METHOD_EDITOR_MEDIATOR_H_
 
 #include "base/scoped_observation.h"
+#include "chrome/browser/ash/input_method/editor_announcer.h"
 #include "chrome/browser/ash/input_method/editor_client_connector.h"
 #include "chrome/browser/ash/input_method/editor_consent_store.h"
 #include "chrome/browser/ash/input_method/editor_event_proxy.h"
@@ -80,6 +81,7 @@
   void OnDisplayTabletStateChanged(display::TabletState state) override;
 
   // EditorSystemActuator::System overrides
+  void Announce(const std::u16string& message) override;
   void ProcessConsentAction(ConsentAction consent_action) override;
   void ShowUI() override;
   void CloseUI() override;
@@ -121,6 +123,7 @@
   std::unique_ptr<EditorMetricsRecorder> metrics_recorder_;
   std::unique_ptr<EditorConsentStore> consent_store_;
   EditorServiceConnector editor_service_connector_;
+  EditorLiveRegionAnnouncer announcer_;
 
   // TODO: b:298285960 - add the instantiation of this instance.
   std::unique_ptr<EditorEventProxy> editor_event_proxy_;
diff --git a/chrome/browser/ash/input_method/editor_system_actuator.cc b/chrome/browser/ash/input_method/editor_system_actuator.cc
index a72fdbe..18c7d9c 100644
--- a/chrome/browser/ash/input_method/editor_system_actuator.cc
+++ b/chrome/browser/ash/input_method/editor_system_actuator.cc
@@ -7,6 +7,9 @@
 #include <memory>
 
 #include "ash/public/cpp/new_window_delegate.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
 #include "chrome/browser/ash/input_method/editor_feedback.h"
 #include "chrome/browser/ash/input_method/editor_metrics_enums.h"
 #include "chrome/browser/ash/input_method/editor_metrics_recorder.h"
@@ -16,6 +19,10 @@
 namespace ash::input_method {
 namespace {
 
+constexpr base::TimeDelta kAnnouncementDelay = base::Milliseconds(200);
+constexpr char16_t kAnnouncementMessage[] =
+    u"Replacing selected text with suggestion";
+
 bool IsUrlAllowed(const GURL& url) {
   return url.SchemeIs(url::kHttpsScheme) ||
          url.spec().starts_with("chrome://os-settings/osLanguages/input");
@@ -39,11 +46,15 @@
   logger->LogNumberOfCharactersInserted(text.length());
   logger->LogNumberOfCharactersSelectedForInsert(
       system_->GetSelectedTextLength());
-  // The text cannot be immediately inserted as the target input is not focused
-  // at this point, the WebUI is focused. After closing the WebUI focus will
-  // return to the original text input.
-  queued_text_insertion_ = std::make_unique<EditorTextInsertion>(text);
-  system_->CloseUI();
+  // After making an announcement there needs to be a small delay to ensure any
+  // other announcements triggered from a text insertion do not collide with the
+  // original announcement.
+  system_->Announce(kAnnouncementMessage);
+  announcement_delay_.Reset();
+  announcement_delay_.Start(
+      FROM_HERE, kAnnouncementDelay,
+      base::BindOnce(&EditorSystemActuator::QueueTextInsertion,
+                     weak_ptr_factory_.GetWeakPtr(), text));
 }
 
 void EditorSystemActuator::ApproveConsent() {
@@ -85,4 +96,13 @@
   }
 }
 
+void EditorSystemActuator::QueueTextInsertion(const std::string pending_text) {
+  // The text cannot be immediately inserted as the target input is not focused
+  // at this point, the WebUI is focused. After closing the WebUI focus will
+  // return to the original text input.
+  queued_text_insertion_ =
+      std::make_unique<EditorTextInsertion>(std::move(pending_text));
+  system_->CloseUI();
+}
+
 }  // namespace ash::input_method
diff --git a/chrome/browser/ash/input_method/editor_system_actuator.h b/chrome/browser/ash/input_method/editor_system_actuator.h
index 7ebfca9..f2e4f52 100644
--- a/chrome/browser/ash/input_method/editor_system_actuator.h
+++ b/chrome/browser/ash/input_method/editor_system_actuator.h
@@ -8,6 +8,8 @@
 #include <memory>
 #include <string>
 
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
 #include "chrome/browser/ash/input_method/editor_consent_enums.h"
 #include "chrome/browser/ash/input_method/editor_metrics_recorder.h"
 #include "chrome/browser/ash/input_method/editor_text_insertion.h"
@@ -24,6 +26,7 @@
   class System {
    public:
     virtual ~System() = default;
+    virtual void Announce(const std::u16string& message) = 0;
     virtual void ProcessConsentAction(ConsentAction consent_action) = 0;
     virtual void ShowUI() = 0;
     virtual void CloseUI() = 0;
@@ -50,6 +53,8 @@
   void OnFocus(int context_id);
 
  private:
+  void QueueTextInsertion(const std::string pending_text);
+
   raw_ptr<Profile> profile_;
   mojo::AssociatedReceiver<orca::mojom::SystemActuator>
       system_actuator_receiver_;
@@ -62,6 +67,9 @@
   // Only one text insertion can be queued at a time, with new text insertions
   // overwriting previously queued insertions.
   std::unique_ptr<EditorTextInsertion> queued_text_insertion_;
+
+  base::OneShotTimer announcement_delay_;
+  base::WeakPtrFactory<EditorSystemActuator> weak_ptr_factory_{this};
 };
 
 }  // namespace ash::input_method
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/qr_code.cc b/chrome/browser/ash/login/oobe_quick_start/connectivity/qr_code.cc
index 3253811e..2ac20a4 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/qr_code.cc
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/qr_code.cc
@@ -35,8 +35,7 @@
 
 void QRCode::GeneratePixelData() {
   std::vector<uint8_t> blob = GetQRCodeData();
-  qr_code_generator::QRCodeGenerator qr_generator;
-  auto generated_code = qr_generator.Generate(blob);
+  auto generated_code = qr_code_generator::Generate(blob);
   CHECK(generated_code.has_value());
   auto res =
       PixelData{generated_code->data.begin(), generated_code->data.end()};
diff --git a/chrome/browser/ash/scanning/lorgnette_scanner_manager.cc b/chrome/browser/ash/scanning/lorgnette_scanner_manager.cc
index c7a3abe0..536d8dd9 100644
--- a/chrome/browser/ash/scanning/lorgnette_scanner_manager.cc
+++ b/chrome/browser/ash/scanning/lorgnette_scanner_manager.cc
@@ -223,6 +223,8 @@
     std::string device_name;
     ScanProtocol protocol;
     if (!GetUsableDeviceNameAndProtocol(scanner_name, device_name, protocol)) {
+      PRINTER_LOG(ERROR) << "GetScannerCapabilities failed for: "
+                         << scanner_name;
       std::move(callback).Run(std::nullopt);
       return;
     }
@@ -238,6 +240,8 @@
   // LorgnetteScannerManager:
   void OpenScanner(const lorgnette::OpenScannerRequest& request,
                    OpenScannerCallback callback) override {
+    std::string connection_string = request.scanner_id().connection_string();
+
     // If the client doesn't have any tokens, whatever they supplied can't be
     // valid.
     TokenToScannerId* valid_tokens =
@@ -246,17 +250,21 @@
       lorgnette::OpenScannerResponse response;
       *response.mutable_scanner_id() = request.scanner_id();
       response.set_result(lorgnette::OPERATION_RESULT_INVALID);
+      PRINTER_LOG(ERROR) << "OpenScanner: No valid tokens for "
+                         << connection_string;
       std::move(callback).Run(std::move(response));
       return;
     }
 
     // If the token isn't found in the previously returned set, it isn't valid.
-    std::optional<ScannerId>* device_id = base::FindOrNull(
-        *valid_tokens, request.scanner_id().connection_string());
+    std::optional<ScannerId>* device_id =
+        base::FindOrNull(*valid_tokens, connection_string);
     if (!device_id) {
       lorgnette::OpenScannerResponse response;
       *response.mutable_scanner_id() = request.scanner_id();
       response.set_result(lorgnette::OPERATION_RESULT_INVALID);
+      PRINTER_LOG(ERROR) << "OpenScanner: No device ID for "
+                         << connection_string;
       std::move(callback).Run(std::move(response));
       return;
     }
@@ -267,15 +275,19 @@
       lorgnette::OpenScannerResponse response;
       *response.mutable_scanner_id() = request.scanner_id();
       response.set_result(lorgnette::OPERATION_RESULT_MISSING);
+      PRINTER_LOG(ERROR) << "OpenScanner: Empty device ID for "
+                         << connection_string;
       std::move(callback).Run(std::move(response));
       return;
     }
 
     // Token is valid.  The necessary SANE connection string is the second
     // field.
+    connection_string = device_id->value().second;
     lorgnette::OpenScannerRequest lorgnette_request = request;
     lorgnette_request.mutable_scanner_id()->set_connection_string(
-        device_id->value().second);
+        connection_string);
+    PRINTER_LOG(EVENT) << "OpenScanner for " << connection_string;
     GetLorgnetteManagerClient()->OpenScanner(
         std::move(lorgnette_request),
         base::BindOnce(&LorgnetteScannerManagerImpl::OnOpenScannerResponse,
@@ -286,6 +298,7 @@
   // LorgnetteScannerManager:
   void CloseScanner(const lorgnette::CloseScannerRequest& request,
                     CloseScannerCallback callback) override {
+    PRINTER_LOG(EVENT) << "CloseScanner: " << request.scanner().token();
     GetLorgnetteManagerClient()->CloseScanner(
         request,
         base::BindOnce(&LorgnetteScannerManagerImpl::OnCloseScannerResponse,
@@ -338,7 +351,8 @@
     std::string device_name;
     ScanProtocol protocol;
     if (!GetUsableDeviceNameAndProtocol(scanner_name, device_name, protocol)) {
-      LOG(ERROR) << "Failed to get device name for " << scanner_name;
+      PRINTER_LOG(ERROR) << "IsRotateAlternate: Failed to get device name for "
+                         << scanner_name;
       return false;
     }
 
@@ -669,7 +683,11 @@
       OpenScannerCallback callback,
       std::optional<lorgnette::OpenScannerResponse> response) {
     if (response) {
+      PRINTER_LOG(EVENT) << "OpenScanner response received. Handle: "
+                         << response->config().scanner().token();
       *response->mutable_scanner_id() = scanner_id;
+    } else {
+      PRINTER_LOG(ERROR) << "OpenScanner null response received.";
     }
     std::move(callback).Run(response);
   }
@@ -943,7 +961,7 @@
                                       ScanProtocol& protocol_out) {
     const auto scanner_it = deduped_scanners_.find(scanner_name);
     if (scanner_it == deduped_scanners_.end()) {
-      LOG(ERROR) << "Failed to find scanner with name " << scanner_name;
+      PRINTER_LOG(ERROR) << "Failed to find scanner with name " << scanner_name;
       return false;
     }
 
@@ -962,7 +980,8 @@
       }
     }
 
-    LOG(ERROR) << "Failed to find usable device name for " << scanner_name;
+    PRINTER_LOG(ERROR) << "Failed to find usable device name for "
+                       << scanner_name;
     return false;
   }
 
@@ -1020,6 +1039,7 @@
 // static
 std::unique_ptr<LorgnetteScannerManager> LorgnetteScannerManager::Create(
     std::unique_ptr<ZeroconfScannerDetector> zeroconf_scanner_detector) {
+  PRINTER_LOG(EVENT) << "LorgnetteScannerManager::Create";
   return std::make_unique<LorgnetteScannerManagerImpl>(
       std::move(zeroconf_scanner_detector));
 }
diff --git a/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_sea_pen_provider_impl.cc b/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_sea_pen_provider_impl.cc
index d9a9c0c..b87d754 100644
--- a/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_sea_pen_provider_impl.cc
+++ b/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_sea_pen_provider_impl.cc
@@ -91,12 +91,9 @@
     const SeaPenImage& sea_pen_image,
     const mojom::SeaPenQueryPtr& query,
     base::OnceCallback<void(bool success)> callback) {
-  // TODO(b/321778818): move the query_info string construction to
-  // SeaPenWallpaperManager.
-  const std::string query_info = QueryDictToXmpString(SeaPenQueryToDict(query));
   auto* wallpaper_controller = ash::WallpaperController::Get();
   wallpaper_controller->SetSeaPenWallpaper(
-      GetAccountId(profile_), sea_pen_image, query_info, std::move(callback));
+      GetAccountId(profile_), sea_pen_image, query, std::move(callback));
 }
 
 }  // namespace ash::personalization_app
diff --git a/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_sea_pen_provider_impl_unittest.cc b/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_sea_pen_provider_impl_unittest.cc
index 52d8597..65a1f53 100644
--- a/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_sea_pen_provider_impl_unittest.cc
+++ b/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_sea_pen_provider_impl_unittest.cc
@@ -139,13 +139,6 @@
       nullptr, nullptr);
 }
 
-std::string DictToMetadataDescription(const base::Value::Dict& dict) {
-  std::string json;
-  bool success = base::JSONWriter::Write(dict, &json);
-  DCHECK(success);
-  return "<dc:description>" + json + "</dc:description>";
-}
-
 class PersonalizationAppSeaPenProviderImplTest : public testing::Test {
  public:
   PersonalizationAppSeaPenProviderImplTest()
@@ -423,7 +416,7 @@
 }
 
 TEST_F(PersonalizationAppSeaPenProviderImplTest,
-       SelectThumbnailSendsFreeTextMetadata) {
+       SelectThumbnailSendsFreeTextQuery) {
   auto time_override = CreateScopedTimeNowOverride();
 
   SetUpProfileForTesting(kFakeTestEmail, GetTestAccountId());
@@ -440,7 +433,6 @@
   sea_pen_provider_remote()->SearchWallpaper(
       std::move(search_query), search_wallpaper_future.GetCallback());
 
-  ASSERT_EQ(std::string(), test_wallpaper_controller()->sea_pen_metadata());
   // Select the first returned thumbnail.
   base::test::TestFuture<bool> select_wallpaper_future;
   sea_pen_provider_remote()->SelectSeaPenThumbnail(
@@ -448,17 +440,12 @@
       select_wallpaper_future.GetCallback());
 
   ASSERT_TRUE(select_wallpaper_future.Take());
-  base::Value::Dict expected_metadata;
-  // `time_override` is still in effect so `base::Time::Now()` should always
-  // return the same value.
-  expected_metadata.Set("creation_time", base::TimeToValue(base::Time::Now()));
-  expected_metadata.Set("freeform_query", user_search_query);
-  EXPECT_THAT(test_wallpaper_controller()->sea_pen_metadata(),
-              testing::HasSubstr(DictToMetadataDescription(expected_metadata)));
+  EXPECT_TRUE(test_wallpaper_controller()->sea_pen_query().Equals(
+      mojom::SeaPenQuery::NewTextQuery(user_search_query)));
 }
 
 TEST_F(PersonalizationAppSeaPenProviderImplTest,
-       SelectThumbnailSendsTemplateMetadata) {
+       SelectThumbnailSendsTemplateQuery) {
   auto time_override = CreateScopedTimeNowOverride();
 
   SetUpProfileForTesting(kFakeTestEmail, GetTestAccountId());
@@ -487,7 +474,6 @@
   sea_pen_provider_remote()->SearchWallpaper(
       std::move(search_query), search_wallpaper_future.GetCallback());
 
-  ASSERT_EQ(std::string(), test_wallpaper_controller()->sea_pen_metadata());
   // Select the first returned thumbnail.
   base::test::TestFuture<bool> select_wallpaper_future;
   sea_pen_provider_remote()->SelectSeaPenThumbnail(
@@ -495,26 +481,12 @@
       select_wallpaper_future.GetCallback());
 
   ASSERT_TRUE(select_wallpaper_future.Take());
-  base::Value::Dict expected_metadata;
-  // `time_override` is still in effect so `base::Time::Now()` should always
-  // return the same value.
-  expected_metadata.Set("creation_time", base::TimeToValue(base::Time::Now()));
-  expected_metadata.Set("user_visible_query_text", "test template query");
-  expected_metadata.Set("user_visible_query_template", "test template title");
-  {
-    base::Value::Dict options;
-    for (const auto& [chip, option] : chosen_options) {
-      options.Set(base::NumberToString(static_cast<int32_t>(chip)),
-                  base::NumberToString(static_cast<int32_t>(option)));
-    }
-    expected_metadata.Set("options", std::move(options));
-  }
-  expected_metadata.Set("template_id",
-                        base::NumberToString(static_cast<int32_t>(
-                            mojom::SeaPenTemplateId::kCharacters)));
 
-  EXPECT_THAT(test_wallpaper_controller()->sea_pen_metadata(),
-              testing::HasSubstr(DictToMetadataDescription(expected_metadata)));
+  EXPECT_TRUE(test_wallpaper_controller()->sea_pen_query().Equals(
+      mojom::SeaPenQuery::NewTemplateQuery(mojom::SeaPenTemplateQuery::New(
+          mojom::SeaPenTemplateId::kCharacters, chosen_options,
+          mojom::SeaPenUserVisibleQuery::New("test template query",
+                                             "test template title")))));
 }
 
 TEST_F(PersonalizationAppSeaPenProviderImplTest,
diff --git a/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc b/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
index ea5ea061..13eff45 100644
--- a/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
+++ b/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
@@ -396,7 +396,9 @@
 
   test_wallpaper_controller()->SetSeaPenWallpaper(
       GetTestAccountId(), {/*jpg_bytes=*/std::string(), /*id=*/111},
-      /*query_info=*/"test query", base::DoNothing());
+      ash::personalization_app::mojom::SeaPenQuery::NewTextQuery(
+          "search_query"),
+      base::DoNothing());
 
   ash::personalization_app::mojom::CurrentWallpaper* current =
       current_wallpaper();
diff --git a/chrome/browser/autofill/personal_data_manager_factory.cc b/chrome/browser/autofill/personal_data_manager_factory.cc
index 31902b2d..85ebd8b4 100644
--- a/chrome/browser/autofill/personal_data_manager_factory.cc
+++ b/chrome/browser/autofill/personal_data_manager_factory.cc
@@ -13,12 +13,14 @@
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/sync/sync_service_factory.h"
 #include "chrome/browser/web_data_service_factory.h"
+#include "components/autofill/content/browser/content_autofill_shared_storage_handler.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/strike_databases/strike_database.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/sync/base/command_line_switches.h"
 #include "components/variations/service/variations_service.h"
+#include "content/public/browser/storage_partition.h"
 
 namespace autofill {
 
@@ -101,9 +103,18 @@
 
   auto* sync_service = SyncServiceFactory::GetForProfile(profile);
 
+  auto* shared_storage_manager =
+      profile->GetDefaultStoragePartition()->GetSharedStorageManager();
+  auto shared_storage_handler =
+      shared_storage_manager
+          ? std::make_unique<ContentAutofillSharedStorageHandler>(
+                *shared_storage_manager)
+          : nullptr;
+
   service->Init(local_storage, account_storage, profile->GetPrefs(),
                 g_browser_process->local_state(), identity_manager,
-                history_service, sync_service, strike_database, image_fetcher);
+                history_service, sync_service, strike_database, image_fetcher,
+                std::move(shared_storage_handler));
 
   return service;
 }
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index bed1503..85b768c 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -316,6 +316,7 @@
 #include "chrome/browser/ui/webui/ash/crostini_upgrader/crostini_upgrader.mojom.h"
 #include "chrome/browser/ui/webui/ash/crostini_upgrader/crostini_upgrader_ui.h"
 #include "chrome/browser/ui/webui/ash/emoji/emoji_picker.mojom.h"
+#include "chrome/browser/ui/webui/ash/emoji/emoji_search_proxy.h"
 #include "chrome/browser/ui/webui/ash/emoji/emoji_ui.h"
 #include "chrome/browser/ui/webui/ash/emoji/new_window_proxy.mojom.h"
 #include "chrome/browser/ui/webui/ash/emoji/seal.mojom.h"
@@ -357,6 +358,7 @@
 #include "chrome/browser/ui/webui/nearby_share/nearby_share.mojom.h"
 #include "chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h"
 #include "chromeos/ash/components/audio/public/mojom/cros_audio_config.mojom.h"
+#include "chromeos/ash/components/emoji/emoji_search.mojom.h"
 #include "chromeos/ash/components/local_search_service/public/mojom/index.mojom.h"
 #include "chromeos/ash/services/auth_factor_config/public/mojom/auth_factor_config.mojom.h"
 #include "chromeos/ash/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom.h"
@@ -1580,6 +1582,12 @@
   RegisterWebUIControllerInterfaceBinder<
       emoji_picker::mojom::PageHandlerFactory, ash::EmojiUI>(map);
 
+  if (base::FeatureList::IsEnabled(
+          ash::features::kImeSystemEmojiPickerMojoSearch)) {
+    RegisterWebUIControllerInterfaceBinder<emoji_search::mojom::EmojiSearch,
+                                           ash::EmojiUI>(map);
+  }
+
   RegisterWebUIControllerInterfaceBinder<sensor::mojom::PageHandlerFactory,
                                          ash::SensorInfoUI>(map);
   RegisterWebUIControllerInterfaceBinder<
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 049fe96..3cff1f68 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -1491,6 +1491,14 @@
   }
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
 
+#if BUILDFLAG(CHROME_FOR_TESTING)
+  if (!g_browser_process->local_state()->GetBoolean(
+          prefs::kChromeForTestingAllowed)) {
+    LOG(ERROR) << "Chrome for Testing is disallowed by the system admin.";
+    return static_cast<int>(chrome::RESULT_CODE_ACTION_DISALLOWED_BY_POLICY);
+  }
+#endif  // BUILDFLAG(CHROME_FOR_TESTING)
+
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kMakeDefaultBrowser)) {
     bool is_managed = g_browser_process->local_state()->IsManagedPreference(
diff --git a/chrome/browser/commerce/price_change/android/java/src/org/chromium/chrome/browser/price_change/PriceChangeModuleMediator.java b/chrome/browser/commerce/price_change/android/java/src/org/chromium/chrome/browser/price_change/PriceChangeModuleMediator.java
index 581ecae..a495a31c 100644
--- a/chrome/browser/commerce/price_change/android/java/src/org/chromium/chrome/browser/price_change/PriceChangeModuleMediator.java
+++ b/chrome/browser/commerce/price_change/android/java/src/org/chromium/chrome/browser/price_change/PriceChangeModuleMediator.java
@@ -106,6 +106,14 @@
                         return;
                     }
                     Tab tab = res.get(0).getTab();
+                    // Check if tab is in the current tab model for multi-window case.
+                    if (tab == null
+                            || TabModelUtils.getTabById(
+                                            mTabModelSelector.getModel(false), tab.getId())
+                                    == null) {
+                        mModuleDelegate.onDataFetchFailed(mModuleType);
+                        return;
+                    }
                     ShoppingPersistedTabData data = res.get(0).getData();
                     mModel.set(
                             PriceChangeModuleProperties.MODULE_TITLE,
diff --git a/chrome/browser/commerce/price_change/android/junit/src/org/chromium/chrome/browser/price_change/PriceChangeModuleMediatorUnitTest.java b/chrome/browser/commerce/price_change/android/junit/src/org/chromium/chrome/browser/price_change/PriceChangeModuleMediatorUnitTest.java
index 72e2f9a..e333a8c4 100644
--- a/chrome/browser/commerce/price_change/android/junit/src/org/chromium/chrome/browser/price_change/PriceChangeModuleMediatorUnitTest.java
+++ b/chrome/browser/commerce/price_change/android/junit/src/org/chromium/chrome/browser/price_change/PriceChangeModuleMediatorUnitTest.java
@@ -119,11 +119,13 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mJniMocker.mock(UrlUtilitiesJni.TEST_HOOKS, mUrlUtilitiesJniMock);
+        mTab = new MockTab(123, mProfile);
         doReturn(mTabModel).when(mTabModelSelector).getModel(false);
+        doReturn(1).when(mTabModel).getCount();
+        doReturn(mTab).when(mTabModel).getTabAt(0);
         ShoppingPersistedTabDataService.setServiceForTesting(mService);
 
         mContext = RuntimeEnvironment.application;
-        mTab = new MockTab(123, mProfile);
         mModel = new PropertyModel(PriceChangeModuleProperties.ALL_KEYS);
         mMediator =
                 new PriceChangeModuleMediator(
@@ -303,6 +305,42 @@
         verify(mModuleDelegate, never()).removeModule(mMediator.getModuleType());
     }
 
+    @Test
+    @SmallTest
+    public void testShowModule_NullTab() {
+        doReturn(true).when(mService).isInitialized();
+
+        mMediator.showModule();
+
+        ShoppingPersistedTabData data = mock(ShoppingPersistedTabData.class);
+        PriceChangeItem item = new PriceChangeItem(null, data);
+        ArgumentCaptor<Callback<List<PriceChangeItem>>> dataCallbackCaptor =
+                ArgumentCaptor.forClass(Callback.class);
+        verify(mService).getAllShoppingPersistedTabDataWithPriceDrop(dataCallbackCaptor.capture());
+        dataCallbackCaptor.getValue().onResult(new ArrayList<>(Arrays.asList(item)));
+        verify(mService, times(0)).initialize(any(Set.class));
+        verify(mModuleDelegate).onDataFetchFailed(eq(ModuleType.PRICE_CHANGE));
+    }
+
+    @Test
+    @SmallTest
+    public void testShowModule_TabFromOtherModel() {
+        doReturn(true).when(mService).isInitialized();
+
+        mMediator.showModule();
+
+        ShoppingPersistedTabData data = mock(ShoppingPersistedTabData.class);
+        PriceChangeItem item = new PriceChangeItem(mTab, data);
+        // Mock that tab is not in the current tab model.
+        doReturn(0).when(mTabModel).getCount();
+        ArgumentCaptor<Callback<List<PriceChangeItem>>> dataCallbackCaptor =
+                ArgumentCaptor.forClass(Callback.class);
+        verify(mService).getAllShoppingPersistedTabDataWithPriceDrop(dataCallbackCaptor.capture());
+        dataCallbackCaptor.getValue().onResult(new ArrayList<>(Arrays.asList(item)));
+        verify(mService, times(0)).initialize(any(Set.class));
+        verify(mModuleDelegate).onDataFetchFailed(eq(ModuleType.PRICE_CHANGE));
+    }
+
     public void showModuleWithInitializedService() {
         doReturn(true).when(mService).isInitialized();
         when(mUrlUtilitiesJniMock.getDomainAndRegistry(eq(PRODUCT_URL.getSpec()), anyBoolean()))
diff --git a/chrome/browser/download/download_item_model.cc b/chrome/browser/download/download_item_model.cc
index 349b510..9054e72 100644
--- a/chrome/browser/download/download_item_model.cc
+++ b/chrome/browser/download/download_item_model.cc
@@ -905,48 +905,6 @@
   }
 }
 
-DownloadItemModel::BubbleUIInfo
-DownloadItemModel::GetBubbleUIInfoForTailoredWarning(
-    TailoredWarningType tailored_warning_type) const {
-  switch (tailored_warning_type) {
-    case TailoredWarningType::kSuspiciousArchive:
-      return DownloadUIModel::BubbleUIInfo::SuspiciousUiPattern(
-          l10n_util::GetStringUTF16(
-              IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_ARCHIVE_MALWARE),
-          l10n_util::GetStringUTF16(
-              IDS_DOWNLOAD_BUBBLE_CONTINUE_SUSPICIOUS_FILE));
-    case TailoredWarningType::kCookieTheft:
-      return DownloadUIModel::BubbleUIInfo::DangerousUiPattern(
-          l10n_util::GetStringUTF16(
-              IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_COOKIE_THEFT));
-    case TailoredWarningType::kCookieTheftWithAccountInfo: {
-      auto* identity_manager = IdentityManagerFactory::GetForProfile(profile());
-      std::string email =
-          identity_manager
-              ? identity_manager
-                    ->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin)
-                    .email
-              : "";
-      base::UmaHistogramBoolean(
-          "SBClientDownload.TailoredWarning.HasVaidEmailForAccountInfo",
-          !email.empty());
-      if (!email.empty()) {
-        return DownloadUIModel::BubbleUIInfo::DangerousUiPattern(
-            l10n_util::GetStringFUTF16(
-                IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_COOKIE_THEFT_AND_ACCOUNT,
-                base::ASCIIToUTF16(email)));
-      }
-      return DownloadUIModel::BubbleUIInfo::DangerousUiPattern(
-          l10n_util::GetStringUTF16(
-              IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_COOKIE_THEFT));
-    }
-    case TailoredWarningType::kNoTailoredWarning: {
-      NOTREACHED();
-      return DownloadUIModel::BubbleUIInfo();
-    }
-  }
-}
-
 TailoredWarningType DownloadItemModel::GetTailoredWarningType() const {
   if (!base::FeatureList::IsEnabled(safe_browsing::kDownloadTailoredWarnings)) {
     return TailoredWarningType::kNoTailoredWarning;
diff --git a/chrome/browser/download/download_item_model.h b/chrome/browser/download/download_item_model.h
index 83dc1ef2..a7fa2333 100644
--- a/chrome/browser/download/download_item_model.h
+++ b/chrome/browser/download/download_item_model.h
@@ -121,8 +121,6 @@
                         DownloadCommands::Command command) const override;
   void ExecuteCommand(DownloadCommands* download_commands,
                       DownloadCommands::Command command) override;
-  BubbleUIInfo GetBubbleUIInfoForTailoredWarning(
-      TailoredWarningType tailored_warning_type) const override;
   TailoredWarningType GetTailoredWarningType() const override;
   bool ShouldShowInBubble() const override;
   bool IsEphemeralWarning() const override;
diff --git a/chrome/browser/download/download_item_model_unittest.cc b/chrome/browser/download/download_item_model_unittest.cc
index 76c2cea..9b167645 100644
--- a/chrome/browser/download/download_item_model_unittest.cc
+++ b/chrome/browser/download/download_item_model_unittest.cc
@@ -566,11 +566,6 @@
         .WillByDefault(Return(test_case.insecure_download_status));
     EXPECT_EQ(base::UTF16ToUTF8(model().GetStatusText()),
               test_case.expected_bubble_status_msg);
-#if !BUILDFLAG(IS_ANDROID)
-    // Android doesn't have BubbleUI info.
-    EXPECT_EQ(model().GetBubbleUIInfo().primary_button_command.value(),
-              DownloadCommands::Command::KEEP);
-#endif  // !BUILDFLAG(IS_ANDROID)
   }
 
   const struct DangerTypeTestCase {
@@ -644,197 +639,6 @@
   EXPECT_TRUE(model().ShouldPreferOpeningInBrowser());
 }
 
-TEST_F(DownloadItemModelTest, InProgressOrCompletedBubbleUIInfo) {
-  SetupDownloadItemDefaults();
-
-  SetupCompletedDownloadItem(base::Hours(1));
-  DownloadUIModel::BubbleUIInfo bubble_ui_info = model().GetBubbleUIInfo();
-  std::vector<DownloadCommands::Command> quick_action_commands;
-  for (auto quick_action : bubble_ui_info.quick_actions) {
-    quick_action_commands.push_back(quick_action.command);
-  }
-  EXPECT_EQ(quick_action_commands,
-            std::vector({DownloadCommands::Command::SHOW_IN_FOLDER,
-                         DownloadCommands::Command::OPEN_WHEN_COMPLETE}));
-  EXPECT_FALSE(bubble_ui_info.primary_button_command.has_value());
-
-  Mock::VerifyAndClearExpectations(&item());
-  Mock::VerifyAndClearExpectations(&model());
-
-  ON_CALL(item(), GetState())
-      .WillByDefault(Return(download::DownloadItem::IN_PROGRESS));
-  EXPECT_CALL(item(), IsPaused()).WillRepeatedly(Return(true));
-  bubble_ui_info = model().GetBubbleUIInfo();
-  quick_action_commands = {};
-  for (auto quick_action : bubble_ui_info.quick_actions) {
-    quick_action_commands.push_back(quick_action.command);
-  }
-  EXPECT_EQ(quick_action_commands,
-            std::vector({DownloadCommands::Command::RESUME,
-                         DownloadCommands::Command::CANCEL}));
-  EXPECT_FALSE(bubble_ui_info.primary_button_command.has_value());
-
-  Mock::VerifyAndClearExpectations(&item());
-  Mock::VerifyAndClearExpectations(&model());
-
-  EXPECT_CALL(item(), IsPaused()).WillRepeatedly(Return(false));
-  bubble_ui_info = model().GetBubbleUIInfo();
-  quick_action_commands = {};
-  for (auto quick_action : bubble_ui_info.quick_actions) {
-    quick_action_commands.push_back(quick_action.command);
-  }
-  EXPECT_EQ(quick_action_commands,
-            std::vector({DownloadCommands::Command::PAUSE,
-                         DownloadCommands::Command::CANCEL}));
-  EXPECT_FALSE(bubble_ui_info.primary_button_command.has_value());
-}
-
-TEST_F(DownloadItemModelTest, DangerousWarningBubbleUIInfo) {
-  SetupCompletedDownloadItem(base::Hours(1));
-  const struct DangerTypeTestCase {
-    download::DownloadDangerType danger_type;
-    std::optional<DownloadCommands::Command> primary_button_command;
-    std::vector<DownloadCommands::Command> subpage_button_commands;
-  } kDangerTypeTestCases[] = {
-      {download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
-       std::nullopt,
-       {DownloadCommands::Command::DISCARD, DownloadCommands::Command::KEEP}},
-      {download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT,
-       std::nullopt,
-       {DownloadCommands::Command::DISCARD}},
-      {download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST,
-       std::nullopt,
-       {DownloadCommands::Command::DISCARD}},
-      {download::DOWNLOAD_DANGER_TYPE_DANGEROUS_ACCOUNT_COMPROMISE,
-       std::nullopt,
-       {DownloadCommands::Command::DISCARD}},
-      {download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED,
-       std::nullopt,
-       {DownloadCommands::Command::DISCARD}},
-      {download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
-       std::nullopt,
-       {DownloadCommands::Command::DISCARD}},
-      {download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING,
-       DownloadCommands::Command::DISCARD,
-       {DownloadCommands::Command::DISCARD, DownloadCommands::Command::KEEP}},
-      {download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT,
-       std::nullopt,
-       {DownloadCommands::Command::DISCARD, DownloadCommands::Command::KEEP}},
-      {download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING,
-       std::nullopt,
-       {DownloadCommands::Command::DEEP_SCAN,
-        DownloadCommands::Command::BYPASS_DEEP_SCANNING}},
-      {download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING,
-       std::nullopt,
-       {DownloadCommands::Command::DISCARD,
-        DownloadCommands::Command::CANCEL_DEEP_SCAN}},
-  };
-  for (const auto& test_case : kDangerTypeTestCases) {
-    SCOPED_TRACE(testing::Message()
-                 << "Failed for danger type "
-                 << download::GetDownloadDangerTypeString(test_case.danger_type)
-                 << std::endl);
-    SetupDownloadItemDefaults();
-    ON_CALL(item(), GetDangerType())
-        .WillByDefault(Return(test_case.danger_type));
-    DownloadUIModel::BubbleUIInfo bubble_ui_info = model().GetBubbleUIInfo();
-    EXPECT_EQ(bubble_ui_info.primary_button_command,
-              test_case.primary_button_command);
-    std::vector<DownloadCommands::Command> subpage_commands;
-    for (auto button : bubble_ui_info.subpage_buttons) {
-      subpage_commands.push_back(button.command);
-    }
-    EXPECT_EQ(subpage_commands, test_case.subpage_button_commands);
-  }
-}
-
-TEST_F(DownloadItemModelTest, InterruptedBubbleUIInfo) {
-  std::vector<download::DownloadInterruptReason> no_retry_interrupt_reasons = {
-      download::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE,
-      download::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED,
-      download::DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED,
-      download::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED,
-      download::DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN,
-      download::DOWNLOAD_INTERRUPT_REASON_FILE_SAME_AS_SOURCE,
-      download::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT};
-  std::vector<download::DownloadInterruptReason> retry_interrupt_reasons = {
-      download::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST,
-      download::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED,
-      download::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT,
-      download::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED,
-      download::DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN,
-      download::DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR,
-      download::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN,
-      download::DOWNLOAD_INTERRUPT_REASON_CRASH,
-      download::DOWNLOAD_INTERRUPT_REASON_SERVER_CONTENT_LENGTH_MISMATCH,
-      download::DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE,
-      download::DOWNLOAD_INTERRUPT_REASON_SERVER_CROSS_ORIGIN_REDIRECT,
-      download::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED,
-      download::DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH,
-      download::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED,
-      download::DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM,
-      download::DOWNLOAD_INTERRUPT_REASON_SERVER_UNREACHABLE,
-      download::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT};
-
-  const struct TestCase {
-    std::vector<download::DownloadInterruptReason> interrupt_reasons;
-    bool can_resume;
-
-    std::string expected_warning_summary;
-    raw_ptr<const gfx::VectorIcon> expected_icon_model_override;
-    std::optional<DownloadCommands::Command> expected_primary_button_command;
-  } kTestCases[] = {
-      {{download::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED},
-       false,
-       "Your organization blocked this file because it didn't meet a security "
-       "policy",
-       &views::kInfoIcon,
-       std::optional<DownloadCommands::Command>()},
-      {{download::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG},
-       false,
-       "Try using a shorter file name or saving to a different folder",
-       &vector_icons::kFileDownloadOffIcon,
-       std::optional<DownloadCommands::Command>()},
-      {{download::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE},
-       false,
-       "Free up space on your device. Then, try to download again",
-       &vector_icons::kFileDownloadOffIcon,
-       std::optional<DownloadCommands::Command>()},
-      {{download::DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED},
-       false,
-       "Try to sign in to the site. Then, download again",
-       &vector_icons::kFileDownloadOffIcon,
-       std::optional<DownloadCommands::Command>()},
-      {no_retry_interrupt_reasons, false, "",
-       &vector_icons::kFileDownloadOffIcon,
-       std::optional<DownloadCommands::Command>()},
-      {retry_interrupt_reasons, false, "", &vector_icons::kFileDownloadOffIcon,
-       DownloadCommands::Command::RETRY},
-      {retry_interrupt_reasons, true, "", &vector_icons::kFileDownloadOffIcon,
-       DownloadCommands::Command::RESUME},
-  };
-
-  SetupDownloadItemDefaults();
-  for (const auto& test_case : kTestCases) {
-    for (const auto& interrupt_reason : test_case.interrupt_reasons) {
-      SetupInterruptedDownloadItem(interrupt_reason);
-      EXPECT_CALL(item(), CanResume())
-          .WillRepeatedly(Return(test_case.can_resume));
-
-      DownloadUIModel::BubbleUIInfo bubble_ui_info = model().GetBubbleUIInfo();
-      EXPECT_EQ(test_case.expected_warning_summary,
-                base::UTF16ToUTF8(bubble_ui_info.warning_summary));
-      EXPECT_EQ(test_case.expected_icon_model_override,
-                bubble_ui_info.icon_model_override);
-      EXPECT_EQ(test_case.expected_primary_button_command,
-                bubble_ui_info.primary_button_command);
-      EXPECT_EQ(kColorDownloadItemIconDangerous,
-                bubble_ui_info.secondary_color);
-      EXPECT_FALSE(bubble_ui_info.has_progress_bar);
-    }
-  }
-}
-
 TEST_F(DownloadItemModelTest, ShouldShowInBubble) {
   auto in_progress = DownloadItem::IN_PROGRESS;
   auto canceled = DownloadItem::CANCELLED;
@@ -918,158 +722,6 @@
   std::vector<int> expected_english = {53, 32, 77, 66, 32, 8226, 32, 65};
   compare_results(english, expected_english);
 }
-
-#if BUILDFLAG(FULL_SAFE_BROWSING)
-// Test file type warning where verdict was obtained.
-TEST_F(DownloadItemModelTest, FileTypeWarning_HasSafeBrowsingVerdict) {
-  SetupDownloadItemDefaults();
-  SetupCompletedDownloadItem(base::Minutes(30));
-  SetStatusTextBuilder(/*for_bubble=*/true);
-  for (auto sb_state : {safe_browsing::SafeBrowsingState::STANDARD_PROTECTION,
-                        safe_browsing::SafeBrowsingState::ENHANCED_PROTECTION,
-                        // This can happen if the user subsequently turned off
-                        // SB after verdict was obtained.
-                        safe_browsing::SafeBrowsingState::NO_SAFE_BROWSING}) {
-    SetSafeBrowsingState(profile()->GetPrefs(), sb_state);
-    EXPECT_CALL(item(), GetDangerType())
-        .WillRepeatedly(Return(download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE));
-    safe_browsing::DownloadProtectionService::SetDownloadProtectionData(
-        &item(), "token", safe_browsing::ClientDownloadResponse::DANGEROUS,
-        safe_browsing::ClientDownloadResponse::TailoredVerdict());
-    DownloadUIModel::BubbleUIInfo bubble_ui_info = model().GetBubbleUIInfo();
-
-    // Subpage warning
-    EXPECT_TRUE(bubble_ui_info.HasSubpage());
-    EXPECT_TRUE(base::MatchPattern(bubble_ui_info.warning_summary,
-                                   u"*file type isn't commonly downloaded*"));
-    // Suspicious pattern has 2 buttons on the subpage.
-    ASSERT_EQ(bubble_ui_info.subpage_buttons.size(), 2u);
-    // Primary subpage button.
-    EXPECT_EQ(bubble_ui_info.subpage_buttons[0].command,
-              DownloadCommands::DISCARD);
-    EXPECT_EQ(bubble_ui_info.subpage_buttons[0].label, u"Delete from history");
-    // Secondary subpage button. File is described as "suspicious".
-    EXPECT_EQ(bubble_ui_info.subpage_buttons[1].command,
-              DownloadCommands::KEEP);
-    EXPECT_EQ(bubble_ui_info.subpage_buttons[1].label,
-              u"Download suspicious file");
-    // Learn more link.
-    EXPECT_TRUE(bubble_ui_info.learn_more_link.has_value());
-    EXPECT_TRUE(
-        base::MatchPattern(bubble_ui_info.learn_more_link->label_and_link_text,
-                           u"Learn why * blocks some downloads"));
-    // Status text for row view. File is described as suspicious.
-    EXPECT_EQ(model().GetStatusText(), u"Suspicious download blocked");
-  }
-}
-
-// Test file type warning where SB is on but no SB verdict was obtained.
-TEST_F(DownloadItemModelTest, FileTypeWarning_SafeBrowsingOn_NoVerdict) {
-  SetupDownloadItemDefaults();
-  SetupCompletedDownloadItem(base::Minutes(30));
-  SetStatusTextBuilder(/*for_bubble=*/true);
-  for (auto sb_state :
-       {safe_browsing::SafeBrowsingState::STANDARD_PROTECTION,
-        safe_browsing::SafeBrowsingState::ENHANCED_PROTECTION}) {
-    SetSafeBrowsingState(profile()->GetPrefs(), sb_state);
-    EXPECT_CALL(item(), GetDangerType())
-        .WillRepeatedly(Return(download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE));
-    DownloadUIModel::BubbleUIInfo bubble_ui_info = model().GetBubbleUIInfo();
-
-    // Subpage warning
-    EXPECT_TRUE(bubble_ui_info.HasSubpage());
-    EXPECT_TRUE(base::MatchPattern(bubble_ui_info.warning_summary,
-                                   u"*file type isn't commonly downloaded*"));
-    // Suspicious pattern has 2 buttons on the subpage.
-    ASSERT_EQ(bubble_ui_info.subpage_buttons.size(), 2u);
-    // Primary subpage button.
-    EXPECT_EQ(bubble_ui_info.subpage_buttons[0].command,
-              DownloadCommands::DISCARD);
-    EXPECT_EQ(bubble_ui_info.subpage_buttons[0].label, u"Delete from history");
-    // Secondary subpage button. File is described as "unverified".
-    EXPECT_EQ(bubble_ui_info.subpage_buttons[1].command,
-              DownloadCommands::KEEP);
-    EXPECT_EQ(bubble_ui_info.subpage_buttons[1].label,
-              u"Download unverified file");
-    // Learn more link.
-    EXPECT_TRUE(bubble_ui_info.learn_more_link.has_value());
-    EXPECT_TRUE(
-        base::MatchPattern(bubble_ui_info.learn_more_link->label_and_link_text,
-                           u"Learn why * blocks some downloads"));
-    // Status text for row view. File is described as unverified.
-    EXPECT_EQ(model().GetStatusText(), u"Unverified download blocked");
-  }
-}
-
-// Test file type warning where SB is disabled by pref and can be turned on.
-TEST_F(DownloadItemModelTest, FileTypeWarning_NoSafeBrowsing_DisabledByPref) {
-  SetupDownloadItemDefaults();
-  SetupCompletedDownloadItem(base::Minutes(30));
-  SetStatusTextBuilder(/*for_bubble=*/true);
-  SetSafeBrowsingState(profile()->GetPrefs(),
-                       safe_browsing::SafeBrowsingState::NO_SAFE_BROWSING);
-  EXPECT_CALL(item(), GetDangerType())
-      .WillRepeatedly(Return(download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE));
-  DownloadUIModel::BubbleUIInfo bubble_ui_info = model().GetBubbleUIInfo();
-
-  // Subpage warning
-  EXPECT_TRUE(bubble_ui_info.HasSubpage());
-  EXPECT_TRUE(base::MatchPattern(bubble_ui_info.warning_summary,
-                                 u"*file can't be verified*"));
-  // Suspicious pattern has 2 buttons on the subpage.
-  ASSERT_EQ(bubble_ui_info.subpage_buttons.size(), 2u);
-  // Primary subpage button.
-  EXPECT_EQ(bubble_ui_info.subpage_buttons[0].command,
-            DownloadCommands::DISCARD);
-  EXPECT_EQ(bubble_ui_info.subpage_buttons[0].label, u"Delete from history");
-  // Secondary subpage button. File is described as "unverified".
-  EXPECT_EQ(bubble_ui_info.subpage_buttons[1].command, DownloadCommands::KEEP);
-  EXPECT_EQ(bubble_ui_info.subpage_buttons[1].label,
-            u"Download unverified file");
-  // Learn more link.
-  EXPECT_TRUE(bubble_ui_info.learn_more_link.has_value());
-  EXPECT_TRUE(
-      base::MatchPattern(bubble_ui_info.learn_more_link->label_and_link_text,
-                         u"Turn on Safe Browsing to *"));
-  // Status text for row view. File is described as unverified.
-  EXPECT_EQ(model().GetStatusText(), u"Unverified download blocked");
-}
-
-// Test file type warning where SB is disabled by enterprise controls and cannot
-// be turned on.
-TEST_F(DownloadItemModelTest, FileTypeWarning_NoSafeBrowsing_DisabledManaged) {
-  SetupDownloadItemDefaults();
-  SetupCompletedDownloadItem(base::Minutes(30));
-  SetStatusTextBuilder(/*for_bubble=*/true);
-  SetSafeBrowsingState(profile()->GetPrefs(),
-                       safe_browsing::SafeBrowsingState::NO_SAFE_BROWSING);
-  testing_profile()->GetTestingPrefService()->SetManagedPref(
-      prefs::kSafeBrowsingEnabled, std::make_unique<base::Value>(false));
-  EXPECT_CALL(item(), GetDangerType())
-      .WillRepeatedly(Return(download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE));
-  DownloadUIModel::BubbleUIInfo bubble_ui_info = model().GetBubbleUIInfo();
-
-  // Subpage warning
-  EXPECT_TRUE(bubble_ui_info.HasSubpage());
-  EXPECT_TRUE(base::MatchPattern(bubble_ui_info.warning_summary,
-                                 u"*file can't be verified*"));
-  // Suspicious pattern has 2 buttons on the subpage.
-  ASSERT_EQ(bubble_ui_info.subpage_buttons.size(), 2u);
-  // Primary subpage button.
-  EXPECT_EQ(bubble_ui_info.subpage_buttons[0].command,
-            DownloadCommands::DISCARD);
-  EXPECT_EQ(bubble_ui_info.subpage_buttons[0].label, u"Delete from history");
-  // Secondary subpage button. File is described as "unverified".
-  EXPECT_EQ(bubble_ui_info.subpage_buttons[1].command, DownloadCommands::KEEP);
-  EXPECT_EQ(bubble_ui_info.subpage_buttons[1].label,
-            u"Download unverified file");
-  // There is no learn more link because the user cannot turn on SB.
-  EXPECT_FALSE(bubble_ui_info.learn_more_link.has_value());
-  // Status text for row view. File is described as unverified.
-  EXPECT_EQ(model().GetStatusText(), u"Unverified download blocked");
-}
-#endif  // BUILDFLAG(FULL_SAFE_BROWSING)
-
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 TEST_F(DownloadItemModelTest, ShouldShowInShelf) {
@@ -1308,86 +960,6 @@
             DownloadUIModel::TailoredWarningType::kCookieTheftWithAccountInfo);
 }
 
-TEST_F(DownloadItemModelTailoredWarningTest,
-       GetBubbleUIInfoForTailoredWarning_CookieTheft) {
-  SetupDownloadItemDefaults();
-  SetupTailoredWarningForItem(
-      download::DOWNLOAD_DANGER_TYPE_DANGEROUS_ACCOUNT_COMPROMISE,
-      TailoredVerdict::COOKIE_THEFT, /*adjustments=*/{});
-
-  DownloadUIModel::BubbleUIInfo bubble_ui_info = model().GetBubbleUIInfo();
-  // No primary button on download row view. Button only appears on subpage.
-  EXPECT_FALSE(bubble_ui_info.primary_button_command.has_value());
-  EXPECT_EQ(bubble_ui_info.subpage_buttons.size(), 1u);
-  EXPECT_EQ(bubble_ui_info.subpage_buttons[0].command,
-            DownloadCommands::Command::DISCARD);
-  EXPECT_TRUE(bubble_ui_info.subpage_buttons[0].is_prominent);
-  EXPECT_EQ(bubble_ui_info.warning_summary,
-            u"This file can harm your personal and social network accounts");
-}
-
-TEST_F(DownloadItemModelTailoredWarningTest,
-       GetBubbleUIInfoForTailoredWarning_SuspiciousArchive) {
-  SetupDownloadItemDefaults();
-  SetupTailoredWarningForItem(download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT,
-                              TailoredVerdict::SUSPICIOUS_ARCHIVE,
-                              /*adjustments=*/{});
-
-  DownloadUIModel::BubbleUIInfo bubble_ui_info = model().GetBubbleUIInfo();
-  // No primary button on download row view. Button only appears on subpage.
-  EXPECT_FALSE(bubble_ui_info.primary_button_command.has_value());
-  EXPECT_EQ(bubble_ui_info.subpage_buttons.size(), 2u);
-  EXPECT_EQ(bubble_ui_info.subpage_buttons[0].command,
-            DownloadCommands::Command::DISCARD);
-  EXPECT_TRUE(bubble_ui_info.subpage_buttons[0].is_prominent);
-  EXPECT_EQ(bubble_ui_info.subpage_buttons[1].command,
-            DownloadCommands::Command::KEEP);
-  EXPECT_FALSE(bubble_ui_info.subpage_buttons[1].is_prominent);
-  EXPECT_EQ(bubble_ui_info.warning_summary,
-            u"This archive file includes other files that may hide malware");
-}
-
-TEST_F(DownloadItemModelTailoredWarningTest,
-       GetBubbleUIInfoForTailoredWarning_AccountInfoStringWithAccount) {
-  SetupDownloadItemDefaults();
-  SetupTailoredWarningForItem(
-      download::DOWNLOAD_DANGER_TYPE_DANGEROUS_ACCOUNT_COMPROMISE,
-      TailoredVerdict::COOKIE_THEFT, {TailoredVerdict::ACCOUNT_INFO_STRING});
-  signin::IdentityManager* identity_manager =
-      IdentityManagerFactory::GetForProfile(profile());
-  signin::SetPrimaryAccount(identity_manager, "test@example.com",
-                            signin::ConsentLevel::kSignin);
-
-  DownloadUIModel::BubbleUIInfo bubble_ui_info = model().GetBubbleUIInfo();
-  // No primary button on download row view. Button only appears on subpage.
-  EXPECT_FALSE(bubble_ui_info.primary_button_command.has_value());
-  EXPECT_EQ(bubble_ui_info.subpage_buttons.size(), 1u);
-  EXPECT_EQ(bubble_ui_info.subpage_buttons[0].command,
-            DownloadCommands::Command::DISCARD);
-  EXPECT_TRUE(bubble_ui_info.subpage_buttons[0].is_prominent);
-  EXPECT_EQ(bubble_ui_info.warning_summary,
-            u"This file can harm your personal and social network accounts, "
-            u"including test@example.com");
-}
-
-TEST_F(DownloadItemModelTailoredWarningTest,
-       GetBubbleUIInfoForTailoredWarning_AccountInfoStringWithoutAccount) {
-  SetupDownloadItemDefaults();
-  SetupTailoredWarningForItem(
-      download::DOWNLOAD_DANGER_TYPE_DANGEROUS_ACCOUNT_COMPROMISE,
-      TailoredVerdict::COOKIE_THEFT, {TailoredVerdict::ACCOUNT_INFO_STRING});
-
-  DownloadUIModel::BubbleUIInfo bubble_ui_info = model().GetBubbleUIInfo();
-  // No primary button on download row view. Button only appears on subpage.
-  EXPECT_FALSE(bubble_ui_info.primary_button_command.has_value());
-  EXPECT_EQ(bubble_ui_info.subpage_buttons.size(), 1u);
-  EXPECT_EQ(bubble_ui_info.subpage_buttons[0].command,
-            DownloadCommands::Command::DISCARD);
-  EXPECT_TRUE(bubble_ui_info.subpage_buttons[0].is_prominent);
-  EXPECT_EQ(bubble_ui_info.warning_summary,
-            u"This file can harm your personal and social network accounts");
-}
-
 class DownloadItemModelTailoredWarningDisabledTest
     : public DownloadItemModelTailoredWarningTest {
  public:
diff --git a/chrome/browser/download/download_status_updater_lacros.cc b/chrome/browser/download/download_status_updater_lacros.cc
index 94afbb1..f248287 100644
--- a/chrome/browser/download/download_status_updater_lacros.cc
+++ b/chrome/browser/download/download_status_updater_lacros.cc
@@ -32,6 +32,7 @@
 #include "chrome/browser/profiles/profile_window.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/chrome_pages.h"
+#include "chrome/browser/ui/download/download_bubble_row_view_info.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chromeos/crosapi/mojom/download_controller.mojom.h"
 #include "chromeos/crosapi/mojom/download_status_updater.mojom.h"
@@ -72,22 +73,21 @@
              : nullptr;
 }
 
-bool IsCommandEnabled(DownloadItemModel& model,
+bool IsCommandEnabled(const DownloadBubbleRowViewInfo& info,
                       DownloadCommands::Command command) {
   // To support other commands, we may need to update checks below to also
-  // inspect `BubbleUIInfo` subpage buttons.
+  // inspect `DownloadBubbleSecurityViewInfo` subpage buttons.
   CHECK(command == DownloadCommands::CANCEL ||
         command == DownloadCommands::PAUSE ||
         command == DownloadCommands::RESUME);
 
-  const DownloadUIModel::BubbleUIInfo info = model.GetBubbleUIInfo();
-
-  // A command is enabled if `BubbleUIInfo` contains a quick action for it. This
-  // is preferred over non-`BubbleUIInfo`-based determination of command
-  // enablement as it takes more signals into account, e.g. if the download has
-  // been marked dangerous.
-  return base::Contains(info.quick_actions, command,
-                        &DownloadUIModel::BubbleUIInfo::QuickAction::command);
+  // A command is enabled if the `DownloadBubbleRowViewInfo` contains
+  // a quick action for it. This is preferred over
+  // non-`DownloadBubbleRowViewInfo`-based determination of command
+  // enablement as it takes more signals into account, e.g. if the
+  // download has been marked dangerous.
+  return base::Contains(info.quick_actions(), command,
+                        &DownloadBubbleRowViewInfo::QuickAction::command);
 }
 
 // Reads a specified image into binary data. Returns an empty string if
@@ -194,14 +194,19 @@
       return false;
     }
 
+    // TODO(crbug.com/40931768): Factor out the quick action selection
+    // so we don't need an entire DownloadBubbleRowViewInfo
+    DownloadBubbleRowViewInfo row_info(std::make_unique<DownloadItemModel>(
+        download,
+        std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>()));
     DownloadItemModel model(
         download, std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>());
     auto status = crosapi::mojom::DownloadStatus::New();
-    status->cancellable = IsCommandEnabled(model, DownloadCommands::CANCEL);
+    status->cancellable = IsCommandEnabled(row_info, DownloadCommands::CANCEL);
     status->full_path = download->GetFullPath();
     status->guid = download->GetGuid();
-    status->pausable = IsCommandEnabled(model, DownloadCommands::PAUSE);
-    status->resumable = IsCommandEnabled(model, DownloadCommands::RESUME);
+    status->pausable = IsCommandEnabled(row_info, DownloadCommands::PAUSE);
+    status->resumable = IsCommandEnabled(row_info, DownloadCommands::RESUME);
     status->state = download::download_item_utils::ConvertToMojoDownloadState(
         download->GetState());
     status->status_text = model.GetStatusText();
@@ -211,14 +216,11 @@
     status->received_bytes_deprecated = download->GetReceivedBytes();
     status->total_bytes_deprecated = download->GetTotalBytes();
 
-    // TODO(https://crbug.com/1482901): Update this code when refactoring the
-    // bubble UI info completes.
-    const DownloadUIModel::BubbleUIInfo info = model.GetBubbleUIInfo();
     auto progress = crosapi::mojom::DownloadProgress::New();
-    progress->loop = info.is_progress_bar_looping;
+    progress->loop = row_info.is_progress_bar_looping();
     progress->received_bytes = download->GetReceivedBytes();
     progress->total_bytes = download->GetTotalBytes();
-    progress->visible = info.has_progress_bar;
+    progress->visible = row_info.has_progress_bar();
     status->progress = std::move(progress);
 
     // If `task` exists and completes, copy the image generated by `task` to
diff --git a/chrome/browser/download/download_ui_model.cc b/chrome/browser/download/download_ui_model.cc
index 6e82350..597a501 100644
--- a/chrome/browser/download/download_ui_model.cc
+++ b/chrome/browser/download/download_ui_model.cc
@@ -761,699 +761,6 @@
   }
 }
 
-DownloadUIModel::BubbleUIInfo::SubpageButton::SubpageButton(
-    DownloadCommands::Command command,
-    std::u16string label,
-    bool is_prominent,
-    std::optional<ui::ColorId> color)
-    : command(command),
-      label(label),
-      is_prominent(is_prominent),
-      color(color) {}
-DownloadUIModel::BubbleUIInfo::QuickAction::QuickAction(
-    DownloadCommands::Command command,
-    const std::u16string& hover_text,
-    const gfx::VectorIcon* icon)
-    : command(command), hover_text(hover_text), icon(icon) {}
-DownloadUIModel::BubbleUIInfo::BubbleUIInfo() = default;
-DownloadUIModel::BubbleUIInfo::~BubbleUIInfo() = default;
-DownloadUIModel::BubbleUIInfo::BubbleUIInfo(const BubbleUIInfo& rhs) = default;
-DownloadUIModel::BubbleUIInfo& DownloadUIModel::BubbleUIInfo::AddSubpageSummary(
-    const std::u16string& summary) {
-  CHECK(!summary.empty());  // An empty summary is interpreted as no subpage
-  warning_summary = summary;
-  return *this;
-}
-DownloadUIModel::BubbleUIInfo&
-DownloadUIModel::BubbleUIInfo::AddSubpageSecondaryIconAndText(
-    const gfx::VectorIcon& icon,
-    const std::u16string& secondary_text) {
-  warning_secondary_icon = &icon;
-  warning_secondary_text = secondary_text;
-  return *this;
-}
-DownloadUIModel::BubbleUIInfo& DownloadUIModel::BubbleUIInfo::AddProgressBar() {
-  has_progress_bar = true;
-  return *this;
-}
-DownloadUIModel::BubbleUIInfo& DownloadUIModel::BubbleUIInfo::AddIconAndColor(
-    const gfx::VectorIcon& vector_icon,
-    ui::ColorId color_id) {
-  secondary_color = color_id;
-  icon_model_override = &vector_icon;
-  return *this;
-}
-DownloadUIModel::BubbleUIInfo&
-DownloadUIModel::BubbleUIInfo::AddSecondaryTextColor(ui::ColorId color_id) {
-  secondary_text_color = color_id;
-  return *this;
-}
-DownloadUIModel::BubbleUIInfo& DownloadUIModel::BubbleUIInfo::AddPrimaryButton(
-    DownloadCommands::Command command) {
-  primary_button_command = command;
-  return *this;
-}
-DownloadUIModel::BubbleUIInfo&
-DownloadUIModel::BubbleUIInfo::AddPrimarySubpageButton(
-    const std::u16string& label,
-    DownloadCommands::Command command) {
-  // The subpage of the bubble supports at most 2 buttons. The primary one must
-  // come first, then the secondary.
-  CHECK(subpage_buttons.empty());
-  subpage_buttons.emplace_back(command, label, /*is_prominent=*/true);
-  return *this;
-}
-DownloadUIModel::BubbleUIInfo&
-DownloadUIModel::BubbleUIInfo::AddSecondarySubpageButton(
-    const std::u16string& label,
-    DownloadCommands::Command command,
-    std::optional<ui::ColorId> color) {
-  // The subpage of the bubble supports at most 2 buttons. The primary one must
-  // come first, then the secondary.
-  CHECK(subpage_buttons.size() == 1);
-  subpage_buttons.emplace_back(command, label, /*is_prominent=*/false, color);
-  return *this;
-}
-
-DownloadUIModel::BubbleUIInfo&
-DownloadUIModel::BubbleUIInfo::SetProgressBarLooping() {
-  is_progress_bar_looping = true;
-  return *this;
-}
-
-DownloadUIModel::BubbleUIInfo& DownloadUIModel::BubbleUIInfo::AddQuickAction(
-    DownloadCommands::Command command,
-    const std::u16string& label,
-    const gfx::VectorIcon* icon) {
-  quick_actions.emplace_back(command, label, icon);
-  return *this;
-}
-
-DownloadUIModel::BubbleUIInfo& DownloadUIModel::BubbleUIInfo::AddLearnMoreLink(
-    int label_text_id,
-    int link_text_id,
-    DownloadCommands::Command command) {
-  size_t link_start_offset = 0;
-  std::u16string link_text = l10n_util::GetStringUTF16(link_text_id);
-  std::u16string label_and_link_text =
-      l10n_util::GetStringFUTF16(label_text_id, link_text, &link_start_offset);
-  learn_more_link = LabelWithLink{
-      label_and_link_text, LabelWithLink::LinkedRange{
-                               link_start_offset, link_text.length(), command}};
-  return *this;
-}
-
-DownloadUIModel::BubbleUIInfo& DownloadUIModel::BubbleUIInfo::AddLearnMoreLink(
-    const std::u16string& link_text,
-    DownloadCommands::Command command) {
-  learn_more_link = LabelWithLink{
-      link_text, LabelWithLink::LinkedRange{0u, link_text.length(), command}};
-  return *this;
-}
-
-DownloadUIModel::BubbleUIInfo&
-DownloadUIModel::BubbleUIInfo::DisableMainButton() {
-  main_button_enabled = false;
-  return *this;
-}
-
-// static
-DownloadUIModel::BubbleUIInfo DownloadUIModel::BubbleUIInfo::DangerousUiPattern(
-    const std::u16string& subpage_summary) {
-  return DownloadUIModel::BubbleUIInfo()
-      .AddSubpageSummary(subpage_summary)
-      .AddLearnMoreLink(
-          l10n_util::GetStringUTF16(
-              IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_BLOCKED_LEARN_MORE_LINK),
-          DownloadCommands::Command::LEARN_MORE_DOWNLOAD_BLOCKED)
-      .AddIconAndColor(features::IsChromeRefresh2023()
-                           ? vector_icons::kDangerousChromeRefreshIcon
-                           : vector_icons::kDangerousIcon,
-                       kColorDownloadItemIconDangerous)
-      .AddSecondaryTextColor(kColorDownloadItemTextDangerous)
-      .AddPrimarySubpageButton(
-          l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_DELETE_FROM_HISTORY),
-          DownloadCommands::Command::DISCARD);
-}
-
-// static
-DownloadUIModel::BubbleUIInfo
-DownloadUIModel::BubbleUIInfo::SuspiciousUiPattern(
-    const std::u16string& subpage_summary,
-    const std::u16string& secondary_subpage_button_label) {
-  return DownloadUIModel::BubbleUIInfo()
-      .AddSubpageSummary(subpage_summary)
-      .AddLearnMoreLink(
-          l10n_util::GetStringUTF16(
-              IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_BLOCKED_LEARN_MORE_LINK),
-          DownloadCommands::Command::LEARN_MORE_DOWNLOAD_BLOCKED)
-      .AddIconAndColor(features::IsChromeRefresh2023()
-                           ? kDownloadWarningIcon
-                           : vector_icons::kNotSecureWarningIcon,
-                       kColorDownloadItemIconWarning)
-      .AddSecondaryTextColor(kColorDownloadItemTextWarning)
-      .AddPrimarySubpageButton(
-          l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_DELETE_FROM_HISTORY),
-          DownloadCommands::Command::DISCARD)
-      .AddSecondarySubpageButton(secondary_subpage_button_label,
-                                 DownloadCommands::Command::KEEP);
-}
-
-ui::ColorId DownloadUIModel::BubbleUIInfo::GetColorForSecondaryText() const {
-  return secondary_text_color.value_or(secondary_color);
-}
-
-bool DownloadUIModel::BubbleUIInfo::HasSubpage() const {
-  return !warning_summary.empty();
-}
-
-DownloadUIModel::BubbleUIInfo DownloadUIModel::GetBubbleUIInfoForInterrupted(
-    FailState fail_state) const {
-  // Only handle danger types that are terminated in the interrupted state in
-  // this function. The other danger types are handled in
-  // `GetBubbleUIInfoForInProgressOrComplete`.
-  switch (GetDangerType()) {
-    case download::DOWNLOAD_DANGER_TYPE_BLOCKED_PASSWORD_PROTECTED:
-      return DownloadUIModel::BubbleUIInfo()
-          .AddSubpageSummary(l10n_util::GetStringUTF16(
-              IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_ENCRYPTED))
-          .AddIconAndColor(features::IsChromeRefresh2023()
-                               ? views::kInfoChromeRefreshIcon
-                               : views::kInfoIcon,
-                           kColorDownloadItemIconDangerous);
-    case download::DOWNLOAD_DANGER_TYPE_BLOCKED_TOO_LARGE:
-      return DownloadUIModel::BubbleUIInfo()
-          .AddSubpageSummary(l10n_util::GetStringUTF16(
-              IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_TOO_BIG))
-          .AddIconAndColor(features::IsChromeRefresh2023()
-                               ? views::kInfoChromeRefreshIcon
-                               : views::kInfoIcon,
-                           kColorDownloadItemIconDangerous);
-    case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK: {
-      if (enterprise_connectors::ShouldPromptReviewForDownload(
-              profile(), GetDownloadItem())) {
-        return DownloadUIModel::BubbleUIInfo()
-            .AddIconAndColor(features::IsChromeRefresh2023()
-                                 ? kDownloadWarningIcon
-                                 : vector_icons::kNotSecureWarningIcon,
-                             kColorDownloadItemIconDangerous)
-            .AddPrimaryButton(DownloadCommands::Command::REVIEW);
-      } else {
-        return DownloadUIModel::BubbleUIInfo()
-            .AddSubpageSummary(l10n_util::GetStringUTF16(
-                IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_SENSITIVE_CONTENT_BLOCK))
-            .AddIconAndColor(features::IsChromeRefresh2023()
-                                 ? views::kInfoChromeRefreshIcon
-                                 : views::kInfoIcon,
-                             kColorDownloadItemIconDangerous);
-      }
-    }
-    case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
-    case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
-    case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
-    case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_ACCOUNT_COMPROMISE:
-    case download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
-    case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
-    case download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT:
-    case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING:
-    case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING:
-    case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_LOCAL_PASSWORD_SCANNING:
-    case download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING:
-    case download::DOWNLOAD_DANGER_TYPE_ASYNC_LOCAL_PASSWORD_SCANNING:
-    case download::DOWNLOAD_DANGER_TYPE_BLOCKED_UNSUPPORTED_FILETYPE:
-    case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_FAILED:
-    case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_SAFE:
-    case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_OPENED_DANGEROUS:
-    case download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
-    case download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
-    case download::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
-    case download::DOWNLOAD_DANGER_TYPE_ALLOWLISTED_BY_POLICY:
-    case download::DOWNLOAD_DANGER_TYPE_MAX:
-      break;
-  }
-
-  switch (fail_state) {
-    case FailState::FILE_BLOCKED:
-      return DownloadUIModel::BubbleUIInfo()
-          .AddSubpageSummary(l10n_util::GetStringUTF16(
-              IDS_DOWNLOAD_BUBBLE_INTERRUPTED_SUBPAGE_SUMMARY_BLOCKED_ORGANIZATION))
-          .AddIconAndColor(features::IsChromeRefresh2023()
-                               ? views::kInfoChromeRefreshIcon
-                               : views::kInfoIcon,
-                           kColorDownloadItemIconDangerous);
-    case FailState::FILE_NAME_TOO_LONG:
-      return DownloadUIModel::BubbleUIInfo()
-          .AddSubpageSummary(l10n_util::GetStringUTF16(
-              IDS_DOWNLOAD_BUBBLE_INTERRUPTED_SUBPAGE_SUMMARY_PATH_TOO_LONG))
-          .AddIconAndColor(features::IsChromeRefresh2023()
-                               ? vector_icons::kFileDownloadOffChromeRefreshIcon
-                               : vector_icons::kFileDownloadOffIcon,
-                           kColorDownloadItemIconDangerous);
-    case FailState::FILE_NO_SPACE:
-      return DownloadUIModel::BubbleUIInfo()
-          .AddSubpageSummary(l10n_util::GetStringUTF16(
-              IDS_DOWNLOAD_BUBBLE_INTERRUPTED_SUBPAGE_SUMMARY_DISK_FULL))
-          .AddIconAndColor(features::IsChromeRefresh2023()
-                               ? vector_icons::kFileDownloadOffChromeRefreshIcon
-                               : vector_icons::kFileDownloadOffIcon,
-                           kColorDownloadItemIconDangerous);
-    case FailState::SERVER_UNAUTHORIZED:
-      return DownloadUIModel::BubbleUIInfo()
-          .AddSubpageSummary(l10n_util::GetStringUTF16(
-              IDS_DOWNLOAD_BUBBLE_INTERRUPTED_SUBPAGE_SUMMARY_FILE_UNAVAILABLE))
-          .AddIconAndColor(features::IsChromeRefresh2023()
-                               ? vector_icons::kFileDownloadOffChromeRefreshIcon
-                               : vector_icons::kFileDownloadOffIcon,
-                           kColorDownloadItemIconDangerous);
-    // No Retry in these cases.
-    case FailState::FILE_TOO_LARGE:
-    case FailState::FILE_VIRUS_INFECTED:
-    case FailState::FILE_SECURITY_CHECK_FAILED:
-    case FailState::FILE_ACCESS_DENIED:
-    case FailState::SERVER_FORBIDDEN:
-    case FailState::FILE_SAME_AS_SOURCE:
-    case FailState::SERVER_BAD_CONTENT:
-      return DownloadUIModel::BubbleUIInfo().AddIconAndColor(
-          features::IsChromeRefresh2023()
-              ? vector_icons::kFileDownloadOffChromeRefreshIcon
-              : vector_icons::kFileDownloadOffIcon,
-          kColorDownloadItemIconDangerous);
-    // Try resume if possible or retry if not in these cases, and in the default
-    // case.
-    case FailState::NETWORK_INVALID_REQUEST:
-    case FailState::NETWORK_FAILED:
-    case FailState::NETWORK_TIMEOUT:
-    case FailState::NETWORK_DISCONNECTED:
-    case FailState::NETWORK_SERVER_DOWN:
-    case FailState::FILE_TRANSIENT_ERROR:
-    case FailState::USER_SHUTDOWN:
-    case FailState::CRASH:
-    case FailState::SERVER_CONTENT_LENGTH_MISMATCH:
-    case FailState::SERVER_NO_RANGE:
-    case FailState::SERVER_CROSS_ORIGIN_REDIRECT:
-    case FailState::FILE_FAILED:
-    case FailState::FILE_HASH_MISMATCH:
-    case FailState::SERVER_FAILED:
-    case FailState::SERVER_CERT_PROBLEM:
-    case FailState::SERVER_UNREACHABLE:
-    case FailState::FILE_TOO_SHORT:
-      break;
-    // Not possible because the USER_CANCELED fail state does not allow a call
-    // into this function
-    case FailState::USER_CANCELED:
-    // Deprecated
-    case FailState::NETWORK_INSTABILITY:
-    case FailState::CANNOT_DOWNLOAD:
-      NOTREACHED();
-      break;
-    case FailState::NO_FAILURE:
-      return DownloadUIModel::BubbleUIInfo();
-  }
-
-  DownloadUIModel::BubbleUIInfo bubble_ui_info =
-      DownloadUIModel::BubbleUIInfo()
-          .AddIconAndColor(features::IsChromeRefresh2023()
-                               ? vector_icons::kFileDownloadOffChromeRefreshIcon
-                               : vector_icons::kFileDownloadOffIcon,
-                           kColorDownloadItemIconDangerous)
-          .AddPrimaryButton(CanResume() ? DownloadCommands::Command::RESUME
-                                        : DownloadCommands::Command::RETRY);
-  return bubble_ui_info;
-}
-
-DownloadUIModel::BubbleUIInfo
-DownloadUIModel::GetBubbleUIInfoForInProgressOrComplete() const {
-  switch (GetInsecureDownloadStatus()) {
-    case download::DownloadItem::InsecureDownloadStatus::BLOCK:
-    case download::DownloadItem::InsecureDownloadStatus::WARN:
-      // The insecure warning uses the suspicious warning pattern but has a
-      // primary button to keep the file.
-      return DownloadUIModel::BubbleUIInfo::SuspiciousUiPattern(
-                 l10n_util::GetStringUTF16(
-                     IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_INSECURE),
-                 l10n_util::GetStringUTF16(
-                     IDS_DOWNLOAD_BUBBLE_CONTINUE_INSECURE_FILE))
-          .AddPrimaryButton(DownloadCommands::Command::KEEP);
-    case download::DownloadItem::InsecureDownloadStatus::UNKNOWN:
-    case download::DownloadItem::InsecureDownloadStatus::SAFE:
-    case download::DownloadItem::InsecureDownloadStatus::VALIDATED:
-    case download::DownloadItem::InsecureDownloadStatus::SILENT_BLOCK:
-      break;
-  }
-
-  if (enterprise_connectors::ShouldPromptReviewForDownload(profile(),
-                                                           GetDownloadItem())) {
-    switch (GetDangerType()) {
-      case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
-        return DownloadUIModel::BubbleUIInfo()
-            .AddIconAndColor(features::IsChromeRefresh2023()
-                                 ? vector_icons::kDangerousChromeRefreshIcon
-                                 : vector_icons::kDangerousIcon,
-                             kColorDownloadItemIconDangerous)
-            .AddPrimaryButton(DownloadCommands::Command::REVIEW);
-      case download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
-        return DownloadUIModel::BubbleUIInfo()
-            .AddIconAndColor(features::IsChromeRefresh2023()
-                                 ? kDownloadWarningIcon
-                                 : vector_icons::kNotSecureWarningIcon,
-                             kColorDownloadItemIconWarning)
-            .AddSecondaryTextColor(kColorDownloadItemTextWarning)
-            .AddPrimaryButton(DownloadCommands::Command::REVIEW);
-      case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING:
-        return DownloadUIModel::BubbleUIInfo()
-            .AddIconAndColor(features::IsChromeRefresh2023()
-                                 ? views::kInfoChromeRefreshIcon
-                                 : views::kInfoIcon,
-                             kColorDownloadItemIconWarning)
-            .AddSecondaryTextColor(kColorDownloadItemTextWarning)
-            .AddPrimaryButton(DownloadCommands::Command::REVIEW);
-      default:
-        break;
-    }
-  }
-
-  if (TailoredWarningType type = GetTailoredWarningType();
-      type != TailoredWarningType::kNoTailoredWarning) {
-    return GetBubbleUIInfoForTailoredWarning(type);
-  }
-
-  DownloadUIModel::BubbleUIInfo ui_info;
-  switch (GetDangerType()) {
-    case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
-      if (IsExtensionDownload()) {
-        return DownloadUIModel::BubbleUIInfo::SuspiciousUiPattern(
-            l10n_util::GetStringFUTF16(
-                IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_UNKNOWN_SOURCE,
-                l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE)),
-            l10n_util::GetStringUTF16(
-                IDS_DOWNLOAD_BUBBLE_CONTINUE_SUSPICIOUS_FILE));
-      }
-      if (WasSafeBrowsingVerdictObtained(GetDownloadItem())) {
-        return DownloadUIModel::BubbleUIInfo::SuspiciousUiPattern(
-            l10n_util::GetStringUTF16(
-                IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_DANGEROUS_FILE_TYPE),
-            l10n_util::GetStringUTF16(
-                IDS_DOWNLOAD_BUBBLE_CONTINUE_SUSPICIOUS_FILE));
-      }
-      if (ShouldShowWarningForNoSafeBrowsing(profile())) {
-        return GetBubbleUIInfoForFileTypeWarningNoSafeBrowsing();
-      }
-      return DownloadUIModel::BubbleUIInfo::SuspiciousUiPattern(
-          l10n_util::GetStringUTF16(
-              IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_DANGEROUS_FILE_TYPE),
-          l10n_util::GetStringUTF16(
-              IDS_DOWNLOAD_BUBBLE_CONTINUE_UNVERIFIED_FILE));
-
-    case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
-    case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
-    case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_ACCOUNT_COMPROMISE:
-    case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
-      return DownloadUIModel::BubbleUIInfo::DangerousUiPattern(
-          l10n_util::GetStringUTF16(
-              IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_DANGEROUS));
-
-    case download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
-      return DownloadUIModel::BubbleUIInfo::DangerousUiPattern(
-          l10n_util::GetStringUTF16(
-              IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_DECEPTIVE));
-
-    case download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: {
-      bool request_ap_verdicts = false;
-#if BUILDFLAG(FULL_SAFE_BROWSING)
-      request_ap_verdicts =
-          safe_browsing::AdvancedProtectionStatusManagerFactory::GetForProfile(
-              profile())
-              ->IsUnderAdvancedProtection();
-#endif
-      if (request_ap_verdicts) {
-        return DownloadUIModel::BubbleUIInfo()
-            .AddSubpageSummary(l10n_util::GetStringUTF16(
-                IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_ADVANCED_PROTECTION))
-            .AddIconAndColor(features::IsChromeRefresh2023()
-                                 ? kDownloadWarningIcon
-                                 : vector_icons::kNotSecureWarningIcon,
-                             kColorDownloadItemIconWarning)
-            .AddSecondaryTextColor(kColorDownloadItemTextWarning)
-            .AddPrimarySubpageButton(
-                l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_DELETE),
-                DownloadCommands::Command::DISCARD)
-            .AddSecondarySubpageButton(
-                l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_CONTINUE),
-                DownloadCommands::Command::KEEP, kColorDownloadItemTextWarning);
-      }
-      return DownloadUIModel::BubbleUIInfo::SuspiciousUiPattern(
-          l10n_util::GetStringUTF16(
-              IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_UNCOMMON_FILE),
-          l10n_util::GetStringUTF16(
-              IDS_DOWNLOAD_BUBBLE_CONTINUE_SUSPICIOUS_FILE));
-    }
-    case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING:
-      return DownloadUIModel::BubbleUIInfo()
-          .AddSubpageSummary(l10n_util::GetStringUTF16(
-              IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_SENSITIVE_CONTENT_WARNING))
-          .AddIconAndColor(features::IsChromeRefresh2023()
-                               ? views::kInfoChromeRefreshIcon
-                               : views::kInfoIcon,
-                           kColorDownloadItemIconWarning)
-          .AddSecondaryTextColor(kColorDownloadItemTextWarning)
-          .AddPrimaryButton(DownloadCommands::Command::DISCARD)
-          .AddPrimarySubpageButton(
-              l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_DELETE),
-              DownloadCommands::Command::DISCARD)
-          .AddSecondarySubpageButton(
-              l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_CONTINUE),
-              DownloadCommands::Command::KEEP, kColorDownloadItemTextWarning);
-    case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING: {
-      ui_info = DownloadUIModel::BubbleUIInfo()
-                    .AddIconAndColor(features::IsChromeRefresh2023()
-                                         ? kDownloadWarningIcon
-                                         : vector_icons::kNotSecureWarningIcon,
-                                     kColorDownloadItemIconWarning)
-                    .AddSecondaryTextColor(kColorDownloadItemTextWarning);
-      std::u16string subpage_text = l10n_util::GetStringFUTF16(
-          IsEncryptedArchive()
-              ? IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_DEEP_SCANNING_PROMPT_ENCRYPTED_ARCHIVE
-              : IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_DEEP_SCANNING_PROMPT_UPDATED,
-          u"\n\n");
-      ui_info.AddSubpageSummary(subpage_text)
-          .AddPrimarySubpageButton(
-              l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_SCAN_UPDATED),
-              DownloadCommands::Command::DEEP_SCAN)
-          .AddSecondarySubpageButton(
-              l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_OPEN_UPDATED),
-              DownloadCommands::Command::BYPASS_DEEP_SCANNING);
-      return ui_info;
-    }
-    case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_LOCAL_PASSWORD_SCANNING: {
-      ui_info = DownloadUIModel::BubbleUIInfo()
-                    .AddIconAndColor(features::IsChromeRefresh2023()
-                                         ? kDownloadWarningIcon
-                                         : vector_icons::kNotSecureWarningIcon,
-                                     kColorDownloadItemIconWarning)
-                    .AddSecondaryTextColor(kColorDownloadItemTextWarning);
-      std::u16string subpage_text = l10n_util::GetStringFUTF16(
-          IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_DEEP_SCANNING_PROMPT_LOCAL_DECRYPTION,
-          u"\n\n");
-      ui_info.AddSubpageSummary(subpage_text)
-          .AddPrimarySubpageButton(
-              l10n_util::GetStringUTF16(
-                  IDS_DOWNLOAD_BUBBLE_ACCEPT_LOCAL_DECRYPTION),
-              // These download commands are not propagated
-              // to the DownloadItem. Instead they are handled specially in
-              // DownloadBubbleSecurityView::ProcessButtonClick. That makes it
-              // okay that the we aren't really prompting for a deep scan.
-              // TODO(crbug/1482901): Remove this by creating a dedicated View
-              // for the local decryption prompt which directly handles teh
-              // button presses.
-              DownloadCommands::Command::DEEP_SCAN)
-          .AddSecondarySubpageButton(
-              l10n_util::GetStringUTF16(
-                  IDS_DOWNLOAD_BUBBLE_BYPASS_LOCAL_DECRYPTION),
-              DownloadCommands::Command::KEEP);
-      return ui_info;
-    }
-    case download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING:
-      ui_info =
-          DownloadUIModel::BubbleUIInfo()
-              .AddProgressBar()
-              .SetProgressBarLooping()
-              .AddSubpageSummary(l10n_util::GetStringUTF16(
-                  IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_ASYNC_SCANNING))
-              .AddSubpageSecondaryIconAndText(
-                  vector_icons::kDocumentScannerIcon,
-                  download::DoesDownloadConnectorBlock(profile(), GetURL())
-                      ? l10n_util::GetStringUTF16(
-                            IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_ASYNC_SCANNING_ENTERPRISE_SECONDARY)
-                      : l10n_util::GetStringUTF16(
-                            IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_ASYNC_SCANNING_SECONDARY))
-              .AddIconAndColor(features::IsChromeRefresh2023()
-                                   ? kDownloadWarningIcon
-                                   : vector_icons::kNotSecureWarningIcon,
-                               kColorDownloadItemIconWarning)
-              .AddPrimarySubpageButton(
-                  l10n_util::GetStringUTF16(
-                      IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_ASYNC_SCANNING_DISCARD),
-                  DownloadCommands::Command::DISCARD);
-      ui_info.subpage_buttons[0].is_prominent = false;
-      if (!download::DoesDownloadConnectorBlock(profile(), GetURL())) {
-        ui_info.AddSecondarySubpageButton(
-            l10n_util::GetStringUTF16(
-                IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_ASYNC_SCANNING_CANCEL),
-            DownloadCommands::Command::CANCEL_DEEP_SCAN,
-            ui::kColorButtonForeground);
-      }
-      return ui_info;
-    case download::DOWNLOAD_DANGER_TYPE_ASYNC_LOCAL_PASSWORD_SCANNING: {
-      std::u16string subpage_text = l10n_util::GetStringFUTF16(
-          IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_LOCAL_DECRYPTION_IN_PROGRESS,
-          u"\n\n");
-      ui_info = DownloadUIModel::BubbleUIInfo()
-                    .AddProgressBar()
-                    .SetProgressBarLooping()
-                    .AddSubpageSummary(subpage_text)
-                    .AddIconAndColor(features::IsChromeRefresh2023()
-                                         ? kDownloadWarningIcon
-                                         : vector_icons::kNotSecureWarningIcon,
-                                     kColorDownloadItemIconWarning)
-                    // These download commands are not propagated
-                    // to the DownloadItem. Instead they are handled specially
-                    // in DownloadBubbleSecurityView::ProcessButtonClick. That
-                    // means the semantics don't have to line up with the actual
-                    // behavior of the download command.
-                    // TODO(crbug/1482901): Remove this by creating a dedicated
-                    // View for the local decryption prompt which directly
-                    // handles teh button presses.
-                    .AddPrimarySubpageButton(
-                        l10n_util::GetStringUTF16(
-                            IDS_DOWNLOAD_BUBBLE_LOCAL_DECRYPTION_CANCEL),
-                        DownloadCommands::Command::CANCEL)
-                    .AddSecondarySubpageButton(
-                        l10n_util::GetStringUTF16(
-                            IDS_DOWNLOAD_BUBBLE_BYPASS_LOCAL_DECRYPTION),
-                        DownloadCommands::Command::BYPASS_DEEP_SCANNING);
-      return ui_info;
-    }
-    case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_FAILED:
-      return DownloadUIModel::BubbleUIInfo()
-          .AddIconAndColor(features::IsChromeRefresh2023()
-                               ? kDownloadWarningIcon
-                               : vector_icons::kNotSecureWarningIcon,
-                           kColorDownloadItemIconWarning)
-          .AddPrimaryButton(DownloadCommands::Command::OPEN_WHEN_COMPLETE)
-          .AddSecondaryTextColor(kColorDownloadItemTextWarning)
-          .DisableMainButton();
-    case download::DOWNLOAD_DANGER_TYPE_BLOCKED_PASSWORD_PROTECTED:
-    case download::DOWNLOAD_DANGER_TYPE_BLOCKED_TOO_LARGE:
-    case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK:
-    case download::DOWNLOAD_DANGER_TYPE_BLOCKED_UNSUPPORTED_FILETYPE:
-    case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_SAFE:
-    case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_OPENED_DANGEROUS:
-    case download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
-    case download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
-    case download::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
-    case download::DOWNLOAD_DANGER_TYPE_ALLOWLISTED_BY_POLICY:
-    case download::DOWNLOAD_DANGER_TYPE_MAX:
-      break;
-  }
-
-  // Add primary button/quick actions for in-progress (paused or active), and
-  // completed downloads
-  bool has_progress_bar = GetState() == DownloadItem::IN_PROGRESS;
-  BubbleUIInfo bubble_ui_info = DownloadUIModel::BubbleUIInfo();
-  if (has_progress_bar) {
-    bubble_ui_info.AddProgressBar();
-    if (IsPaused()) {
-      bubble_ui_info.AddQuickAction(
-          DownloadCommands::Command::RESUME,
-          l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_RESUME_QUICK_ACTION),
-          features::IsChromeRefresh2023()
-              ? &vector_icons::kPlayArrowChromeRefreshIcon
-              : &vector_icons::kPlayArrowIcon);
-      bubble_ui_info.AddQuickAction(
-          DownloadCommands::Command::CANCEL,
-          l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_CANCEL_QUICK_ACTION),
-          features::IsChromeRefresh2023()
-              ? &vector_icons::kCancelChromeRefreshIcon
-              : &vector_icons::kCancelIcon);
-    } else {
-      bubble_ui_info.AddQuickAction(
-          DownloadCommands::Command::PAUSE,
-          l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_PAUSE_QUICK_ACTION),
-          features::IsChromeRefresh2023()
-              ? &vector_icons::kPauseChromeRefreshIcon
-              : &vector_icons::kPauseIcon);
-      bubble_ui_info.AddQuickAction(
-          DownloadCommands::Command::CANCEL,
-          l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_CANCEL_QUICK_ACTION),
-          features::IsChromeRefresh2023()
-              ? &vector_icons::kCancelChromeRefreshIcon
-              : &vector_icons::kCancelIcon);
-    }
-  } else {
-    bubble_ui_info.AddQuickAction(
-        DownloadCommands::Command::SHOW_IN_FOLDER,
-        l10n_util::GetStringUTF16(
-            IDS_DOWNLOAD_BUBBLE_SHOW_IN_FOLDER_QUICK_ACTION),
-        features::IsChromeRefresh2023()
-            ? &vector_icons::kFolderChromeRefreshIcon
-            : &vector_icons::kFolderIcon);
-    bubble_ui_info.AddQuickAction(
-        DownloadCommands::Command::OPEN_WHEN_COMPLETE,
-        l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_OPEN_QUICK_ACTION),
-        features::IsChromeRefresh2023()
-            ? &vector_icons::kLaunchChromeRefreshIcon
-            : &kOpenInNewIcon);
-  }
-  return bubble_ui_info;
-}
-
-DownloadUIModel::BubbleUIInfo
-DownloadUIModel::GetBubbleUIInfoForTailoredWarning(
-    TailoredWarningType tailored_warning_type) const {
-  NOTREACHED();
-  return DownloadUIModel::BubbleUIInfo();
-}
-
-DownloadUIModel::BubbleUIInfo
-DownloadUIModel::GetBubbleUIInfoForFileTypeWarningNoSafeBrowsing() const {
-  BubbleUIInfo ui_info = BubbleUIInfo::SuspiciousUiPattern(
-      l10n_util::GetStringUTF16(
-          IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_NO_SAFE_BROWSING),
-      l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_CONTINUE_UNVERIFIED_FILE));
-  // Clear the "Learn why Chrome..." link. If the user is not capable of turning
-  // on SB, do not show the default link and label.
-  ui_info.learn_more_link = std::nullopt;
-  if (CanUserTurnOnSafeBrowsing(profile())) {
-    ui_info.AddLearnMoreLink(
-        IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_SAFE_BROWSING_SETTING_LABEL,
-        IDS_DOWNLOAD_BUBBLE_SUBPAGE_SUMMARY_WARNING_SAFE_BROWSING_SETTING_LINK,
-        DownloadCommands::Command::OPEN_SAFE_BROWSING_SETTING);
-  }
-  return ui_info;
-}
-
-DownloadUIModel::BubbleUIInfo DownloadUIModel::GetBubbleUIInfo() const {
-  switch (GetState()) {
-    case DownloadItem::IN_PROGRESS:
-    case DownloadItem::COMPLETE:
-      return GetBubbleUIInfoForInProgressOrComplete();
-    case DownloadItem::INTERRUPTED: {
-      const FailState fail_state = GetLastFailState();
-      if (fail_state != FailState::USER_CANCELED) {
-        return GetBubbleUIInfoForInterrupted(fail_state);
-      }
-    }
-      [[fallthrough]];
-    case DownloadItem::CANCELLED:
-    case DownloadItem::MAX_DOWNLOAD_STATE:
-      return DownloadUIModel::BubbleUIInfo().AddIconAndColor(
-          features::IsChromeRefresh2023()
-              ? vector_icons::kFileDownloadOffChromeRefreshIcon
-              : vector_icons::kFileDownloadOffIcon,
-          ui::kColorSecondaryForeground);
-  }
-}
-
 DownloadUIModel::TailoredWarningType DownloadUIModel::GetTailoredWarningType()
     const {
   return TailoredWarningType::kNoTailoredWarning;
diff --git a/chrome/browser/download/download_ui_model.h b/chrome/browser/download/download_ui_model.h
index f40cc15..62fd5c9 100644
--- a/chrome/browser/download/download_ui_model.h
+++ b/chrome/browser/download/download_ui_model.h
@@ -123,144 +123,6 @@
     std::u16string GetBubbleWarningStatusText() const;
   };
 
-#if !BUILDFLAG(IS_ANDROID)
-  // Keep UI logic in this class in sync with DownloadBubbleRowViewInfo.
-  // TODO(crbug.com/1482901): Unify the logic.
-  struct BubbleUIInfo {
-    struct SubpageButton {
-      DownloadCommands::Command command;
-      std::u16string label;
-      bool is_prominent = false;
-
-      // Controls the text color of the button. Only applied for some secondary
-      // buttons.
-      std::optional<ui::ColorId> color;
-
-      SubpageButton(DownloadCommands::Command command,
-                    std::u16string label,
-                    bool is_prominent,
-                    std::optional<ui::ColorId> color = std::nullopt);
-    };
-
-    struct QuickAction {
-      DownloadCommands::Command command;
-      std::u16string hover_text;
-      raw_ptr<const gfx::VectorIcon> icon = nullptr;
-      QuickAction(DownloadCommands::Command command,
-                  const std::u16string& hover_text,
-                  const gfx::VectorIcon* icon);
-    };
-
-    struct LabelWithLink {
-      struct LinkedRange {
-        // The offset where the link text (i.e. "Chrome blocks some downloads")
-        // starts, with respect to the label string containing it.
-        size_t start_offset = 0;
-        // Link text length.
-        size_t length = 0;
-        // Action to perform when the link is clicked.
-        DownloadCommands::Command command;
-      };
-
-      // The entire label string with link, i.e. "Learn why Chrome blocks some
-      // downloads".
-      std::u16string label_and_link_text;
-      // The link info. Note this assumes that the text contains exactly one
-      // link.
-      LinkedRange linked_range;
-    };
-
-    // has a progress bar and a cancel button.
-    bool has_progress_bar = false;
-    bool is_progress_bar_looping = false;
-    // kColorAlertHighSeverity, kColorAlertMediumSeverityIcon, or
-    // kColorSecondaryForeground
-    ui::ColorId secondary_color = ui::kColorSecondaryForeground;
-    // Color used for alert text, which may be different from |secondary_color|,
-    // used for icons. If this is nullopt, |secondary_color| will be used for
-    // text.
-    std::optional<ui::ColorId> secondary_text_color = std::nullopt;
-
-    // Override icon
-    raw_ptr<const gfx::VectorIcon> icon_model_override = nullptr;
-
-    // Subpage summary of the download warning
-    std::u16string warning_summary;
-
-    // Secondary label for the subpage summary
-    std::u16string warning_secondary_text;
-
-    // Icon for the secondary text in the subpage
-    raw_ptr<const gfx::VectorIcon> warning_secondary_icon = nullptr;
-
-    // The command for the primary button
-    std::optional<DownloadCommands::Command> primary_button_command;
-
-    // List of quick actions
-    std::vector<QuickAction> quick_actions;
-
-    // Subpage buttons
-    std::vector<SubpageButton> subpage_buttons;
-
-    // Text with link to go at the bottom of the subpage summary, such as "Learn
-    // why Chrome blocks some downloads".
-    std::optional<LabelWithLink> learn_more_link;
-
-    // Whether the main button should be enabled. When true, the main button
-    // will either:
-    // - Open the subpage, if it exists
-    // - Open the download, if no subpage exists
-    bool main_button_enabled = true;
-
-    BubbleUIInfo();
-    ~BubbleUIInfo();
-    BubbleUIInfo(const BubbleUIInfo&);
-    BubbleUIInfo& AddSubpageSummary(const std::u16string& summary);
-    BubbleUIInfo& AddSubpageSecondaryIconAndText(
-        const gfx::VectorIcon& icon,
-        const std::u16string& secondary_text);
-    BubbleUIInfo& AddProgressBar();
-    BubbleUIInfo& AddIconAndColor(const gfx::VectorIcon& vector_icon,
-                                  ui::ColorId color_id);
-    BubbleUIInfo& AddSecondaryTextColor(ui::ColorId color_id);
-    BubbleUIInfo& AddPrimaryButton(DownloadCommands::Command command);
-    // Add button to the subpage. Only two buttons are supported.
-    // The first one added is the primary, and the second one the secondary.
-    BubbleUIInfo& AddPrimarySubpageButton(const std::u16string& label,
-                                          DownloadCommands::Command command);
-    BubbleUIInfo& AddSecondarySubpageButton(
-        const std::u16string& label,
-        DownloadCommands::Command command,
-        std::optional<ui::ColorId> color = std::nullopt);
-    BubbleUIInfo& SetProgressBarLooping();
-    BubbleUIInfo& AddQuickAction(DownloadCommands::Command command,
-                                 const std::u16string& label,
-                                 const gfx::VectorIcon* icon);
-    // Add a learn_more_link with the specified message ids and command when
-    // clicked. Assumes that the message given by label_text_id has a
-    // placeholder where the message specified by link_text_id should go.
-    BubbleUIInfo& AddLearnMoreLink(int label_text_id,
-                                   int link_text_id,
-                                   DownloadCommands::Command command);
-    // Same as above but takes the link text string itself, and assumes that
-    // the whole string should be linked, rather than a substring.
-    BubbleUIInfo& AddLearnMoreLink(const std::u16string& link_text,
-                                   DownloadCommands::Command command);
-
-    BubbleUIInfo& DisableMainButton();
-
-    // Set common characteristics for dangerous or suspicious downloads.
-    static BubbleUIInfo DangerousUiPattern(
-        const std::u16string& subpage_summary);
-    static BubbleUIInfo SuspiciousUiPattern(
-        const std::u16string& subpage_summary,
-        const std::u16string& secondary_subpage_button_label);
-
-    ui::ColorId GetColorForSecondaryText() const;
-    bool HasSubpage() const;
-  };
-#endif
-
   using DownloadUIModelPtr = std::unique_ptr<DownloadUIModel>;
 
   DownloadUIModel();
@@ -585,15 +447,6 @@
   virtual void ExecuteCommand(DownloadCommands* download_commands,
                               DownloadCommands::Command command);
 
-  // Gets the information about the download bubbles subpage.
-  BubbleUIInfo GetBubbleUIInfo() const;
-  BubbleUIInfo GetBubbleUIInfoForInterrupted(
-      offline_items_collection::FailState fail_state) const;
-  BubbleUIInfo GetBubbleUIInfoForInProgressOrComplete() const;
-  virtual BubbleUIInfo GetBubbleUIInfoForTailoredWarning(
-      TailoredWarningType tailored_warning_type) const;
-  BubbleUIInfo GetBubbleUIInfoForFileTypeWarningNoSafeBrowsing() const;
-
   // Returns |true| if this download should be displayed in the download bubble.
   // Note that this may return true even if the download bubble is not enabled
   // on the platform.
diff --git a/chrome/browser/enterprise/idle/idle_service_interactive_uitest.cc b/chrome/browser/enterprise/idle/idle_service_interactive_uitest.cc
index 144b633..4cb222c 100644
--- a/chrome/browser/enterprise/idle/idle_service_interactive_uitest.cc
+++ b/chrome/browser/enterprise/idle/idle_service_interactive_uitest.cc
@@ -116,6 +116,7 @@
   void SetUpInProcessBrowserTestFixture() override {
     task_runner_ = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
     polling_service().SetTaskRunnerForTest(task_runner_);
+    polling_service().SetPollIntervalForTest(base::Seconds(1));
 
     auto time_provider =
         std::make_unique<testing::NiceMock<MockIdleTimeProvider>>();
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.cc b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
index 36ffca1..2b14e30 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_api.cc
+++ b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
@@ -410,58 +410,6 @@
   }
 }
 
-// Adds `site` to the extension's set of runtime granted host permissions.
-void GrantPermissionsForSite(content::BrowserContext* context,
-                             const Extension& extension,
-                             const URLPattern& site,
-                             base::OnceClosure done_callback) {
-  URLPatternSet new_host_permissions({site});
-  PermissionsUpdater(context).GrantRuntimePermissions(
-      extension,
-      PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
-                    new_host_permissions.Clone(), new_host_permissions.Clone()),
-      std::move(done_callback));
-}
-
-// Revokes the extension's access to `site` in its host permissions.
-void RevokePermissionsForSite(content::BrowserContext* context,
-                              const Extension& extension,
-                              const URLPattern& site,
-                              base::OnceClosure done_callback) {
-  // Revoke all sites which have some intersection with `site` from the
-  // extension's set of runtime granted host permissions.
-  auto* permissions_manager = PermissionsManager::Get(context);
-  std::unique_ptr<const PermissionSet> runtime_granted_permissions =
-      permissions_manager->GetRuntimePermissionsFromPrefs(extension);
-
-  URLPatternSet explicit_hosts;
-  for (const auto& pattern : runtime_granted_permissions->explicit_hosts()) {
-    if (site.OverlapsWith(pattern)) {
-      explicit_hosts.AddPattern(pattern);
-    }
-  }
-  URLPatternSet scriptable_hosts;
-  for (const auto& pattern : runtime_granted_permissions->scriptable_hosts()) {
-    if (site.OverlapsWith(pattern)) {
-      scriptable_hosts.AddPattern(pattern);
-    }
-  }
-
-  std::unique_ptr<const PermissionSet> permissions_to_remove =
-      PermissionSet::CreateIntersection(
-          PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
-                        std::move(explicit_hosts), std::move(scriptable_hosts)),
-          *permissions_manager->GetRevokablePermissions(extension),
-          URLPatternSet::IntersectionBehavior::kDetailed);
-  if (permissions_to_remove->IsEmpty()) {
-    std::move(done_callback).Run();
-    return;
-  }
-
-  PermissionsUpdater(context).RevokeRuntimePermissions(
-      extension, *permissions_to_remove, std::move(done_callback));
-}
-
 }  // namespace
 
 namespace ChoosePath = api::developer_private::ChoosePath;
@@ -2649,8 +2597,7 @@
           modifier.RemoveAllGrantedHostPermissions();
           done_callback.Run();
         } else {
-          RevokePermissionsForSite(browser_context(), extension, parsed_site,
-                                   done_callback);
+          modifier.RemoveHostPermissions(parsed_site, done_callback);
         }
         break;
       case developer::HostAccess::kOnSpecificSites:
@@ -2661,8 +2608,7 @@
           modifier.SetWithholdHostPermissions(true);
           modifier.RemoveAllGrantedHostPermissions();
         }
-        GrantPermissionsForSite(browser_context(), extension, parsed_site,
-                                done_callback);
+        modifier.GrantHostPermission(parsed_site, done_callback);
         break;
       case developer::HostAccess::kOnAllSites:
         modifier.SetWithholdHostPermissions(false);
diff --git a/chrome/browser/extensions/api/image_writer_private/write_from_url_operation_unittest.cc b/chrome/browser/extensions/api/image_writer_private/write_from_url_operation_unittest.cc
index 5740289..078e430 100644
--- a/chrome/browser/extensions/api/image_writer_private/write_from_url_operation_unittest.cc
+++ b/chrome/browser/extensions/api/image_writer_private/write_from_url_operation_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/containers/span.h"
 #include "base/functional/bind.h"
 #include "base/run_loop.h"
 #include "chrome/browser/extensions/api/image_writer_private/error_constants.h"
diff --git a/chrome/browser/extensions/scripting_permissions_modifier.cc b/chrome/browser/extensions/scripting_permissions_modifier.cc
index 1216668..f24a29f 100644
--- a/chrome/browser/extensions/scripting_permissions_modifier.cc
+++ b/chrome/browser/extensions/scripting_permissions_modifier.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/extensions/scripting_permissions_modifier.h"
 
+#include "base/functional/callback_helpers.h"
 #include "chrome/browser/extensions/permissions_updater.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
@@ -44,16 +45,17 @@
   extension_prefs_->SetWithholdingPermissions(extension_->id(),
                                               should_withhold);
 
-  if (should_withhold)
-    WithholdHostPermissions();
-  else
+  if (should_withhold) {
+    RemoveAllGrantedHostPermissions();
+  } else {
     GrantWithheldHostPermissions();
+  }
 }
 
 void ScriptingPermissionsModifier::GrantHostPermission(const GURL& url) {
-  DCHECK(permissions_manager_->CanAffectExtension(*extension_));
+  CHECK(permissions_manager_->CanAffectExtension(*extension_));
   // Check that we don't grant host permission to a restricted URL.
-  DCHECK(
+  CHECK(
       !extension_->permissions_data()->IsRestrictedUrl(url, /*error=*/nullptr))
       << "Cannot grant access to a restricted URL.";
 
@@ -62,39 +64,72 @@
   URLPatternSet scriptable_hosts;
   scriptable_hosts.AddOrigin(UserScript::ValidUserScriptSchemes(), url);
 
-  PermissionsUpdater(browser_context_)
-      .GrantRuntimePermissions(
-          *extension_,
-          PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
-                        std::move(explicit_hosts), std::move(scriptable_hosts)),
-          base::DoNothing());
+  GrantHostPermission(std::move(explicit_hosts), std::move(scriptable_hosts),
+                      base::DoNothing());
+}
+
+// Adds `site` to the extension's set of runtime granted host permissions.
+void ScriptingPermissionsModifier::GrantHostPermission(
+    const URLPattern& site,
+    base::OnceClosure done_callback) {
+  CHECK(permissions_manager_->CanAffectExtension(*extension_));
+
+  URLPatternSet new_host_permissions({site});
+  GrantHostPermission(new_host_permissions.Clone(),
+                      new_host_permissions.Clone(), std::move(done_callback));
 }
 
 void ScriptingPermissionsModifier::RemoveGrantedHostPermission(
     const GURL& url) {
-  DCHECK(permissions_manager_->CanAffectExtension(*extension_));
-  DCHECK(permissions_manager_->HasGrantedHostPermission(*extension_, url));
+  CHECK(permissions_manager_->CanAffectExtension(*extension_));
+  CHECK(permissions_manager_->HasGrantedHostPermission(*extension_, url));
 
   std::unique_ptr<const PermissionSet> runtime_permissions =
       permissions_manager_->GetRuntimePermissionsFromPrefs(*extension_);
 
   URLPatternSet explicit_hosts;
   for (const auto& pattern : runtime_permissions->explicit_hosts()) {
-    if (pattern.MatchesSecurityOrigin(url))
+    if (pattern.MatchesSecurityOrigin(url)) {
       explicit_hosts.AddPattern(pattern);
+    }
   }
   URLPatternSet scriptable_hosts;
   for (const auto& pattern : runtime_permissions->scriptable_hosts()) {
-    if (pattern.MatchesSecurityOrigin(url))
+    if (pattern.MatchesSecurityOrigin(url)) {
       scriptable_hosts.AddPattern(pattern);
+    }
   }
 
-  PermissionsUpdater(browser_context_)
-      .RevokeRuntimePermissions(
-          *extension_,
-          PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
-                        std::move(explicit_hosts), std::move(scriptable_hosts)),
-          base::DoNothing());
+  WithholdHostPermissions(std::move(explicit_hosts),
+                          std::move(scriptable_hosts), base::DoNothing());
+}
+
+void ScriptingPermissionsModifier::RemoveHostPermissions(
+    const URLPattern& pattern,
+    base::OnceClosure done_callback) {
+  CHECK(permissions_manager_->CanAffectExtension(*extension_));
+
+  // Revoke all sites which have some intersection with `pattern` from the
+  // extension's set of runtime granted host permissions.
+  std::unique_ptr<const PermissionSet> runtime_permissions =
+      permissions_manager_->GetRuntimePermissionsFromPrefs(*extension_);
+
+  URLPatternSet explicit_hosts;
+  for (const auto& runtime_pattern : runtime_permissions->explicit_hosts()) {
+    if (pattern.OverlapsWith(runtime_pattern)) {
+      explicit_hosts.AddPattern(runtime_pattern);
+    }
+  }
+  URLPatternSet scriptable_hosts;
+  for (const auto& runtime_pattern : runtime_permissions->scriptable_hosts()) {
+    if (pattern.OverlapsWith(runtime_pattern)) {
+      scriptable_hosts.AddPattern(runtime_pattern);
+    }
+  }
+
+  WithholdHostPermissions(std::move(explicit_hosts),
+                          std::move(scriptable_hosts),
+                          std::move(done_callback));
 }
 
 void ScriptingPermissionsModifier::RemoveBroadGrantedHostPermissions() {
@@ -126,7 +161,25 @@
 
 void ScriptingPermissionsModifier::RemoveAllGrantedHostPermissions() {
   DCHECK(permissions_manager_->CanAffectExtension(*extension_));
-  WithholdHostPermissions();
+
+  std::unique_ptr<const PermissionSet> revokable_permissions =
+      permissions_manager_->GetRevokablePermissions(*extension_);
+  DCHECK(revokable_permissions);
+  PermissionsUpdater(browser_context_)
+      .RevokeRuntimePermissions(*extension_, *revokable_permissions,
+                                base::DoNothing());
+}
+
+void ScriptingPermissionsModifier::GrantHostPermission(
+    URLPatternSet explicit_hosts,
+    URLPatternSet scriptable_hosts,
+    base::OnceClosure done_callback) {
+  PermissionsUpdater(browser_context_)
+      .GrantRuntimePermissions(
+          *extension_,
+          PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
+                        std::move(explicit_hosts), std::move(scriptable_hosts)),
+          std::move(done_callback));
 }
 
 void ScriptingPermissionsModifier::GrantWithheldHostPermissions() {
@@ -140,13 +193,24 @@
       .GrantRuntimePermissions(*extension_, permissions, base::DoNothing());
 }
 
-void ScriptingPermissionsModifier::WithholdHostPermissions() {
-  std::unique_ptr<const PermissionSet> revokable_permissions =
-      permissions_manager_->GetRevokablePermissions(*extension_);
-  DCHECK(revokable_permissions);
+void ScriptingPermissionsModifier::WithholdHostPermissions(
+    URLPatternSet explicit_hosts,
+    URLPatternSet scriptable_hosts,
+    base::OnceClosure done_callback) {
+  std::unique_ptr<const PermissionSet> permissions_to_remove =
+      PermissionSet::CreateIntersection(
+          PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
+                        std::move(explicit_hosts), std::move(scriptable_hosts)),
+          *permissions_manager_->GetRevokablePermissions(*extension_),
+          URLPatternSet::IntersectionBehavior::kDetailed);
+  if (permissions_to_remove->IsEmpty()) {
+    std::move(done_callback).Run();
+    return;
+  }
+
   PermissionsUpdater(browser_context_)
-      .RevokeRuntimePermissions(*extension_, *revokable_permissions,
-                                base::DoNothing());
+      .RevokeRuntimePermissions(*extension_, *permissions_to_remove,
+                                std::move(done_callback));
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/scripting_permissions_modifier.h b/chrome/browser/extensions/scripting_permissions_modifier.h
index faa49ba..2c961b0 100644
--- a/chrome/browser/extensions/scripting_permissions_modifier.h
+++ b/chrome/browser/extensions/scripting_permissions_modifier.h
@@ -7,10 +7,13 @@
 
 #include <memory>
 
+#include "base/functional/callback_forward.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
+#include "extensions/common/url_pattern_set.h"
 
 class GURL;
+class URLPattern;
 
 namespace content {
 class BrowserContext;
@@ -21,6 +24,7 @@
 class ExtensionPrefs;
 class PermissionSet;
 class PermissionsManager;
+class URLPatternSet;
 
 // Responsible for managing the majority of click-to-script features, including
 // granting, withholding, and querying host permissions, and determining if an
@@ -43,17 +47,30 @@
 
   // Grants the extension permission to run on the origin of |url|.
   // This may only be called for extensions that can be affected (i.e., for
-  // which CanAffectExtension() returns true). Anything else will DCHECK.
+  // which CanAffectExtension() returns true). Anything else will CHECK.
   void GrantHostPermission(const GURL& url);
 
+  // Grants the extension permission to run on `pattern`.
+  // This may only be called for extensions that can be affected (i.e., for
+  // which CanAffectExtension() returns true). Anything else will CHECK.
+  void GrantHostPermission(const URLPattern& site,
+                           base::OnceClosure done_callback);
+
   // Revokes permission to run on the origin of |url|, including any permissions
   // that match or overlap with the origin. For instance, removing access to
   // https://google.com will remove access to *://*.com/* as well.
   // DCHECKs if |url| has not been granted.
   // This may only be called for extensions that can be affected (i.e., for
-  // which CanAffectExtension() returns true). Anything else will DCHECK.
+  // which CanAffectExtension() returns true). Anything else will CHECK.
   void RemoveGrantedHostPermission(const GURL& url);
 
+  // Revokes permission to run on all sites that have some intersection with
+  // `pattern`. This may only be called for extensions that can be affected
+  // (i.e., for which CanAffectExtension() returns true). Anything else will
+  // CHECK.
+  void RemoveHostPermissions(const URLPattern& pattern,
+                             base::OnceClosure done_callback);
+
   // Revokes host permission patterns granted to the extension that effectively
   // grant access to all urls.
   void RemoveBroadGrantedHostPermissions();
@@ -75,11 +92,20 @@
       const PermissionSet& permissions);
 
  private:
+  // Grants `explicit_hosts` and `scriptable_hosts` permissions. Calls
+  // `done_callback` on completion.
+  void GrantHostPermission(URLPatternSet explicit_hosts,
+                           URLPatternSet scriptable_hosts,
+                           base::OnceClosure done_callback);
+
   // Grants any withheld host permissions.
   void GrantWithheldHostPermissions();
 
-  // Revokes any granted host permissions.
-  void WithholdHostPermissions();
+  // Revokes `explicit_hosts` and `scriptable_hosts` permissions. Calls
+  // `done_callback` on completion.
+  void WithholdHostPermissions(URLPatternSet explicit_hosts,
+                               URLPatternSet scriptable_hosts,
+                               base::OnceClosure done_callback);
 
   raw_ptr<content::BrowserContext> browser_context_;
 
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 3bc2414..c3c0971 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -893,6 +893,11 @@
     "expiry_milestone": 140
   },
   {
+    "name": "bookmarks-and-reading-list-behind-opt-in",
+    "owners": [ "wylieb@chromium.org", "bsazonov@chromium.org" ],
+    "expiry_milestone": 126
+  },
+  {
     "name": "bookmarks-improved-save-flow",
     "owners": ["wylieb@chromium.org", "fgorski@chromium.org", "mdjones@chromium.org"],
     "expiry_milestone": 115
@@ -1276,11 +1281,6 @@
     "expiry_milestone": 125
   },
   {
-    "name": "clipboard-history-web-contents-paste",
-    "owners": ["dmblack@google.com", "multipaste@google.com"],
-    "expiry_milestone": 125
-  },
-    {
     "name": "clipboard-maximum-age",
     "owners": ["pnoland@chromium.org", "chrome-mobile-search@google.com"],
     "expiry_milestone": 125
@@ -2220,11 +2220,6 @@
     "expiry_milestone": -1
   },
   {
-    "name": "enable-bookmark-folders-for-account-storage",
-    "owners": [ "wylieb@chromium.org", "bsazonov@chromium.org" ],
-    "expiry_milestone": 126
-  },
-  {
     "name": "enable-borderless-printing",
     "owners": [ "bryancain@chromium.org", "project-bolton-eng@google.com" ],
     "expiry_milestone": 122
@@ -7420,6 +7415,11 @@
     "expiry_milestone": 120
   },
   {
+    "name": "shortcuts-not-apps",
+    "owners": [ "dmurph@chromium.org", "desktop-pwas-team@google.com" ],
+    "expiry_milestone": 132
+  },
+  {
     "name": "show-autofill-type-predictions",
     "owners": [ "ftirelo@chromium.org", "mathp@chromium.org" ],
     // This is used by autofill devs to debug on Android.
@@ -8382,6 +8382,11 @@
     "expiry_milestone": 130
   },
   {
+    "name": "web-app-universal-install",
+    "owners": [ "dmurph@chromium.org", "desktop-pwas-team@google.com" ],
+    "expiry_milestone": 132
+  },
+  {
     "name": "web-app-user-display-mode-sync-browser-mitigation",
     "owners": [ "alancutter@chromium.org", "desktop-pwas-team@google.com" ],
     "expiry_milestone": 132
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index f60d0d7..3c42d23 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -987,11 +987,6 @@
 const char kEnableAutofillAddressSavePromptDescription[] =
     "Enable the Autofill address save prompts.";
 
-const char kEnableBookmarkFoldersForAccountStorageName[] =
-    "Enable account bookmark folders";
-const char kEnableBookmarkFoldersForAccountStorageDescription[] =
-    "Enables account bookmark folders to be made available to users.";
-
 const char kEnterpriseProfileBadgingName[] =
     "Enable enterprise profile badging";
 const char kEnterpriseProfileBadgingDescription[] =
@@ -3346,12 +3341,6 @@
     "keyboard shortcuts and have the events routed directly to the website "
     "when in fullscreen mode.";
 
-const char kStylusBatteryStatusName[] =
-    "Show stylus battery stylus in the stylus tools menu";
-const char kStylusBatteryStatusDescription[] =
-    "Enables viewing the current stylus battery level in the stylus tools "
-    "menu.";
-
 const char kTabDragDropName[] = "Tab Drag and Drop via Strip";
 const char kTabDragDropDescription[] =
     "Enables Tab drag and drop UI to move tab on tab-strip across windows.";
@@ -3900,6 +3889,12 @@
     "Enable experiment that will disable max node and timeout limits for the "
     "AXTreeSnapshotter, and track performance stats.";
 
+const char kAccountBookmarksAndReadingListBehindOptInName[] =
+    "Account bookmarks and reading list behind opt-in";
+const char kAccountBookmarksAndReadingListBehindOptInDescription[] =
+    "Make account bookmarks and reading lists available to users that sign in "
+    "via promo in the bookmark manager.";
+
 const char kAddToHomescreenIPHName[] = "Add to homescreen IPH";
 const char kAddToHomescreenIPHDescription[] =
     " Shows in-product-help messages educating users about add to homescreen "
@@ -5148,6 +5143,15 @@
     "during a manifest update if the icons were generated, indictative of"
     "network errors during the sync install.";
 
+const char kWebAppUniversalInstallName[] = "Web App Universal Install";
+const char kWebAppUniversalInstallDescription[] =
+    "Allows any site to be installable on Windows, Mac, and Linux.";
+
+const char kShortcutsNotAppsName[] = "Shortcuts not Apps";
+const char kShortcutsNotAppsDescription[] =
+    "Changes the 'create shortcut' 3-dot menu option to put a fire-and-forget "
+    "link on the desktop, instead of an app.";
+
 const char kUserDisplayModeSyncBrowserMitigationName[] =
     "Web App User Display Mode Sync Browser Mitigation";
 const char kUserDisplayModeSyncBrowserMitigationDescription[] =
@@ -5939,12 +5943,6 @@
     "annotation for copied URLs in the clipboard history menu: If the URL has "
     "been visited, its page title will appear as part of the URL's menu item.";
 
-const char kClipboardHistoryWebContentsPasteName[] =
-    "Explicitly paste into web contents from clipboard history";
-const char kClipboardHistoryWebContentsPasteDescription[] =
-    "Enables an experimental behavior where clipboard history explicitly "
-    "pastes into web contents instead of using synthetic key events.";
-
 const char kCloudGamingDeviceName[] = "Enable cloud game search";
 const char kCloudGamingDeviceDescription[] =
     "Enables cloud game search results in the launcher.";
@@ -5956,12 +5954,6 @@
     " Overrides any other component updater check request parameters that may "
     "have been specified.";
 
-const char kContextualNudgesName[] =
-    "Contextual nudges for user gesture education";
-const char kContextualNudgesDescription[] =
-    "Enables contextual nudges, periodically showing the user a label "
-    "explaining how to interact with a particular UI element using gestures.";
-
 const char kCrosOnDeviceGrammarCheckName[] = "On-device Grammar Check";
 const char kCrosOnDeviceGrammarCheckDescription[] =
     "Enable new on-device grammar check component.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index d45339c..2012d55 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -574,9 +574,6 @@
 extern const char kEnableAutofillAddressSavePromptName[];
 extern const char kEnableAutofillAddressSavePromptDescription[];
 
-extern const char kEnableBookmarkFoldersForAccountStorageName[];
-extern const char kEnableBookmarkFoldersForAccountStorageDescription[];
-
 extern const char kEnterpriseProfileBadgingName[];
 extern const char kEnterpriseProfileBadgingDescription[];
 
@@ -1902,9 +1899,6 @@
 extern const char kStrictOriginIsolationName[];
 extern const char kStrictOriginIsolationDescription[];
 
-extern const char kStylusBatteryStatusName[];
-extern const char kStylusBatteryStatusDescription[];
-
 extern const char kSupportTool[];
 extern const char kSupportToolDescription[];
 
@@ -2265,6 +2259,9 @@
 extern const char kAccessibilitySnapshotStressTestsName[];
 extern const char kAccessibilitySnapshotStressTestsDescription[];
 
+extern const char kAccountBookmarksAndReadingListBehindOptInName[];
+extern const char kAccountBookmarksAndReadingListBehindOptInDescription[];
+
 extern const char kAdaptiveButtonInTopToolbarName[];
 extern const char kAdaptiveButtonInTopToolbarDescription[];
 extern const char kAdaptiveButtonInTopToolbarTranslateName[];
@@ -2982,6 +2979,12 @@
 extern const char kWebAppSyncGeneratedIconUpdateFixName[];
 extern const char kWebAppSyncGeneratedIconUpdateFixDescription[];
 
+extern const char kWebAppUniversalInstallName[];
+extern const char kWebAppUniversalInstallDescription[];
+
+extern const char kShortcutsNotAppsName[];
+extern const char kShortcutsNotAppsDescription[];
+
 extern const char kUserDisplayModeSyncBrowserMitigationName[];
 extern const char kUserDisplayModeSyncBrowserMitigationDescription[];
 
@@ -3421,18 +3424,12 @@
 extern const char kClipboardHistoryUrlTitlesName[];
 extern const char kClipboardHistoryUrlTitlesDescription[];
 
-extern const char kClipboardHistoryWebContentsPasteName[];
-extern const char kClipboardHistoryWebContentsPasteDescription[];
-
 extern const char kCloudGamingDeviceName[];
 extern const char kCloudGamingDeviceDescription[];
 
 extern const char kComponentUpdaterTestRequestName[];
 extern const char kComponentUpdaterTestRequestDescription[];
 
-extern const char kContextualNudgesName[];
-extern const char kContextualNudgesDescription[];
-
 extern const char kCroshSWAName[];
 extern const char kCroshSWADescription[];
 
diff --git a/chrome/browser/idle/idle_browsertest.cc b/chrome/browser/idle/idle_browsertest.cc
index f416c31..e1bdc10e 100644
--- a/chrome/browser/idle/idle_browsertest.cc
+++ b/chrome/browser/idle/idle_browsertest.cc
@@ -22,6 +22,7 @@
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "ui/base/idle/idle_polling_service.h"
 #include "ui/base/idle/idle_time_provider.h"
 #include "ui/base/test/idle_test_utils.h"
 
@@ -65,6 +66,9 @@
     https_server()->ServeFilesFromSourceDirectory("content/test/data");
     https_server()->SetSSLConfig(net::EmbeddedTestServer::CERT_OK);
     ASSERT_TRUE(https_server()->Start());
+    // The default 15s polling interval causes tests to time out.
+    ui::IdlePollingService::GetInstance()->SetPollIntervalForTest(
+        base::Seconds(1));
   }
 
   content::WebContents* web_contents() const {
diff --git a/chrome/browser/ip_protection/ip_protection_config_http.cc b/chrome/browser/ip_protection/ip_protection_config_http.cc
index ad65cbd..7ab4ab5 100644
--- a/chrome/browser/ip_protection/ip_protection_config_http.cc
+++ b/chrome/browser/ip_protection/ip_protection_config_http.cc
@@ -11,8 +11,10 @@
 #include "base/strings/strcat.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
+#include "base/version_info/channel.h"
 #include "build/branding_buildflags.h"
 #include "chrome/browser/ip_protection/get_proxy_config.pb.h"
+#include "chrome/common/channel_info.h"
 #include "google_apis/google_api_keys.h"
 #include "net/base/features.h"
 #include "services/network/public/cpp/resource_request.h"
@@ -93,6 +95,15 @@
 // should be much smaller than this).
 const int kIpProtectionRequestMaxBodySize = 256 * 1024;
 const char kProtobufContentType[] = "application/x-protobuf";
+
+// TODO(https://crbug.com/1299886): Once `google_apis::GetAPIKey()` handles this
+// logic we can remove this helper.
+std::string GetAPIKey() {
+  return chrome::GetChannel() == version_info::Channel::STABLE
+             ? google_apis::GetAPIKey()
+             : google_apis::GetNonStableAPIKey();
+}
+
 }  // namespace
 
 IpProtectionConfigHttp::IpProtectionConfigHttp(
@@ -222,8 +233,7 @@
         net::HttpRequestHeaders::kAuthorization,
         base::StrCat({"Bearer ", oauth_token.value()}));
   } else {
-    resource_request->headers.SetHeader(kGoogApiKeyHeader,
-                                        google_apis::GetAPIKey());
+    resource_request->headers.SetHeader(kGoogApiKeyHeader, GetAPIKey());
   }
 
   std::unique_ptr<network::SimpleURLLoader> url_loader =
diff --git a/chrome/browser/magic_stack/android/BUILD.gn b/chrome/browser/magic_stack/android/BUILD.gn
index a198d21..875c385a 100644
--- a/chrome/browser/magic_stack/android/BUILD.gn
+++ b/chrome/browser/magic_stack/android/BUILD.gn
@@ -20,7 +20,9 @@
   ]
   deps = [
     ":java_resources",
+    "//base:base_cached_flags_java",
     "//base:base_java",
+    "//chrome/browser/flags:java",
     "//chrome/browser/profiles/android:java",
     "//chrome/browser/tab:java",
     "//chrome/browser/tabmodel:java",
@@ -67,6 +69,7 @@
     "//base:base_java",
     "//base:base_java_test_support",
     "//base:base_junit_test_support",
+    "//chrome/browser/flags:java",
     "//chrome/browser/profiles/android:java",
     "//chrome/browser/tab:java",
     "//chrome/browser/tabmodel:java",
diff --git a/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesCoordinator.java b/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesCoordinator.java
index ec41774..7744120 100644
--- a/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesCoordinator.java
+++ b/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesCoordinator.java
@@ -19,6 +19,7 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.supplier.ObservableSupplier;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.magic_stack.ModuleRegistry.OnViewCreatedCallback;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
@@ -351,7 +352,15 @@
     List<Integer> getModuleList() {
         // TODO(https://crbug.com/1512962): Gets the modules ranking list using segmentation service
         // API.
-        List<Integer> generalModuleList = List.of(ModuleType.PRICE_CHANGE, ModuleType.SINGLE_TAB);
+        List<Integer> generalModuleList;
+        if (mModuleDelegateHost.isHomeSurface()) {
+            generalModuleList = List.of(ModuleType.PRICE_CHANGE, ModuleType.SINGLE_TAB);
+        } else if (ChromeFeatureList.sTabResumptionModuleAndroid.isEnabled()) {
+            generalModuleList = List.of(ModuleType.PRICE_CHANGE, ModuleType.TAB_RESUMPTION);
+        } else {
+            generalModuleList = List.of(ModuleType.PRICE_CHANGE);
+        }
+
         List<Integer> moduleList = new ArrayList<>();
         for (int i = 0; i < generalModuleList.size(); i++) {
             @ModuleType int currentModuleType = generalModuleList.get(i);
diff --git a/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesMetricsUtils.java b/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesMetricsUtils.java
index d82a476..69cca4e11 100644
--- a/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesMetricsUtils.java
+++ b/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesMetricsUtils.java
@@ -6,6 +6,7 @@
 
 import static org.chromium.chrome.browser.magic_stack.ModuleDelegate.ModuleType.PRICE_CHANGE;
 import static org.chromium.chrome.browser.magic_stack.ModuleDelegate.ModuleType.SINGLE_TAB;
+import static org.chromium.chrome.browser.magic_stack.ModuleDelegate.ModuleType.TAB_RESUMPTION;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -63,6 +64,8 @@
                 return "SingleTab";
             case (PRICE_CHANGE):
                 return "PriceChange";
+            case (TAB_RESUMPTION):
+                return "TabResumption";
             default:
                 assert false : "Module type not supported!";
                 return null;
diff --git a/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/ModuleDelegate.java b/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/ModuleDelegate.java
index 419a25c..5fdece5f 100644
--- a/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/ModuleDelegate.java
+++ b/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/ModuleDelegate.java
@@ -23,12 +23,18 @@
      * <p>These values are persisted to logs. Entries should not be renumbered and numeric values
      * should never be reused. See tools/metrics/histograms/enums.xml.
      */
-    @IntDef({ModuleType.SINGLE_TAB, ModuleType.PRICE_CHANGE, ModuleType.NUM_ENTRIES})
+    @IntDef({
+        ModuleType.SINGLE_TAB,
+        ModuleType.PRICE_CHANGE,
+        ModuleType.TAB_RESUMPTION,
+        ModuleType.NUM_ENTRIES
+    })
     @Retention(RetentionPolicy.SOURCE)
     @interface ModuleType {
         int SINGLE_TAB = 0;
         int PRICE_CHANGE = 1;
-        int NUM_ENTRIES = 2;
+        int TAB_RESUMPTION = 2;
+        int NUM_ENTRIES = 3;
     }
 
     /**
diff --git a/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/ModuleDelegateHost.java b/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/ModuleDelegateHost.java
index b3984be9..63a5562 100644
--- a/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/ModuleDelegateHost.java
+++ b/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/ModuleDelegateHost.java
@@ -65,4 +65,12 @@
     default Tab getTrackingTab() {
         return null;
     }
+
+    /**
+     * Returns whether the host is Start surface or NTP home surface which are shown at startup. The
+     * concept of the home surface is effectively the UI approach originally taken by Start surface,
+     * that tries to show a local tab resumption module. This value returned here is allowed to
+     * change at runtime for NTP.
+     */
+    boolean isHomeSurface();
 }
diff --git a/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesCoordinatorUnitTest.java b/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesCoordinatorUnitTest.java
index 50f8ce78..703ca71 100644
--- a/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesCoordinatorUnitTest.java
+++ b/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesCoordinatorUnitTest.java
@@ -46,6 +46,9 @@
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Features;
+import org.chromium.base.test.util.Features.DisableFeatures;
+import org.chromium.base.test.util.Features.EnableFeatures;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.magic_stack.ModuleDelegate.ModuleType;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
@@ -175,32 +178,80 @@
 
     @Test
     @SmallTest
+    @DisableFeatures({ChromeFeatureList.TAB_RESUMPTION_MODULE_ANDROID})
+    public void testGetModuleList_Default() {
+        when(mHomeModulesConfigManager.getEnabledModuleList())
+                .thenReturn(
+                        new HashSet<>(
+                                List.of(
+                                        ModuleType.SINGLE_TAB,
+                                        ModuleType.PRICE_CHANGE,
+                                        ModuleType.TAB_RESUMPTION)));
+        assertFalse(DeviceFormFactor.isNonMultiDisplayContextOnTablet(mActivity));
+        mCoordinator = createCoordinator(/* skipInitProfile= */ false);
+
+        when(mModuleDelegateHost.isHomeSurface()).thenReturn(true);
+        List<Integer> expectedModuleList = List.of(ModuleType.PRICE_CHANGE, ModuleType.SINGLE_TAB);
+        assertEquals(expectedModuleList, mCoordinator.getModuleList());
+
+        when(mModuleDelegateHost.isHomeSurface()).thenReturn(false);
+        expectedModuleList = List.of(ModuleType.PRICE_CHANGE);
+        assertEquals(expectedModuleList, mCoordinator.getModuleList());
+    }
+
+    @Test
+    @SmallTest
+    @EnableFeatures({ChromeFeatureList.TAB_RESUMPTION_MODULE_ANDROID})
+    public void testGetModuleList_DefaultWithTabResumption() {
+        when(mHomeModulesConfigManager.getEnabledModuleList())
+                .thenReturn(
+                        new HashSet<>(
+                                List.of(
+                                        ModuleType.SINGLE_TAB,
+                                        ModuleType.PRICE_CHANGE,
+                                        ModuleType.TAB_RESUMPTION)));
+        assertFalse(DeviceFormFactor.isNonMultiDisplayContextOnTablet(mActivity));
+        mCoordinator = createCoordinator(/* skipInitProfile= */ false);
+
+        when(mModuleDelegateHost.isHomeSurface()).thenReturn(true);
+        List<Integer> expectedModuleList = List.of(ModuleType.PRICE_CHANGE, ModuleType.SINGLE_TAB);
+        assertEquals(expectedModuleList, mCoordinator.getModuleList());
+
+        when(mModuleDelegateHost.isHomeSurface()).thenReturn(false);
+        expectedModuleList = List.of(ModuleType.PRICE_CHANGE, ModuleType.TAB_RESUMPTION);
+        assertEquals(expectedModuleList, mCoordinator.getModuleList());
+    }
+
+    @Test
+    @SmallTest
     public void testGetModuleList() {
+        when(mModuleDelegateHost.isHomeSurface()).thenReturn(true);
         when(mHomeModulesConfigManager.getEnabledModuleList())
                 .thenReturn(new HashSet<>(List.of(ModuleType.SINGLE_TAB)));
         assertFalse(DeviceFormFactor.isNonMultiDisplayContextOnTablet(mActivity));
         mCoordinator = createCoordinator(/* skipInitProfile= */ false);
         List<Integer> expectedModuleList = List.of(ModuleType.SINGLE_TAB);
-        assertEquals(mCoordinator.getModuleList(), expectedModuleList);
+        assertEquals(expectedModuleList, mCoordinator.getModuleList());
     }
 
     @Test
     @SmallTest
     public void testOnModuleConfigChanged() {
         assertFalse(DeviceFormFactor.isNonMultiDisplayContextOnTablet(mActivity));
+        when(mModuleDelegateHost.isHomeSurface()).thenReturn(true);
         mCoordinator = createCoordinator(/* skipInitProfile= */ false);
 
         verify(mHomeModulesConfigManager).addListener(mHomeModulesStateListener.capture());
         List<Integer> expectedModuleListBeforeHidingModule =
                 List.of(ModuleType.PRICE_CHANGE, ModuleType.SINGLE_TAB);
-        assertEquals(mCoordinator.getModuleList(), expectedModuleListBeforeHidingModule);
+        assertEquals(expectedModuleListBeforeHidingModule, mCoordinator.getModuleList());
 
         mHomeModulesStateListener.getValue().onModuleConfigChanged(ModuleType.PRICE_CHANGE, false);
         List<Integer> expectedModuleListAfterHidingModule = List.of(ModuleType.SINGLE_TAB);
-        assertEquals(mCoordinator.getModuleList(), expectedModuleListAfterHidingModule);
+        assertEquals(expectedModuleListAfterHidingModule, mCoordinator.getModuleList());
 
         mHomeModulesStateListener.getValue().onModuleConfigChanged(ModuleType.PRICE_CHANGE, true);
-        assertEquals(mCoordinator.getModuleList(), expectedModuleListBeforeHidingModule);
+        assertEquals(expectedModuleListBeforeHidingModule, mCoordinator.getModuleList());
 
         mCoordinator.destroy();
         verify(mHomeModulesConfigManager).removeListener(mHomeModulesStateListener.capture());
diff --git a/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesMediatorUnitTest.java b/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesMediatorUnitTest.java
index faa507a..1f0214c 100644
--- a/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesMediatorUnitTest.java
+++ b/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesMediatorUnitTest.java
@@ -18,10 +18,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import static org.chromium.chrome.browser.magic_stack.ModuleDelegate.ModuleType.NUM_ENTRIES;
-import static org.chromium.chrome.browser.magic_stack.ModuleDelegate.ModuleType.PRICE_CHANGE;
-import static org.chromium.chrome.browser.magic_stack.ModuleDelegate.ModuleType.SINGLE_TAB;
-
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
@@ -34,14 +30,10 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Features;
-import org.chromium.chrome.browser.magic_stack.HomeModulesMediatorUnitTest.ShadowHomeModulesMetricsUtils;
-import org.chromium.chrome.browser.magic_stack.ModuleDelegate.ModuleType;
 import org.chromium.chrome.browser.util.BrowserUiUtils.HostSurface;
 import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
@@ -53,28 +45,8 @@
 
 /** Unit tests for {@link HomeModulesMediator}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(
-        manifest = Config.NONE,
-        shadows = {ShadowHomeModulesMetricsUtils.class})
+@Config(manifest = Config.NONE)
 public class HomeModulesMediatorUnitTest {
-    @Implements(HomeModulesMetricsUtils.class)
-    static class ShadowHomeModulesMetricsUtils {
-        @Implementation
-        public static String getModuleName(@ModuleType int moduleType) {
-            switch (moduleType) {
-                case SINGLE_TAB:
-                    return "SingleTab";
-                case (PRICE_CHANGE):
-                    return "PriceChange";
-                case (NUM_ENTRIES):
-                    return "ForTesting";
-                default:
-                    assert false : "Module type not supported!";
-                    return null;
-            }
-        }
-    }
-
     @Rule public TestRule mProcessor = new Features.JUnitProcessor();
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
diff --git a/chrome/browser/metrics/structured/ash_event_storage.cc b/chrome/browser/metrics/structured/ash_event_storage.cc
index 4bfedb05..2835f7c 100644
--- a/chrome/browser/metrics/structured/ash_event_storage.cc
+++ b/chrome/browser/metrics/structured/ash_event_storage.cc
@@ -10,6 +10,8 @@
 
 namespace metrics::structured {
 
+using ::google::protobuf::RepeatedPtrField;
+
 AshEventStorage::AshEventStorage(base::TimeDelta write_delay,
                                  const base::FilePath& pre_user_event_path)
     : write_delay_(write_delay) {
@@ -32,37 +34,34 @@
   }
 }
 
-void AshEventStorage::AddEvent(StructuredEventProto&& event) {
+void AshEventStorage::AddEvent(StructuredEventProto event) {
   PersistentProto<EventsProto>* event_store_to_write =
       GetStoreToWriteEvent(event);
 
   if (!event_store_to_write) {
-    pre_storage_events_.emplace_back(event);
+    pre_storage_events_.emplace_back(std::move(event));
     return;
   }
 
-  *event_store_to_write->get()->add_non_uma_events() = event;
+  event_store_to_write->get()->mutable_non_uma_events()->Add(std::move(event));
   event_store_to_write->QueueWrite();
 }
 
-void AshEventStorage::MoveEvents(ChromeUserMetricsExtension& uma_proto) {
-  StructuredDataProto* proto = uma_proto.mutable_structured_data();
-
-  if (IsPreUserStorageReadable() &&
-      pre_user_events()->non_uma_events_size() > 0) {
-    proto->mutable_events()->MergeFrom(pre_user_events()->non_uma_events());
-    pre_user_events()->clear_non_uma_events();
+RepeatedPtrField<StructuredEventProto> AshEventStorage::TakeEvents() {
+  if (IsPreUserStorageReadable()) {
+    RepeatedPtrField<StructuredEventProto> events =
+        std::move(*pre_user_events()->mutable_non_uma_events());
     pre_user_events_->QueueWrite();
-  }
-  if (IsProfileReady() && user_events()->non_uma_events_size() > 0) {
-    proto->mutable_events()->MergeFrom(user_events()->non_uma_events());
-    user_events()->clear_non_uma_events();
-    user_events_->QueueWrite();
+    return events;
   }
 
-  // TODO(b/312292811): Cleanup |pre_user_events_| after the first upload as it
-  // is not needed. This cannot be done currently because the dtor will trigger
-  // a blocking call on a non-blocking thread.
+  // Profile must be ready if |pre_user_events| has been cleanedup.
+  CHECK(IsProfileReady());
+
+  RepeatedPtrField<StructuredEventProto> events =
+      std::move(*user_events()->mutable_non_uma_events());
+  user_events_->QueueWrite();
+  return events;
 }
 
 int AshEventStorage::RecordedEventsCount() const {
@@ -137,8 +136,6 @@
 }
 
 void AshEventStorage::OnWrite(const WriteStatus status) {
-  DCHECK(base::CurrentUIThread::IsSet());
-
   switch (status) {
     case WriteStatus::kOk:
       break;
@@ -152,8 +149,6 @@
 }
 
 void AshEventStorage::OnRead(const ReadStatus status) {
-  DCHECK(base::CurrentUIThread::IsSet());
-
   switch (status) {
     case ReadStatus::kOk:
     case ReadStatus::kMissing:
@@ -170,8 +165,6 @@
 }
 
 void AshEventStorage::OnProfileRead(const ReadStatus status) {
-  DCHECK(base::CurrentUIThread::IsSet());
-
   switch (status) {
     case ReadStatus::kOk:
     case ReadStatus::kMissing:
@@ -191,6 +184,37 @@
   CHECK(user_events_.get());
   is_user_initialized_ = true;
 
+  // Move any events that are current in |pre_user_events_| into the
+  // |user_events_|.
+  if (pre_user_events() && pre_user_events()->non_uma_events_size() > 0) {
+    RepeatedPtrField<StructuredEventProto>* users_events =
+        user_events()->mutable_non_uma_events();
+    RepeatedPtrField<StructuredEventProto>* pre_users_events =
+        pre_user_events()->mutable_non_uma_events();
+
+    // Moving events from |pre_users_events| to |users_events|.
+    users_events->Reserve(users_events->size() + pre_users_events->size());
+
+    // Temporary buffer to extract the |pre_users_events| into.
+    std::vector<StructuredEventProto*> extracted(pre_users_events->size(),
+                                                 nullptr);
+
+    // Extract and add the elements into |users_events|
+    pre_users_events->ExtractSubrange(0, pre_users_events->size(),
+                                      extracted.data());
+
+    for (auto* element : extracted) {
+      users_events->AddAllocated(element);
+    }
+  }
+
+  (*pre_user_events_)->Clear();
+  pre_user_events_->QueueWrite();
+
+  // The write is fine because it will add to a task that is not tied to the
+  // lifetime of |pre_user_events_|.
+  pre_user_events_.reset();
+
   // Dealloc any memory that the vector is occupying as it will not be used
   // anymore.
   std::vector<StructuredEventProto>().swap(pre_storage_events_);
diff --git a/chrome/browser/metrics/structured/ash_event_storage.h b/chrome/browser/metrics/structured/ash_event_storage.h
index 905e0f82..363bc6f 100644
--- a/chrome/browser/metrics/structured/ash_event_storage.h
+++ b/chrome/browser/metrics/structured/ash_event_storage.h
@@ -34,8 +34,9 @@
 
   // EventStorage:
   void OnReady() override;
-  void AddEvent(StructuredEventProto&& event) override;
-  void MoveEvents(ChromeUserMetricsExtension& uma_proto) override;
+  void AddEvent(StructuredEventProto event) override;
+  ::google::protobuf::RepeatedPtrField<StructuredEventProto> TakeEvents()
+      override;
   int RecordedEventsCount() const override;
   void Purge() override;
   void OnProfileAdded(const base::FilePath& path) override;
diff --git a/chrome/browser/metrics/structured/ash_event_storage_unittest.cc b/chrome/browser/metrics/structured/ash_event_storage_unittest.cc
index f0eff5a..c456263 100644
--- a/chrome/browser/metrics/structured/ash_event_storage_unittest.cc
+++ b/chrome/browser/metrics/structured/ash_event_storage_unittest.cc
@@ -54,11 +54,11 @@
   }
 
   StructuredDataProto GetReport(AshEventStorage* storage) {
-    ChromeUserMetricsExtension uma;
+    StructuredDataProto structured_data;
 
-    storage->MoveEvents(uma);
+    *structured_data.mutable_events() = storage->TakeEvents();
 
-    return uma.structured_data();
+    return structured_data;
   }
 
   void ExpectNoErrors() {
@@ -220,4 +220,32 @@
   ExpectNoErrors();
 }
 
+TEST_F(AshEventStorageTest, MergePreUserAndUserEvents) {
+  std::unique_ptr<AshEventStorage> storage = BuildTestStorage();
+  Wait();
+
+  // Add event before OnProfileAdded is called.
+  storage->AddEvent(BuildTestEvent());
+  storage->AddEvent(BuildTestEvent());
+  storage->AddEvent(BuildTestEvent());
+  ASSERT_TRUE(storage->IsReady());
+
+  // There should be 3 events in the pre-profile storage.
+  EventsProto events_proto;
+  storage->CopyEvents(&events_proto);
+  EXPECT_EQ(events_proto.non_uma_events_size(), 3);
+
+  // Add profile and add an event while the profile events are being loaded.
+  storage->OnProfileAdded(GetUserDirectory());
+  storage->AddEvent(BuildTestEvent());
+  Wait();
+
+  storage->AddEvent(BuildTestEvent());
+
+  const auto data = GetReport(storage.get());
+  EXPECT_EQ(data.events_size(), 5);
+
+  ExpectNoErrors();
+}
+
 }  // namespace metrics::structured
diff --git a/chrome/browser/metrics/structured/chrome_event_storage.cc b/chrome/browser/metrics/structured/chrome_event_storage.cc
index 93fb6a0..88ad0673 100644
--- a/chrome/browser/metrics/structured/chrome_event_storage.cc
+++ b/chrome/browser/metrics/structured/chrome_event_storage.cc
@@ -7,22 +7,19 @@
 #include "components/metrics/structured/histogram_util.h"
 
 namespace metrics::structured {
+
+using ::google::protobuf::RepeatedPtrField;
+
 ChromeEventStorage::ChromeEventStorage() = default;
 
 ChromeEventStorage::~ChromeEventStorage() = default;
 
-// TODO(b/320995781): Cleanup the API to take event by value.
-void ChromeEventStorage::AddEvent(StructuredEventProto&& event) {
+void ChromeEventStorage::AddEvent(StructuredEventProto event) {
   *events_.add_non_uma_events() = std::move(event);
 }
 
-// TODO(b/320995781): Change the name TakeEvent and return
-// mutable_non_uma_events().
-void ChromeEventStorage::MoveEvents(ChromeUserMetricsExtension& uma_proto) {
-  StructuredDataProto* proto = uma_proto.mutable_structured_data();
-  proto->mutable_events()->Swap(events_.mutable_non_uma_events());
-
-  events_.clear_non_uma_events();
+RepeatedPtrField<StructuredEventProto> ChromeEventStorage::TakeEvents() {
+  return std::move(*events_.mutable_non_uma_events());
 }
 
 int ChromeEventStorage::RecordedEventsCount() const {
diff --git a/chrome/browser/metrics/structured/chrome_event_storage.h b/chrome/browser/metrics/structured/chrome_event_storage.h
index 483faaf..3e2d293b 100644
--- a/chrome/browser/metrics/structured/chrome_event_storage.h
+++ b/chrome/browser/metrics/structured/chrome_event_storage.h
@@ -24,8 +24,9 @@
   ~ChromeEventStorage() override;
 
   // EventStorage:
-  void AddEvent(StructuredEventProto&& event) override;
-  void MoveEvents(ChromeUserMetricsExtension& uma_proto) override;
+  void AddEvent(StructuredEventProto event) override;
+  ::google::protobuf::RepeatedPtrField<StructuredEventProto> TakeEvents()
+      override;
   int RecordedEventsCount() const override;
   void Purge() override;
   void CopyEvents(EventsProto* proto) const override;
diff --git a/chrome/browser/metrics/structured/chrome_event_storage_unittest.cc b/chrome/browser/metrics/structured/chrome_event_storage_unittest.cc
index d82d62e..f38e289 100644
--- a/chrome/browser/metrics/structured/chrome_event_storage_unittest.cc
+++ b/chrome/browser/metrics/structured/chrome_event_storage_unittest.cc
@@ -37,11 +37,11 @@
   void Wait() { task_environment_.RunUntilIdle(); }
 
   StructuredDataProto GetReport(ChromeEventStorage* storage) {
-    ChromeUserMetricsExtension uma;
+    StructuredDataProto structured_data;
 
-    storage->MoveEvents(uma);
+    *structured_data.mutable_events() = storage->TakeEvents();
 
-    return uma.structured_data();
+    return structured_data;
   }
 
   void ExpectNoErrors() {
diff --git a/chrome/browser/metrics/structured_metrics_service_browsertest.cc b/chrome/browser/metrics/structured_metrics_service_browsertest.cc
index 4cc729d..4f767b3 100644
--- a/chrome/browser/metrics/structured_metrics_service_browsertest.cc
+++ b/chrome/browser/metrics/structured_metrics_service_browsertest.cc
@@ -3,10 +3,11 @@
 // found in the LICENSE file.
 
 #include <memory>
-#include "build/build_config.h"
+#include <utility>
 
 #include "base/run_loop.h"
 #include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/metrics/structured/test/structured_metrics_mixin.h"
@@ -167,14 +168,14 @@
   WaitUntilKeysReady();
 
   // Record a couple of events and verify that they are recorded.
-  structured::events::v2::test_project_one::TestEventOne()
-      .SetTestMetricOne("metric one")
-      .SetTestMetricTwo(10)
-      .Record();
+  structured::StructuredMetricsClient::Record(
+      std::move(structured::events::v2::test_project_one::TestEventOne()
+                    .SetTestMetricOne("metric one")
+                    .SetTestMetricTwo(10)));
 
-  structured::events::v2::test_project_five::TestEventSix()
-      .SetTestMetricSix("metric six")
-      .Record();
+  structured::StructuredMetricsClient::Record(
+      std::move(structured::events::v2::test_project_five::TestEventSix()
+                    .SetTestMetricSix("metric six")));
 
   // This will timeout and fail the test if events have not been recorded
   // successfully.
@@ -218,14 +219,14 @@
   WaitUntilKeysReady();
 
   // Record a couple of events and verify that they are recorded.
-  structured::events::v2::test_project_one::TestEventOne()
-      .SetTestMetricOne("metric one")
-      .SetTestMetricTwo(10)
-      .Record();
+  structured::StructuredMetricsClient::Record(
+      std::move(structured::events::v2::test_project_one::TestEventOne()
+                    .SetTestMetricOne("metric one")
+                    .SetTestMetricTwo(10)));
 
-  structured::events::v2::test_project_five::TestEventSix()
-      .SetTestMetricSix("metric six")
-      .Record();
+  structured::StructuredMetricsClient::Record(
+      std::move(structured::events::v2::test_project_five::TestEventSix()
+                    .SetTestMetricSix("metric six")));
 
   // This will timeout and fail the test if events have not been recorded
   // successfully.
@@ -263,10 +264,10 @@
   WaitUntilKeysReady();
 
   // Record an event inorder to build a log.
-  structured::events::v2::test_project_one::TestEventOne()
-      .SetTestMetricOne("metric one")
-      .SetTestMetricTwo(10)
-      .Record();
+  structured::StructuredMetricsClient::Record(
+      std::move(structured::events::v2::test_project_one::TestEventOne()
+                    .SetTestMetricOne("metric one")
+                    .SetTestMetricTwo(10)));
 
   // This will timeout and fail the test if events have not been recorded
   // successfully.
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_module_service.cc b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_module_service.cc
index 9e122b4..3fac509 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_module_service.cc
+++ b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_module_service.cc
@@ -16,6 +16,8 @@
 #include "chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_signals.h"
 #include "chrome/browser/new_tab_page/new_tab_page_util.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
+#include "chrome/browser/segmentation_platform/segmentation_platform_service_factory.h"
+#include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/history_types.h"
 #include "components/history_clusters/core/history_clusters_service.h"
 #include "components/history_clusters/core/history_clusters_service_task.h"
@@ -69,6 +71,7 @@
 
 HistoryClustersModuleService::HistoryClustersModuleService(
     history_clusters::HistoryClustersService* history_clusters_service,
+    history::HistoryService* history_service,
     CartService* cart_service,
     TemplateURLService* template_url_service,
     OptimizationGuideKeyedService* optimization_guide_keyed_service,
@@ -88,7 +91,7 @@
       optimization_guide_keyed_service) {
     module_ranker_ = std::make_unique<HistoryClustersModuleRanker>(
         optimization_guide_keyed_service, segmentation_platform_service,
-        cart_service_, category_boostlist_);
+        history_service, cart_service_, category_boostlist_);
   }
 }
 HistoryClustersModuleService::~HistoryClustersModuleService() = default;
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_module_service.h b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_module_service.h
index 16ea554..8edfc33 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_module_service.h
+++ b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_module_service.h
@@ -14,6 +14,7 @@
 
 namespace history {
 struct Cluster;
+class HistoryService;
 }  // namespace history
 
 namespace history_clusters {
@@ -38,6 +39,7 @@
   HistoryClustersModuleService(const HistoryClustersModuleService&) = delete;
   HistoryClustersModuleService(
       history_clusters::HistoryClustersService* history_clusters_service,
+      history::HistoryService* history_service,
       CartService* cart_service,
       TemplateURLService* template_url_service,
       OptimizationGuideKeyedService* optimization_guide_keyed_service,
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_module_service_factory.cc b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_module_service_factory.cc
index 94fd422..5888837 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_module_service_factory.cc
+++ b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_module_service_factory.cc
@@ -7,12 +7,14 @@
 #include "base/feature_list.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/cart/cart_service_factory.h"
+#include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/history_clusters/history_clusters_service_factory.h"
 #include "chrome/browser/new_tab_page/modules/history_clusters/history_clusters_module_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/segmentation_platform/segmentation_platform_service_factory.h"
+#include "components/keyed_service/core/service_access_type.h"
 #include "components/search/ntp_features.h"
 #include "content/public/browser/browser_context.h"
 
@@ -36,6 +38,7 @@
               .WithGuest(ProfileSelection::kNone)
               .WithAshInternals(ProfileSelection::kNone)
               .Build()) {
+  DependsOn(HistoryServiceFactory::GetInstance());
   DependsOn(HistoryClustersServiceFactory::GetInstance());
   DependsOn(CartServiceFactory::GetInstance());
   DependsOn(TemplateURLServiceFactory::GetInstance());
@@ -57,6 +60,11 @@
     return nullptr;
   }
   auto* profile = Profile::FromBrowserContext(context);
+  auto* hs = HistoryServiceFactory::GetForProfile(
+      profile, ServiceAccessType::EXPLICIT_ACCESS);
+  if (!hs) {
+    return nullptr;
+  }
   auto* tus = TemplateURLServiceFactory::GetForProfile(profile);
   if (!tus) {
     return nullptr;
@@ -65,7 +73,7 @@
       segmentation_platform::SegmentationPlatformServiceFactory::GetForProfile(
           profile);
   return std::make_unique<HistoryClustersModuleService>(
-      hcs, CartServiceFactory::GetForProfile(profile), tus,
+      hcs, hs, CartServiceFactory::GetForProfile(profile), tus,
       OptimizationGuideKeyedServiceFactory::GetForProfile(profile), sps);
 }
 
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_module_service_unittest.cc b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_module_service_unittest.cc
index a38680a..57da827d 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_module_service_unittest.cc
+++ b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_module_service_unittest.cc
@@ -85,7 +85,8 @@
         kTemplateURLData, std::size(kTemplateURLData));
     history_clusters_module_service_ =
         std::make_unique<HistoryClustersModuleService>(
-            test_history_clusters_service_.get(), mock_cart_service_.get(),
+            test_history_clusters_service_.get(),
+            /*history_service=*/nullptr, mock_cart_service_.get(),
             template_url_service_.get(),
             /*optimization_guide_keyed_service=*/nullptr,
             /*segmentation_platform_service=*/nullptr);
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_module_util.cc b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_module_util.cc
index 4ebb4a5..940d02a7 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_module_util.cc
+++ b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_module_util.cc
@@ -208,7 +208,7 @@
       GenerateSampleVisit(0, kSampleSearchQuery + " - Google Search",
                           GURL("https://www.google.com/search?q=" +
                                std::string(encoded_query.view())),
-                          false));
+                          false, visit_categories));
 
   return history::Cluster(
       cluster_id, sample_visits, {},
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_test_support.cc b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_test_support.cc
index 1dbdfb1..99b1815 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_test_support.cc
+++ b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_test_support.cc
@@ -28,6 +28,7 @@
                                    nullptr,
                                    nullptr,
                                    nullptr,
+                                   nullptr,
                                    nullptr) {}
 
 MockHistoryClustersModuleService::~MockHistoryClustersModuleService() = default;
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_category_metrics.cc b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_category_metrics.cc
new file mode 100644
index 0000000..0540af3
--- /dev/null
+++ b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_category_metrics.cc
@@ -0,0 +1,26 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_category_metrics.h"
+
+#include <set>
+#include <string>
+
+HistoryClustersCategoryMetrics::HistoryClustersCategoryMetrics() = default;
+HistoryClustersCategoryMetrics::HistoryClustersCategoryMetrics(
+    std::set<std::string> most_frequently_seen_ids,
+    size_t most_frequent_seen_category_count,
+    std::set<std::string> most_frequently_engaged_ids,
+    size_t most_frequent_used_category_count)
+    : most_frequently_seen_category_ids(most_frequently_seen_ids),
+      most_frequent_seen_category_for_period_count(
+          most_frequent_seen_category_count),
+      most_frequently_used_category_ids(most_frequently_engaged_ids),
+      most_frequent_used_category_for_period_count(
+          most_frequent_used_category_count) {}
+
+HistoryClustersCategoryMetrics::HistoryClustersCategoryMetrics(
+    const HistoryClustersCategoryMetrics&) = default;
+
+HistoryClustersCategoryMetrics::~HistoryClustersCategoryMetrics() = default;
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_category_metrics.h b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_category_metrics.h
new file mode 100644
index 0000000..fc78162
--- /dev/null
+++ b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_category_metrics.h
@@ -0,0 +1,36 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_NEW_TAB_PAGE_MODULES_HISTORY_CLUSTERS_RANKING_HISTORY_CLUSTERS_CATEGORY_METRICS_H_
+#define CHROME_BROWSER_NEW_TAB_PAGE_MODULES_HISTORY_CLUSTERS_RANKING_HISTORY_CLUSTERS_CATEGORY_METRICS_H_
+
+#include <set>
+#include <string>
+#include <utility>
+
+// Used for capturing aggregate cluster related metrics that will be used at
+// cluster ranking time.
+struct HistoryClustersCategoryMetrics {
+  HistoryClustersCategoryMetrics();
+  HistoryClustersCategoryMetrics(std::set<std::string> most_frequently_seen_ids,
+                                 size_t most_frequent_seen_category_count,
+                                 std::set<std::string> most_frequently_used_ids,
+                                 size_t most_frequent_used_category_count);
+  HistoryClustersCategoryMetrics(const HistoryClustersCategoryMetrics&);
+  ~HistoryClustersCategoryMetrics();
+
+  // A set with the most fequently seen category, or categories in case of a
+  // tie, for a given period of time.
+  std::set<std::string> most_frequently_seen_category_ids;
+
+  size_t most_frequent_seen_category_for_period_count = 0;
+
+  // A set with the most fequently used category, or categories in case of a
+  // tie, for a given period of time.
+  std::set<std::string> most_frequently_used_category_ids;
+
+  size_t most_frequent_used_category_for_period_count = 0;
+};
+
+#endif  // CHROME_BROWSER_NEW_TAB_PAGE_MODULES_HISTORY_CLUSTERS_RANKING_HISTORY_CLUSTERS_CATEGORY_METRICS_H_
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranker.cc b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranker.cc
index 97bc735..1fba0a1 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranker.cc
+++ b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranker.cc
@@ -6,16 +6,20 @@
 
 #include <iterator>
 #include <set>
+#include <string>
 #include <utility>
+#include <vector>
 
 #include "base/feature_list.h"
 #include "base/ranges/algorithm.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
 #include "chrome/browser/cart/cart_db.h"
 #include "chrome/browser/cart/cart_service.h"
 #include "chrome/browser/new_tab_page/modules/history_clusters/cart/cart_processor.h"
 #include "chrome/browser/new_tab_page/modules/history_clusters/history_clusters_module_util.h"
 #include "chrome/browser/new_tab_page/modules/history_clusters/ranking/history_cluster_metrics.h"
+#include "chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_category_metrics.h"
 #include "chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_metrics_logger.h"
 #include "chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_signals.h"
 #include "chrome/browser/new_tab_page/new_tab_page_util.h"
@@ -39,13 +43,44 @@
 const char kHistoryClustersUsedCategoriesEventName[] =
     "NewTabPage.HistoryClusters.UsedCategories";
 
+namespace {
+
+// Returns a pair consisting of the most frequent names (in case of a tie), and
+// their associated count.
+std::pair<std::set<std::string>, size_t> GetMostFrequent(
+    const std::set<std::string>& names,
+    const std::vector<float>& counts) {
+  CHECK_EQ(names.size(), counts.size());
+
+  int most_frequent_count = 0;
+  std::set<std::string> most_frequent;
+  int i = 0;
+  for (const auto& name : names) {
+    int count = floor(counts[i]);
+    if (count > most_frequent_count) {
+      most_frequent_count = counts[i];
+      most_frequent.clear();
+    }
+    if (count == most_frequent_count) {
+      most_frequent.insert(name);
+    }
+    i++;
+  }
+
+  return {std::move(most_frequent), static_cast<size_t>(most_frequent_count)};
+}
+
+}  // namespace
+
 HistoryClustersModuleRanker::HistoryClustersModuleRanker(
     optimization_guide::OptimizationGuideModelProvider* model_provider,
     segmentation_platform::SegmentationPlatformService*
         segmentation_platform_service,
+    history::HistoryService* history_service,
     CartService* cart_service,
     const base::flat_set<std::string>& category_boostlist)
     : segmentation_platform_service_(segmentation_platform_service),
+      history_service_(history_service),
       cart_service_(cart_service),
       category_boostlist_(category_boostlist),
       query_day_count_(GetFieldTrialParamByFeatureAsInt(
@@ -71,12 +106,36 @@
     ClustersCallback callback) {
   DCHECK(!clusters.empty());
 
+  history::QueryOptions query_options;
+  query_options.end_time = base::Time::Now();
+  query_options.begin_time =
+      query_options.end_time - base::Days(query_day_count_);
+  history_service_->GetAnnotatedVisits(
+      query_options, false, false,
+      base::BindOnce(&HistoryClustersModuleRanker::OnGotAnnotatedVisits,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(clusters),
+                     std::move(callback)),
+      &task_tracker_);
+}
+
+void HistoryClustersModuleRanker::OnGotAnnotatedVisits(
+    std::vector<history::Cluster> clusters,
+    ClustersCallback callback,
+    const std::vector<history::AnnotatedVisit> annotated_visits) {
   auto metrics_ready_callback =
-      base::BindOnce(&HistoryClustersModuleRanker::OnClusterMetricsReady,
+      base::BindOnce(&HistoryClustersModuleRanker::OnMetricsReady,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback));
+  if (annotated_visits.empty()) {
+    OnMetricsQueryDataReady(
+        std::move(metrics_ready_callback), std::move(clusters),
+        /*category_ids=*/{},
+        segmentation_platform::DatabaseClient::ResultStatus::kError, {});
+    return;
+  }
   if (!segmentation_platform_service_) {
     OnMetricsQueryDataReady(
         std::move(metrics_ready_callback), std::move(clusters),
+        /*category_ids=*/{},
         segmentation_platform::DatabaseClient::ResultStatus::kError, {});
     return;
   }
@@ -85,6 +144,7 @@
   if (!client) {
     OnMetricsQueryDataReady(
         std::move(metrics_ready_callback), std::move(clusters),
+        /*category_ids=*/{},
         segmentation_platform::DatabaseClient::ResultStatus::kError, {});
     return;
   }
@@ -105,30 +165,52 @@
   segmentation_platform::DatabaseApiClients::AddSumGroupQuery(
       writer, kHistoryClusterUsedEventName, cluster_ids, query_day_count_);
 
+  std::set<std::string> category_ids;
+  for (const auto& annotated_visit : annotated_visits) {
+    for (const auto& category :
+         annotated_visit.content_annotations.model_annotations.categories) {
+      category_ids.insert(category.id);
+    }
+  }
+  if (!category_ids.empty()) {
+    segmentation_platform::DatabaseApiClients::AddSumGroupQuery(
+        writer, kHistoryClustersSeenCategoriesEventName, category_ids,
+        query_day_count_);
+    segmentation_platform::DatabaseApiClients::AddSumGroupQuery(
+        writer, kHistoryClustersUsedCategoriesEventName, category_ids,
+        query_day_count_);
+  }
+
   // The resulting vector of floats produced by the queries specified in the
   // `metadata` above is guaranteed to be of size equal to 2 * the size of the
-  // `cluster_ids` set.
+  // `cluster_ids` set + 2 * the size of the `category_ids` set.
   client->ProcessFeatures(
       metadata, base::Time::Now(),
       base::BindOnce(&HistoryClustersModuleRanker::OnMetricsQueryDataReady,
                      weak_ptr_factory_.GetWeakPtr(),
-                     std::move(metrics_ready_callback), std::move(clusters)));
+                     std::move(metrics_ready_callback), std::move(clusters),
+                     std::move(category_ids)));
 }
 
 void HistoryClustersModuleRanker::OnMetricsQueryDataReady(
     base::OnceCallback<void(std::vector<history::Cluster>,
-                            std::vector<HistoryClusterMetrics>)> callback,
+                            std::vector<HistoryClusterMetrics>,
+                            HistoryClustersCategoryMetrics)> callback,
     std::vector<history::Cluster> clusters,
+    const std::set<std::string> category_ids,
     segmentation_platform::DatabaseClient::ResultStatus status,
     const segmentation_platform::ModelProvider::Request& result) {
   if (status != segmentation_platform::DatabaseClient::ResultStatus::kSuccess) {
-    std::move(callback).Run(std::move(clusters), {});
+    std::move(callback).Run(std::move(clusters), {}, {});
     return;
   }
 
-  // Split the single query results vector into two vectors, one for
-  // each of the requested queries.
-  DCHECK_EQ(result.size() / 2, clusters.size());
+  // The result vector size should match the expected sizes for the 4 queries
+  // specified above as part of the metadata. The sum group query is guaranteed
+  // to produce as many vector entries as `metric_names` are provided as input.
+  CHECK_EQ(result.size(), (2 * clusters.size() + 2 * category_ids.size()));
+  // Split the single query results vector into multiple vectors, one
+  // for each of the requested queries.
   auto seen_counts =
       std::vector<float>(result.begin(), result.begin() + clusters.size());
   auto used_counts =
@@ -141,29 +223,54 @@
     metrics.push_back(std::move(cluster_metrics));
   }
 
-  std::move(callback).Run(std::move(clusters), std::move(metrics));
+  if (category_ids.empty()) {
+    std::move(callback).Run(std::move(clusters), std::move(metrics), {});
+    return;
+  }
+
+  auto category_frequencies_it = result.begin() + 2 * clusters.size();
+  auto most_frequently_seen = GetMostFrequent(
+      category_ids,
+      std::vector<float>(category_frequencies_it,
+                         category_frequencies_it + category_ids.size()));
+  auto most_frequently_used = GetMostFrequent(
+      category_ids,
+      std::vector<float>(category_frequencies_it + category_ids.size(),
+                         result.end()));
+  HistoryClustersCategoryMetrics category_metrics = {
+      std::move(most_frequently_seen.first),
+      most_frequently_seen.second,
+      std::move(most_frequently_used.first),
+      most_frequently_used.second,
+  };
+
+  std::move(callback).Run(std::move(clusters), std::move(metrics),
+                          std::move(category_metrics));
 }
 
-void HistoryClustersModuleRanker::OnClusterMetricsReady(
+void HistoryClustersModuleRanker::OnMetricsReady(
     ClustersCallback callback,
     std::vector<history::Cluster> clusters,
-    std::vector<HistoryClusterMetrics> cluster_metrics) {
+    std::vector<HistoryClusterMetrics> cluster_metrics,
+    HistoryClustersCategoryMetrics category_metrics) {
   if (IsCartModuleEnabled() && cart_service_) {
     cart_service_->LoadAllActiveCarts(
         base::BindOnce(&HistoryClustersModuleRanker::OnAllSignalsReady,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(clusters),
-                       std::move(cluster_metrics), std::move(callback)));
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+                       std::move(clusters), std::move(cluster_metrics),
+                       std::move(category_metrics)));
   } else {
-    OnAllSignalsReady(std::move(clusters), std::move(cluster_metrics),
-                      std::move(callback),
+    OnAllSignalsReady(std::move(callback), std::move(clusters),
+                      std::move(cluster_metrics), std::move(category_metrics),
                       /*success=*/false, /*active_carts=*/{});
   }
 }
 
 void HistoryClustersModuleRanker::OnAllSignalsReady(
-    std::vector<history::Cluster> clusters,
-    std::vector<HistoryClusterMetrics> cluster_metrics,
     ClustersCallback callback,
+    std::vector<history::Cluster> clusters,
+    const std::vector<HistoryClusterMetrics>& cluster_metrics,
+    const HistoryClustersCategoryMetrics& category_metrics,
     bool success,
     std::vector<CartDB::KeyAndValue> active_carts) {
   auto ranking_signals =
@@ -173,7 +280,8 @@
     ranking_signals->emplace_back(
         active_carts, category_boostlist_, clusters.at(i),
         !cluster_metrics.empty() ? cluster_metrics.at(i)
-                                 : HistoryClusterMetrics());
+                                 : HistoryClusterMetrics(),
+        category_metrics);
   }
 
 #if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranker.h b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranker.h
index 79cc6625..585c7a25 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranker.h
+++ b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranker.h
@@ -13,6 +13,7 @@
 #include "base/containers/flat_map.h"
 #include "base/functional/callback.h"
 #include "base/memory/weak_ptr.h"
+#include "base/task/cancelable_task_tracker.h"
 #include "chrome/browser/cart/cart_db.h"
 #include "components/history/core/browser/history_types.h"
 #include "components/optimization_guide/machine_learning_tflite_buildflags.h"
@@ -24,6 +25,10 @@
 extern const char kHistoryClustersSeenCategoriesEventName[];
 extern const char kHistoryClustersUsedCategoriesEventName[];
 
+namespace history {
+class HistoryService;
+}  // namespace history
+
 namespace optimization_guide {
 class OptimizationGuideModelProvider;
 }  // namespace optimization_guide
@@ -34,6 +39,7 @@
 
 class CartService;
 struct HistoryClusterMetrics;
+struct HistoryClustersCategoryMetrics;
 class HistoryClustersModuleRankingModelHandler;
 class HistoryClustersModuleRankingSignals;
 
@@ -44,6 +50,7 @@
       optimization_guide::OptimizationGuideModelProvider* model_provider,
       segmentation_platform::SegmentationPlatformService*
           segmentation_platform_service,
+      history::HistoryService* history_service,
       CartService* cart_service,
       const base::flat_set<std::string>& category_boostlist);
   ~HistoryClustersModuleRanker();
@@ -63,27 +70,37 @@
 #endif
 
  private:
+  // Callback invoked when user observed visits data is ready.
+  void OnGotAnnotatedVisits(
+      std::vector<history::Cluster> clusters,
+      ClustersCallback callback,
+      const std::vector<history::AnnotatedVisit> annotated_visits);
+
   // Callback invoked with the segmentation framework's metrics query data is
   // ready.
   void OnMetricsQueryDataReady(
       base::OnceCallback<void(std::vector<history::Cluster>,
-                              std::vector<HistoryClusterMetrics>)> callback,
+                              std::vector<HistoryClusterMetrics>,
+                              HistoryClustersCategoryMetrics)> callback,
       std::vector<history::Cluster> clusters,
+      const std::set<std::string> category_ids,
       segmentation_platform::DatabaseClient::ResultStatus status,
       const segmentation_platform::ModelProvider::Request& result);
 
-  // Callback invoked when the clusters' metrics are ready.
-  void OnClusterMetricsReady(
-      ClustersCallback callback,
-      std::vector<history::Cluster> clusters,
-      std::vector<HistoryClusterMetrics> cluster_metrics);
+  // Callback invoked when the cluster related metrics are ready.
+  void OnMetricsReady(ClustersCallback callback,
+                      std::vector<history::Cluster> clusters,
+                      std::vector<HistoryClusterMetrics> cluster_metrics,
+                      HistoryClustersCategoryMetrics category_metrics);
 
   // Callback invoked when all signals for ranking are ready.
-  void OnAllSignalsReady(std::vector<history::Cluster> clusters,
-                         std::vector<HistoryClusterMetrics> cluster_metrics,
-                         ClustersCallback callback,
-                         bool success,
-                         std::vector<CartDB::KeyAndValue> active_carts);
+  void OnAllSignalsReady(
+      ClustersCallback callback,
+      std::vector<history::Cluster> clusters,
+      const std::vector<HistoryClusterMetrics>& cluster_metrics,
+      const HistoryClustersCategoryMetrics& category_metrics,
+      bool success,
+      std::vector<CartDB::KeyAndValue> active_carts);
 
   // Runs the fallback heuristic if `model_handler_` is not instantiated or if
   // the model is not available.
@@ -98,6 +115,9 @@
   raw_ptr<segmentation_platform::SegmentationPlatformService>
       segmentation_platform_service_;
 
+  // The service used to fetch all available category ids;
+  raw_ptr<history::HistoryService> history_service_;
+
   // The cart service used to check for active carts.
   raw_ptr<CartService> cart_service_;
 
@@ -111,6 +131,9 @@
   // The model returns a float between -1.0 and 0.
   float threshold_param_;
 
+  // The task tracker for the HistoryService callbacks.
+  base::CancelableTaskTracker task_tracker_;
+
 #if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
   // Callback invoked when `model_handler_` has completed scoring of `clusters`.
   void OnBatchModelExecutionComplete(
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranker_unittest.cc b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranker_unittest.cc
index c722a16..b5e15bdbf 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranker_unittest.cc
+++ b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranker_unittest.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranker.h"
 
+#include <set>
+#include <string>
+
 #include "base/run_loop.h"
 #include "base/test/gmock_callback_support.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -12,7 +15,10 @@
 #include "chrome/browser/cart/cart_service.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/new_tab_page/modules/history_clusters/cart/cart_processor.h"
+#include "chrome/browser/new_tab_page/modules/history_clusters/history_clusters_test_support.h"
+#include "chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_category_metrics.h"
 #include "chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_signals.h"
+#include "chrome/browser/new_tab_page/modules/test_support.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/history_clusters/core/clustering_test_utils.h"
 #include "components/history_clusters/core/history_clusters_util.h"
@@ -33,6 +39,7 @@
 
 namespace {
 
+using ntp::MockHistoryService;
 using segmentation_platform::DatabaseClient;
 using FeaturesCallback =
     segmentation_platform::DatabaseClient::FeaturesCallback;
@@ -72,6 +79,8 @@
 
     mock_database_client_ = std::make_unique<MockDatabaseClient>();
 
+    mock_history_service_ = std::make_unique<MockHistoryService>();
+
     mock_cart_service_ =
         std::make_unique<MockCartService>(testing_profile_.get());
   }
@@ -121,6 +130,8 @@
 
   MockDatabaseClient& mock_database_client() { return *mock_database_client_; }
 
+  MockHistoryService& mock_history_service() { return *mock_history_service_; }
+
   MockCartService& mock_cart_service() { return *mock_cart_service_; }
 
  protected:
@@ -132,6 +143,7 @@
   std::unique_ptr<MockSegmentationPlatformService>
       mock_segmentation_platform_service_;
   std::unique_ptr<MockDatabaseClient> mock_database_client_;
+  std::unique_ptr<MockHistoryService> mock_history_service_;
   std::unique_ptr<MockCartService> mock_cart_service_;
 };
 
@@ -179,11 +191,26 @@
         false;
   }
 
+  auto& history_service = mock_history_service();
+  EXPECT_CALL(history_service, GetAnnotatedVisits(_, _, _, _, _))
+      .WillOnce(Invoke(
+          [](const history::QueryOptions& options,
+             bool compute_redirect_chain_start_properties,
+             bool get_unclustered_visits_only,
+             history::HistoryService::GetAnnotatedVisitsCallback callback,
+             base::CancelableTaskTracker* tracker)
+              -> base::CancelableTaskTracker::TaskId {
+            std::move(callback).Run({});
+            return 0;
+          }));
+
   base::flat_set<std::string> boost = {};
   auto module_ranker = std::make_unique<HistoryClustersModuleRanker>(
       /*optimization_guide_model_provider=*/nullptr,
-      /*segmentation_platform_service=*/nullptr, /*cart_service=*/nullptr,
-      boost);
+      /*segmentation_platform_service=*/nullptr,
+      /*history_service=*/&history_service,
+      /*cart_service=*/nullptr, boost);
+
   base::flat_map<int64_t, HistoryClustersModuleRankingSignals> ranking_signals;
   std::vector<history::Cluster> clusters =
       RankClusters(module_ranker.get(), {cluster1, cluster2}, &ranking_signals);
@@ -284,11 +311,26 @@
         false;
   }
 
+  auto& history_service = mock_history_service();
+  EXPECT_CALL(history_service, GetAnnotatedVisits(_, _, _, _, _))
+      .WillOnce(Invoke(
+          [](const history::QueryOptions& options,
+             bool compute_redirect_chain_start_properties,
+             bool get_unclustered_visits_only,
+             history::HistoryService::GetAnnotatedVisitsCallback callback,
+             base::CancelableTaskTracker* tracker)
+              -> base::CancelableTaskTracker::TaskId {
+            std::move(callback).Run({});
+            return 0;
+          }));
+
   base::flat_set<std::string> boost = {"boosted", "boostedbuthidden"};
   auto module_ranker = std::make_unique<HistoryClustersModuleRanker>(
       /*optimization_guide_model_provider=*/nullptr,
-      /*segmentation_platform_service=*/nullptr, /*cart_service=*/nullptr,
-      boost);
+      /*segmentation_platform_service=*/nullptr,
+      /*history_service=*/&history_service,
+      /*cart_service=*/nullptr, boost);
+
   base::flat_map<int64_t, HistoryClustersModuleRankingSignals> ranking_signals;
   std::vector<history::Cluster> clusters = RankClusters(
       module_ranker.get(), {cluster1, cluster2, cluster3}, &ranking_signals);
@@ -328,44 +370,89 @@
   EXPECT_TRUE(ranking_signals[3].belongs_to_boosted_category);
 }
 
-TEST_F(HistoryClustersModuleRankerTest, RecencyWithClusterMetrics) {
-  base::Time now = base::Time::Now();
-  history::AnnotatedVisit visit1 =
-      history_clusters::testing::CreateDefaultAnnotatedVisit(
-          1, GURL("https://github.com/"));
-  visit1.visit_row.is_known_to_sync = true;
-  visit1.content_annotations.has_url_keyed_image = true;
-  visit1.visit_row.visit_time = now - base::Hours(2);
-  history::Cluster cluster1;
-  cluster1.cluster_id = 1;
-  cluster1.visits = {history_clusters::testing::CreateClusterVisit(
-      visit1, /*normalized_url=*/std::nullopt, 1.0)};
+class HistoryClustersModuleRankerWithMetricsTest
+    : public HistoryClustersModuleRankerTest,
+      public ::testing::WithParamInterface<bool> {};
 
-  history::AnnotatedVisit visit2 =
+INSTANTIATE_TEST_SUITE_P(All,
+                         HistoryClustersModuleRankerWithMetricsTest,
+                         ::testing::Bool());
+
+TEST_P(HistoryClustersModuleRankerWithMetricsTest,
+       RecencyWithClusterAndCategoryMetrics) {
+  base::Time now = base::Time::Now();
+  bool add_category_metrics = GetParam();
+  const int kSampleCategoryWeight = 95;
+
+  const auto kSampleCategory1 = std::string("category1");
+  history::AnnotatedVisit cluster1_visit =
       history_clusters::testing::CreateDefaultAnnotatedVisit(
           1, GURL("https://github.com/"));
-  visit2.visit_row.is_known_to_sync = true;
-  visit2.content_annotations.has_url_keyed_image = true;
-  visit2.visit_row.visit_time = now - base::Hours(1);
-  history::Cluster cluster2;
-  cluster2.cluster_id = 2;
-  cluster2.visits = {history_clusters::testing::CreateClusterVisit(
-      visit2, /*normalized_url=*/std::nullopt, 1.0)};
+  cluster1_visit.visit_row.is_known_to_sync = true;
+  cluster1_visit.content_annotations.has_url_keyed_image = true;
+  cluster1_visit.visit_row.visit_time = now - base::Hours(2);
+  const auto kSampleCategory2 = std::string("category2");
+  history::AnnotatedVisit cluster2_visit =
+      history_clusters::testing::CreateDefaultAnnotatedVisit(
+          1, GURL("https://github.com/"));
+  cluster2_visit.visit_row.is_known_to_sync = true;
+  cluster2_visit.content_annotations.has_url_keyed_image = true;
+  cluster2_visit.visit_row.visit_time = now - base::Hours(1);
+
+  if (add_category_metrics) {
+    cluster1_visit.content_annotations.model_annotations.categories = {
+        {kSampleCategory1, kSampleCategoryWeight}};
+
+    cluster2_visit.content_annotations.model_annotations.categories = {
+        {kSampleCategory2, kSampleCategoryWeight}};
+  }
+
+  auto& history_service = mock_history_service();
+  EXPECT_CALL(history_service, GetAnnotatedVisits(_, false, false, _, _))
+      .WillOnce(Invoke(
+          [&cluster1_visit, &cluster2_visit](
+              const history::QueryOptions& options,
+              bool compute_redirect_chain_start_properties,
+              bool get_unclustered_visits_only,
+              history::HistoryService::GetAnnotatedVisitsCallback callback,
+              base::CancelableTaskTracker* tracker)
+              -> base::CancelableTaskTracker::TaskId {
+            std::move(callback).Run({cluster1_visit, cluster2_visit});
+            return 0;
+          }));
 
   auto& segmentation_platform_service = mock_segmentation_platform_service();
   EXPECT_CALL(segmentation_platform_service, GetDatabaseClient())
       .WillOnce(testing::Return(&mock_database_client()));
 
+  std::vector<float> frequencies = {2.0, 1.0, 1.0, 0.0};
+  if (add_category_metrics) {
+    frequencies.insert(frequencies.end(),
+                       {/*category1 seen*/ 3, /*category2 seen*/ 7,
+                        /*category1 used*/ 5, /*category2 used*/ 10});
+  }
   EXPECT_CALL(mock_database_client(), ProcessFeatures(_, _, _))
-      .WillOnce(Invoke([](const SegmentationModelMetadata& metadata,
-                          base::Time end_time, FeaturesCallback callback) {
-        std::move(callback).Run(ResultStatus::kSuccess, {2.0, 1.0, 1.0, 0.0});
+      .WillOnce(Invoke([&frequencies](const SegmentationModelMetadata& metadata,
+                                      base::Time end_time,
+                                      FeaturesCallback callback) {
+        std::move(callback).Run(ResultStatus::kSuccess, frequencies);
       }));
 
   base::flat_set<std::string> boost = {};
   auto module_ranker = std::make_unique<HistoryClustersModuleRanker>(
       /*optimization_guide_model_provider=*/nullptr,
-      &segmentation_platform_service, /*cart_service=*/nullptr, boost);
+      /*segmentation_platform_service=*/&segmentation_platform_service,
+      /*history_service=*/&history_service,
+      /*cart_service=*/nullptr, boost);
+
+  history::Cluster cluster1;
+  cluster1.cluster_id = 1;
+  cluster1.visits = {history_clusters::testing::CreateClusterVisit(
+      cluster1_visit, /*normalized_url=*/std::nullopt, 1.0)};
+  history::Cluster cluster2;
+  cluster2.cluster_id = 2;
+  cluster2.visits = {history_clusters::testing::CreateClusterVisit(
+      cluster2_visit, /*normalized_url=*/std::nullopt, 1.0)};
   base::flat_map<int64_t, HistoryClustersModuleRankingSignals> ranking_signals;
   std::vector<history::Cluster> clusters =
       RankClusters(module_ranker.get(), {cluster1, cluster2}, &ranking_signals);
@@ -378,9 +465,23 @@
   EXPECT_TRUE(ranking_signals.contains(1));
   EXPECT_EQ(ranking_signals[1].num_times_seen_last_24h, 2u);
   EXPECT_EQ(ranking_signals[1].num_times_used_last_24h, 1u);
+  EXPECT_EQ(ranking_signals[1].belongs_to_most_seen_category, false);
+  EXPECT_EQ(ranking_signals[1].belongs_to_most_used_category, false);
   EXPECT_TRUE(ranking_signals.contains(2));
   EXPECT_EQ(ranking_signals[2].num_times_seen_last_24h, 1u);
   EXPECT_EQ(ranking_signals[2].num_times_used_last_24h, 0u);
+  if (add_category_metrics) {
+    EXPECT_EQ(ranking_signals[1].most_frequent_category_seen_count_last_24h,
+              7u);
+    EXPECT_EQ(ranking_signals[1].most_frequent_category_used_count_last_24h,
+              10u);
+    EXPECT_EQ(ranking_signals[2].belongs_to_most_seen_category, true);
+    EXPECT_EQ(ranking_signals[2].belongs_to_most_used_category, true);
+    EXPECT_EQ(ranking_signals[2].most_frequent_category_seen_count_last_24h,
+              7u);
+    EXPECT_EQ(ranking_signals[2].most_frequent_category_used_count_last_24h,
+              10u);
+  }
 }
 
 class HistoryClustersModuleRankerCartTest
@@ -442,10 +543,24 @@
           testing::Invoke([&carts](CartDB::LoadCallback callback) -> void {
             std::move(callback).Run(true, carts);
           })));
+  auto& history_service = mock_history_service();
+  EXPECT_CALL(history_service, GetAnnotatedVisits(_, _, _, _, _))
+      .WillOnce(Invoke(
+          [](const history::QueryOptions& options,
+             bool compute_redirect_chain_start_properties,
+             bool get_unclustered_visits_only,
+             history::HistoryService::GetAnnotatedVisitsCallback callback,
+             base::CancelableTaskTracker* tracker)
+              -> base::CancelableTaskTracker::TaskId {
+            std::move(callback).Run({});
+            return 0;
+          }));
+
   base::flat_map<int64_t, HistoryClustersModuleRankingSignals> ranking_signals;
   auto module_ranker = std::make_unique<HistoryClustersModuleRanker>(
       /*optimization_guide_model_provider=*/nullptr,
-      /*segmentation_platform_service=*/nullptr, &cart_service, boost);
+      /*segmentation_platform_service=*/nullptr, &history_service,
+      &cart_service, boost);
   std::vector<history::Cluster> clusters =
       RankClusters(module_ranker.get(), {cluster1, cluster2}, &ranking_signals);
 
@@ -574,11 +689,24 @@
         false;
   }
 
+  auto& history_service = mock_history_service();
+  EXPECT_CALL(history_service, GetAnnotatedVisits(_, _, _, _, _))
+      .WillOnce(Invoke(
+          [](const history::QueryOptions& options,
+             bool compute_redirect_chain_start_properties,
+             bool get_unclustered_visits_only,
+             history::HistoryService::GetAnnotatedVisitsCallback callback,
+             base::CancelableTaskTracker* tracker)
+              -> base::CancelableTaskTracker::TaskId {
+            std::move(callback).Run({});
+            return 0;
+          }));
   auto model_provider = std::make_unique<
       optimization_guide::TestOptimizationGuideModelProvider>();
   base::flat_set<std::string> boost = {};
   auto module_ranker = std::make_unique<HistoryClustersModuleRanker>(
       model_provider.get(), /*segmentation_platform_service=*/nullptr,
+      &history_service,
       /*cart_service=*/nullptr, boost);
   base::flat_map<int64_t, HistoryClustersModuleRankingSignals> ranking_signals;
   std::vector<history::Cluster> clusters =
@@ -688,6 +816,18 @@
   auto& cart_service = mock_cart_service();
   cart_db::ChromeCartContentProto cart_proto;
   std::vector<CartDB::KeyAndValue> carts = {{"amazon.com", cart_proto}};
+  auto& history_service = mock_history_service();
+  EXPECT_CALL(history_service, GetAnnotatedVisits(_, _, _, _, _))
+      .WillOnce(Invoke(
+          [](const history::QueryOptions& options,
+             bool compute_redirect_chain_start_properties,
+             bool get_unclustered_visits_only,
+             history::HistoryService::GetAnnotatedVisitsCallback callback,
+             base::CancelableTaskTracker* tracker)
+              -> base::CancelableTaskTracker::TaskId {
+            std::move(callback).Run({});
+            return 0;
+          }));
   EXPECT_CALL(cart_service, LoadAllActiveCarts(base::test::IsNotNullCallback()))
       .WillOnce(testing::WithArgs<0>(
           testing::Invoke([&carts](CartDB::LoadCallback callback) -> void {
@@ -695,7 +835,7 @@
           })));
   auto module_ranker = std::make_unique<HistoryClustersModuleRanker>(
       model_provider.get(), /*segmentation_platform_service=*/nullptr,
-      &cart_service, boost);
+      &history_service, &cart_service, boost);
   auto model_handler = std::make_unique<FakeModelHandler>(model_provider.get());
   module_ranker->OverrideModelHandlerForTesting(std::move(model_handler));
   base::flat_map<int64_t, HistoryClustersModuleRankingSignals> ranking_signals;
@@ -755,12 +895,25 @@
   history::Cluster cluster2 = cluster1;
   cluster2.cluster_id = 2;
 
+  auto& history_service = mock_history_service();
+  EXPECT_CALL(history_service, GetAnnotatedVisits(_, _, _, _, _))
+      .WillOnce(Invoke(
+          [](const history::QueryOptions& options,
+             bool compute_redirect_chain_start_properties,
+             bool get_unclustered_visits_only,
+             history::HistoryService::GetAnnotatedVisitsCallback callback,
+             base::CancelableTaskTracker* tracker)
+              -> base::CancelableTaskTracker::TaskId {
+            std::move(callback).Run({});
+            return 0;
+          }));
+
   base::flat_set<std::string> boost = {"boosted", "boostedbuthidden"};
   auto model_provider = std::make_unique<
       optimization_guide::TestOptimizationGuideModelProvider>();
   auto module_ranker = std::make_unique<HistoryClustersModuleRanker>(
       model_provider.get(), /*optimization_guide_model_provider=*/nullptr,
-      nullptr, boost);
+      &history_service, /*cart_service=*/nullptr, boost);
   auto model_handler =
       std::make_unique<MockHistoryClustersModuleRankingModelHandler>(
           model_provider.get());
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_metrics_logger_unittest.cc b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_metrics_logger_unittest.cc
index 73d17a7b..53c28ee 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_metrics_logger_unittest.cc
+++ b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_metrics_logger_unittest.cc
@@ -33,6 +33,9 @@
   signals1.num_abandoned_carts = 1;
   signals1.num_times_seen_last_24h = 1;
   signals1.num_times_used_last_24h = 1;
+  signals1.num_associated_categories = 2;
+  signals1.belongs_to_most_seen_category = true;
+  signals1.belongs_to_most_used_category = true;
 
   HistoryClustersModuleRankingSignals signals2;
   signals2.duration_since_most_recent_visit = base::Minutes(5);
@@ -43,6 +46,9 @@
   signals2.num_abandoned_carts = 0;
   signals2.num_times_seen_last_24h = 0;
   signals2.num_times_used_last_24h = 0;
+  signals2.num_associated_categories = 1;
+  signals2.belongs_to_most_seen_category = false;
+  signals2.belongs_to_most_used_category = false;
 
   HistoryClustersModuleRankingSignals should_not_be_logged;
   should_not_be_logged.duration_since_most_recent_visit = base::Minutes(100);
@@ -51,6 +57,9 @@
   should_not_be_logged.num_total_visits = 100;
   should_not_be_logged.num_unique_hosts = 300;
   should_not_be_logged.num_abandoned_carts = 100;
+  should_not_be_logged.num_associated_categories = 100;
+  should_not_be_logged.belongs_to_most_seen_category = false;
+  should_not_be_logged.belongs_to_most_used_category = false;
 
   HistoryClustersModuleRankingMetricsLogger logger(ukm::NoURLSourceId());
   logger.AddSignals({{1, signals1}, {2, signals2}, {3, should_not_be_logged}});
@@ -94,6 +103,18 @@
       ukm::builders::NewTabPage_HistoryClusters::kNumTimesUsedLast24hName, 1);
   test_ukm_recorder.ExpectEntryMetric(
       entry,
+      ukm::builders::NewTabPage_HistoryClusters::kNumAssociatedCategoriesName,
+      2);
+  test_ukm_recorder.ExpectEntryMetric(
+      entry,
+      ukm::builders::NewTabPage_HistoryClusters::kBelongsToMostSeenCategoryName,
+      1);
+  test_ukm_recorder.ExpectEntryMetric(
+      entry,
+      ukm::builders::NewTabPage_HistoryClusters::kBelongsToMostUsedCategoryName,
+      1);
+  test_ukm_recorder.ExpectEntryMetric(
+      entry,
       ukm::builders::NewTabPage_HistoryClusters::kDidEngageWithModuleName, 1);
   test_ukm_recorder.ExpectEntryMetric(
       entry, ukm::builders::NewTabPage_HistoryClusters::kDidDisableModuleName,
@@ -135,8 +156,24 @@
       ukm::builders::NewTabPage_HistoryClusters::kNumTimesUsedLast24hName, 0);
   test_ukm_recorder.ExpectEntryMetric(
       entry2,
+      ukm::builders::NewTabPage_HistoryClusters::kNumAssociatedCategoriesName,
+      1);
+  test_ukm_recorder.ExpectEntryMetric(
+      entry,
+      ukm::builders::NewTabPage_HistoryClusters::kBelongsToMostSeenCategoryName,
+      1);
+  test_ukm_recorder.ExpectEntryMetric(
+      entry,
+      ukm::builders::NewTabPage_HistoryClusters::kBelongsToMostUsedCategoryName,
+      1);
+  test_ukm_recorder.ExpectEntryMetric(
+      entry2,
       ukm::builders::NewTabPage_HistoryClusters::kDidEngageWithModuleName, 0);
   test_ukm_recorder.ExpectEntryMetric(
+      entry2,
+      ukm::builders::NewTabPage_HistoryClusters::kBelongsToMostUsedCategoryName,
+      0);
+  test_ukm_recorder.ExpectEntryMetric(
       entry2, ukm::builders::NewTabPage_HistoryClusters::kDidDisableModuleName,
       0);
   test_ukm_recorder.ExpectEntryMetric(
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_model_handler.cc b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_model_handler.cc
index eb646781..7dbb9534 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_model_handler.cc
+++ b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_model_handler.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_model_metadata.pb.h"
 #include "chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_signals.h"
 #include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/model_metadata.pb.h"
 
 namespace {
 
@@ -54,6 +55,28 @@
       case new_tab_page::proto::HISTORY_CLUSTERS_MODULE_RANKING_NUM_TIMES_USED:
         input_vector.push_back(signals.num_times_used_last_24h);
         break;
+      case new_tab_page::proto::
+          HISTORY_CLUSTERS_MODULE_RANKING_NUM_ASSOCIATED_CATEGORIES:
+        input_vector.push_back(signals.num_associated_categories);
+        break;
+      case new_tab_page::proto::
+          HISTORY_CLUSTERS_MODULE_RANKING_BELONGS_TO_MOST_SEEN_CATEGORY:
+        input_vector.push_back(signals.belongs_to_most_seen_category);
+        break;
+      case new_tab_page::proto::
+          HISTORY_CLUSTERS_MODULE_RANKING_BELONGS_TO_MOST_USED_CATEGORY:
+        input_vector.push_back(signals.belongs_to_most_used_category);
+        break;
+      case new_tab_page::proto::
+          HISTORY_CLUSTERS_MODULE_RANKING_MOST_FREQUENT_SEEN_CATEGORY_COUNT:
+        input_vector.push_back(
+            signals.most_frequent_category_seen_count_last_24h);
+        break;
+      case new_tab_page::proto::
+          HISTORY_CLUSTERS_MODULE_RANKING_MOST_FREQUENT_USED_CATEGORY_COUNT:
+        input_vector.push_back(
+            signals.most_frequent_category_used_count_last_24h);
+        break;
       default:
         NOTREACHED();
     }
@@ -68,6 +91,23 @@
   std::move(closure).Run();
 }
 
+std::optional<optimization_guide::proto::Any> CreateModelMetadata() {
+  new_tab_page::proto::HistoryClustersModuleRankingModelMetadata model_metadata;
+  model_metadata.set_version(
+      HistoryClustersModuleRankingSignals::kClientVersion);
+
+  std::string serialized_metadata;
+  model_metadata.SerializeToString(&serialized_metadata);
+  optimization_guide::proto::Any any;
+  any.set_value(std::move(serialized_metadata));
+  any.set_type_url(
+      "type.googleapis.com/"
+      "google.internal.chrome.optimizationguide.v1."
+      "HistoryClustersModuleRankingModelMetadata");
+
+  return any;
+}
+
 }  // namespace
 
 HistoryClustersModuleRankingModelHandler::
@@ -81,7 +121,7 @@
           /*model_inference_timeout=*/std::nullopt,
           optimization_guide::proto::OptimizationTarget::
               OPTIMIZATION_TARGET_NEW_TAB_PAGE_HISTORY_CLUSTERS_MODULE_RANKING,
-          /*model_metadata=*/std::nullopt) {
+          CreateModelMetadata()) {
   // Unloading the model is done via custom logic in this class.
   SetShouldUnloadModelOnComplete(false);
 }
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_model_handler_unittest.cc b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_model_handler_unittest.cc
index 261d885..00989ab 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_model_handler_unittest.cc
+++ b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_model_handler_unittest.cc
@@ -175,6 +175,11 @@
   inputs1.num_abandoned_carts = 1;
   inputs1.num_times_seen_last_24h = 2;
   inputs1.num_times_used_last_24h = 1;
+  inputs1.num_associated_categories = 2;
+  inputs1.belongs_to_most_seen_category = true;
+  inputs1.belongs_to_most_used_category = true;
+  inputs1.most_frequent_category_seen_count_last_24h = 2;
+  inputs1.most_frequent_category_used_count_last_24h = 1;
 
   HistoryClustersModuleRankingSignals inputs2;
   inputs2.duration_since_most_recent_visit = base::Minutes(5);
@@ -185,6 +190,11 @@
   inputs2.num_abandoned_carts = 0;
   inputs2.num_times_seen_last_24h = 1;
   inputs2.num_times_used_last_24h = 0;
+  inputs2.num_associated_categories = 1;
+  inputs2.belongs_to_most_seen_category = false;
+  inputs2.belongs_to_most_used_category = false;
+  inputs2.most_frequent_category_seen_count_last_24h = 1;
+  inputs2.most_frequent_category_used_count_last_24h = 1;
 
   {
     new_tab_page::proto::HistoryClustersModuleRankingModelMetadata metadata;
@@ -245,6 +255,54 @@
 
     EXPECT_THAT(GetOutputs({inputs1, inputs2}), ElementsAre(2 + 1, 1 + 0));
   }
+
+  {
+    new_tab_page::proto::HistoryClustersModuleRankingModelMetadata metadata;
+    metadata.set_version(HistoryClustersModuleRankingSignals::kClientVersion);
+    metadata.add_signals(
+        new_tab_page::proto::
+            HISTORY_CLUSTERS_MODULE_RANKING_NUM_ABANDONED_CARTS);
+    metadata.add_signals(
+        new_tab_page::proto::
+            HISTORY_CLUSTERS_MODULE_RANKING_NUM_ASSOCIATED_CATEGORIES);
+    PushModelFileToModelExecutor(metadata);
+
+    EXPECT_TRUE(model_handler()->CanExecuteAvailableModel());
+
+    EXPECT_THAT(GetOutputs({inputs1, inputs2}), ElementsAre(1 + 2, 0 + 1));
+  }
+
+  {
+    new_tab_page::proto::HistoryClustersModuleRankingModelMetadata metadata;
+    metadata.set_version(HistoryClustersModuleRankingSignals::kClientVersion);
+    metadata.add_signals(
+        new_tab_page::proto::
+            HISTORY_CLUSTERS_MODULE_RANKING_BELONGS_TO_MOST_SEEN_CATEGORY);
+    metadata.add_signals(
+        new_tab_page::proto::
+            HISTORY_CLUSTERS_MODULE_RANKING_BELONGS_TO_MOST_USED_CATEGORY);
+    PushModelFileToModelExecutor(metadata);
+
+    EXPECT_TRUE(model_handler()->CanExecuteAvailableModel());
+
+    EXPECT_THAT(GetOutputs({inputs1, inputs2}), ElementsAre(1 + 1, 0 + 0));
+  }
+
+  {
+    new_tab_page::proto::HistoryClustersModuleRankingModelMetadata metadata;
+    metadata.set_version(HistoryClustersModuleRankingSignals::kClientVersion);
+    metadata.add_signals(
+        new_tab_page::proto::
+            HISTORY_CLUSTERS_MODULE_RANKING_MOST_FREQUENT_SEEN_CATEGORY_COUNT);
+    metadata.add_signals(
+        new_tab_page::proto::
+            HISTORY_CLUSTERS_MODULE_RANKING_MOST_FREQUENT_USED_CATEGORY_COUNT);
+    PushModelFileToModelExecutor(metadata);
+
+    EXPECT_TRUE(model_handler()->CanExecuteAvailableModel());
+
+    EXPECT_THAT(GetOutputs({inputs1, inputs2}), ElementsAre(2 + 1, 1 + 1));
+  }
 }
 
 }  // namespace
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_model_metadata.proto b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_model_metadata.proto
index d84ba7da..8ef22b4d3 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_model_metadata.proto
+++ b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_model_metadata.proto
@@ -27,6 +27,18 @@
   HISTORY_CLUSTERS_MODULE_RANKING_NUM_TIMES_SEEN = 7;
   // The number of times the cluster was seen by the user in a fixed time range.
   HISTORY_CLUSTERS_MODULE_RANKING_NUM_TIMES_USED = 8;
+  // The number of categories associated with the cluster.
+  HISTORY_CLUSTERS_MODULE_RANKING_NUM_ASSOCIATED_CATEGORIES = 9;
+  // Whether the cluster is of the most seen category.
+  HISTORY_CLUSTERS_MODULE_RANKING_BELONGS_TO_MOST_SEEN_CATEGORY = 10;
+  // Whether the cluster is of the most used category.
+  HISTORY_CLUSTERS_MODULE_RANKING_BELONGS_TO_MOST_USED_CATEGORY = 11;
+  // The number of times the most frequently seen category by a user occurred in
+  // a fixed time range.
+  HISTORY_CLUSTERS_MODULE_RANKING_MOST_FREQUENT_SEEN_CATEGORY_COUNT = 12;
+  // The number of times the most frequently used category by a user occurred in
+  // a fixed time range.
+  HISTORY_CLUSTERS_MODULE_RANKING_MOST_FREQUENT_USED_CATEGORY_COUNT = 13;
 }
 
 message HistoryClustersModuleRankingModelMetadata {
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_signals.cc b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_signals.cc
index 967224a..556786f 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_signals.cc
+++ b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_signals.cc
@@ -6,17 +6,37 @@
 
 #include "chrome/browser/new_tab_page/modules/history_clusters/cart/cart_processor.h"
 #include "chrome/browser/new_tab_page/modules/history_clusters/ranking/history_cluster_metrics.h"
+#include "chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_category_metrics.h"
 #include "components/commerce/core/proto/cart_db_content.pb.h"
 #include "components/history_clusters/core/history_clusters_util.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "services/metrics/public/cpp/metrics_utils.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 
+#include <set>
+#include <string>
+
+namespace {
+
+bool HasSetIntersection(const std::set<std::string>& cluster_category_ids,
+                        const std::set<std::string>& category_ids) {
+  std::vector<std::string> categories_intersection;
+  std::set_intersection(cluster_category_ids.begin(),
+                        cluster_category_ids.end(), category_ids.begin(),
+                        category_ids.end(),
+                        std::back_inserter(categories_intersection));
+
+  return !categories_intersection.empty();
+}
+
+}  // namespace
+
 HistoryClustersModuleRankingSignals::HistoryClustersModuleRankingSignals(
     const std::vector<CartDB::KeyAndValue>& active_carts,
     const base::flat_set<std::string>& category_boostlist,
     const history::Cluster& cluster,
-    const HistoryClusterMetrics& metrics)
+    const HistoryClusterMetrics& metrics,
+    const HistoryClustersCategoryMetrics& category_metrics)
     : duration_since_most_recent_visit(
           base::Time::Now() -
           cluster.GetMostRecentVisit().annotated_visit.visit_row.visit_time),
@@ -54,6 +74,17 @@
 
   num_unique_hosts = hosts.size();
   num_abandoned_carts = cart_tlds.size();
+  const auto cluster_categories =
+      history_clusters::GetClusterCategoryIds(cluster);
+  num_associated_categories = cluster_categories.size();
+  belongs_to_most_seen_category = HasSetIntersection(
+      cluster_categories, category_metrics.most_frequently_seen_category_ids);
+  belongs_to_most_used_category = HasSetIntersection(
+      cluster_categories, category_metrics.most_frequently_used_category_ids);
+  most_frequent_category_seen_count_last_24h =
+      category_metrics.most_frequent_seen_category_for_period_count;
+  most_frequent_category_used_count_last_24h =
+      category_metrics.most_frequent_used_category_for_period_count;
 }
 
 HistoryClustersModuleRankingSignals::HistoryClustersModuleRankingSignals() =
@@ -75,4 +106,13 @@
   ukm_entry_builder->SetNumAbandonedCarts(num_abandoned_carts);
   ukm_entry_builder->SetNumTimesSeenLast24h(num_times_seen_last_24h);
   ukm_entry_builder->SetNumTimesUsedLast24h(num_times_used_last_24h);
+  ukm_entry_builder->SetNumAssociatedCategories(num_associated_categories);
+  ukm_entry_builder->SetBelongsToMostSeenCategory(
+      belongs_to_most_seen_category);
+  ukm_entry_builder->SetBelongsToMostUsedCategory(
+      belongs_to_most_used_category);
+  ukm_entry_builder->SetMostFrequentSeenCategoryCount(
+      most_frequent_category_seen_count_last_24h);
+  ukm_entry_builder->SetMostFrequentUsedCategoryCount(
+      most_frequent_category_used_count_last_24h);
 }
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_signals.h b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_signals.h
index 8ee6f38..4ab099e 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_signals.h
+++ b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_signals.h
@@ -13,6 +13,8 @@
 #include "components/commerce/core/proto/cart_db_content.pb.h"
 #include "components/history/core/browser/history_types.h"
 
+struct HistoryClustersCategoryMetrics;
+
 namespace ukm::builders {
 class NewTabPage_HistoryClusters;
 }  // namespace ukm::builders
@@ -22,14 +24,16 @@
 // The signals used to rank clusters for the history clusters module.
 class HistoryClustersModuleRankingSignals {
  public:
-  static constexpr int kClientVersion = 1;
+  static constexpr int kClientVersion = 2;
 
   // Creates signals from `cluster`.
   HistoryClustersModuleRankingSignals(
       const std::vector<CartDB::KeyAndValue>& active_carts,
       const base::flat_set<std::string>& category_boostlist,
       const history::Cluster& cluster,
-      const HistoryClusterMetrics& metrics);
+      const HistoryClusterMetrics& metrics,
+      const HistoryClustersCategoryMetrics& category_metrics);
+
   HistoryClustersModuleRankingSignals();
   ~HistoryClustersModuleRankingSignals();
   HistoryClustersModuleRankingSignals(
@@ -56,6 +60,20 @@
   size_t num_times_seen_last_24h = 0;
   // The number of times the cluster was used by the user in the last 24 hours.
   size_t num_times_used_last_24h = 0;
+  // The number of categories associated with the cluster.
+  size_t num_associated_categories = 0;
+  // Whether the cluster is of the most frequently seen category in the last 24
+  // hours.
+  bool belongs_to_most_seen_category = false;
+  // Whether the cluster is of the most frequently engaged category in the last
+  // 24 hours.
+  bool belongs_to_most_used_category = false;
+  // The number of times the most frequently seen category has been seen in the
+  // last 24 hours.
+  size_t most_frequent_category_seen_count_last_24h = 0;
+  // The number of times the most frequentluy used category has been used in the
+  // last 24 hours.
+  size_t most_frequent_category_used_count_last_24h = 0;
 };
 
 #endif  // CHROME_BROWSER_NEW_TAB_PAGE_MODULES_HISTORY_CLUSTERS_RANKING_HISTORY_CLUSTERS_MODULE_RANKING_SIGNALS_H_
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_signals_unittest.cc b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_signals_unittest.cc
index aba9af90..266992c5 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_signals_unittest.cc
+++ b/chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_module_ranking_signals_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/test/task_environment.h"
 #include "chrome/browser/new_tab_page/modules/history_clusters/ranking/history_cluster_metrics.h"
+#include "chrome/browser/new_tab_page/modules/history_clusters/ranking/history_clusters_category_metrics.h"
 #include "components/commerce/core/proto/cart_db_content.pb.h"
 #include "components/history_clusters/core/clustering_test_utils.h"
 #include "components/ukm/test_ukm_recorder.h"
@@ -55,8 +56,10 @@
 
   HistoryClusterMetrics cluster_metrics = {.num_times_seen = 0,
                                            .num_times_used = 0};
+  HistoryClustersCategoryMetrics category_metrics = {};
   HistoryClustersModuleRankingSignals signals(
-      /*active_carts=*/{}, /*category_boostlist=*/{}, cluster, cluster_metrics);
+      /*active_carts=*/{}, /*category_boostlist=*/{}, cluster, cluster_metrics,
+      category_metrics);
   EXPECT_GT(signals.duration_since_most_recent_visit.InMinutes(), 0);
   // Even though it says boosted, there is no passed-in boostlist so it's false.
   EXPECT_FALSE(signals.belongs_to_boosted_category);
@@ -65,8 +68,11 @@
   // github.com and search.com
   EXPECT_EQ(signals.num_unique_hosts, 2u);
   EXPECT_EQ(signals.num_abandoned_carts, 0u);
+  EXPECT_EQ(signals.num_associated_categories, 3u);
   EXPECT_EQ(signals.num_times_seen_last_24h, 0u);
   EXPECT_EQ(signals.num_times_used_last_24h, 0u);
+  EXPECT_EQ(signals.belongs_to_most_seen_category, false);
+  EXPECT_EQ(signals.belongs_to_most_used_category, false);
 
   // Verify UKM.
   ukm::TestAutoSetUkmRecorder test_ukm_recorder;
@@ -95,6 +101,9 @@
   test_ukm_recorder.ExpectEntryMetric(
       entry, ukm::builders::NewTabPage_HistoryClusters::kNumAbandonedCartsName,
       0);
+  test_ukm_recorder.ExpectEntryMetric(
+      entry, ukm::builders::NewTabPage_HistoryClusters::kNumAbandonedCartsName,
+      0);
 }
 
 TEST_F(HistoryClustersModuleRankingSignalsTest, ConstructorHasCartsAndBoost) {
@@ -133,8 +142,10 @@
   base::flat_set<std::string> category_boostlist = {"boosted"};
   HistoryClusterMetrics cluster_metrics = {.num_times_seen = 1,
                                            .num_times_used = 1};
+  HistoryClustersCategoryMetrics category_metrics = {};
   HistoryClustersModuleRankingSignals signals(active_carts, category_boostlist,
-                                              cluster, cluster_metrics);
+                                              cluster, cluster_metrics,
+                                              category_metrics);
   EXPECT_GT(signals.duration_since_most_recent_visit.InMinutes(), 0);
   EXPECT_TRUE(signals.belongs_to_boosted_category);
   EXPECT_EQ(signals.num_visits_with_image, 2u);
@@ -143,6 +154,7 @@
   EXPECT_EQ(signals.num_unique_hosts, 3u);
   // m.merchant.com and www.merchant.com should both match to merchant.com.
   EXPECT_EQ(signals.num_abandoned_carts, 1u);
+  EXPECT_EQ(signals.num_associated_categories, 3u);
   EXPECT_EQ(signals.num_times_seen_last_24h, 1u);
   EXPECT_EQ(signals.num_times_used_last_24h, 1u);
 
@@ -176,6 +188,9 @@
 }
 
 TEST_F(HistoryClustersModuleRankingSignalsTest, ConstructorWithMetrics) {
+  const auto kSampleCategory1 = std::string("category1");
+  const auto kSampleCategory2 = std::string("category2");
+
   history::Cluster cluster;
   cluster.cluster_id = 1;
   history::AnnotatedVisit visit =
@@ -183,15 +198,25 @@
           1, GURL("https://github.com/"));
   visit.visit_row.is_known_to_sync = true;
   visit.content_annotations.has_url_keyed_image = true;
+  visit.content_annotations.model_annotations.categories = {
+      {kSampleCategory1, 90}, {kSampleCategory2, 84}};
   cluster.visits = {history_clusters::testing::CreateClusterVisit(
       visit, /*normalized_url=*/std::nullopt, 1.0)};
-
   HistoryClusterMetrics cluster_metrics = {.num_times_seen = 2,
                                            .num_times_used = 1};
+  HistoryClustersCategoryMetrics category_metrics(
+      {kSampleCategory1, kSampleCategory2}, 2,
+      {kSampleCategory2, kSampleCategory2}, 1);
   HistoryClustersModuleRankingSignals signals(
-      /*active_carts=*/{}, /*category_boostlist=*/{}, cluster, cluster_metrics);
+      /*active_carts=*/{}, /*category_boostlist=*/{}, cluster, cluster_metrics,
+      category_metrics);
   EXPECT_EQ(signals.num_times_seen_last_24h, 2u);
   EXPECT_EQ(signals.num_times_used_last_24h, 1u);
+  EXPECT_EQ(signals.num_associated_categories, 2u);
+  EXPECT_EQ(signals.belongs_to_most_seen_category, true);
+  EXPECT_EQ(signals.belongs_to_most_used_category, true);
+  EXPECT_EQ(signals.most_frequent_category_seen_count_last_24h, 2u);
+  EXPECT_EQ(signals.most_frequent_category_used_count_last_24h, 1u);
 
   // Verify UKM.
   ukm::TestAutoSetUkmRecorder test_ukm_recorder;
@@ -209,6 +234,28 @@
   test_ukm_recorder.ExpectEntryMetric(
       entry,
       ukm::builders::NewTabPage_HistoryClusters::kNumTimesUsedLast24hName, 1);
+  test_ukm_recorder.ExpectEntryMetric(
+      entry,
+      ukm::builders::NewTabPage_HistoryClusters::kNumAssociatedCategoriesName,
+      2);
+  test_ukm_recorder.ExpectEntryMetric(
+      entry,
+      ukm::builders::NewTabPage_HistoryClusters::kBelongsToMostSeenCategoryName,
+      true);
+  test_ukm_recorder.ExpectEntryMetric(
+      entry,
+      ukm::builders::NewTabPage_HistoryClusters::kBelongsToMostUsedCategoryName,
+      true);
+  test_ukm_recorder.ExpectEntryMetric(
+      entry,
+      ukm::builders::NewTabPage_HistoryClusters::
+          kMostFrequentSeenCategoryCountName,
+      2);
+  test_ukm_recorder.ExpectEntryMetric(
+      entry,
+      ukm::builders::NewTabPage_HistoryClusters::
+          kMostFrequentUsedCategoryCountName,
+      1);
 }
 
 }  // namespace
diff --git a/chrome/browser/new_tab_page/modules/test_support.h b/chrome/browser/new_tab_page/modules/test_support.h
index b6d6de8..49501fb 100644
--- a/chrome/browser/new_tab_page/modules/test_support.h
+++ b/chrome/browser/new_tab_page/modules/test_support.h
@@ -26,6 +26,15 @@
   MOCK_METHOD1(ClearCachedDataForContextID,
                void(history::ContextID context_id));
 
+  MOCK_CONST_METHOD5(
+      GetAnnotatedVisits,
+      base::CancelableTaskTracker::TaskId(
+          const history::QueryOptions& options,
+          bool compute_redirect_chain_start_properties,
+          bool get_unclustered_visits_only,
+          history::HistoryService::GetAnnotatedVisitsCallback callback,
+          base::CancelableTaskTracker* tracker));
+
   MOCK_METHOD3(HideVisits,
                base::CancelableTaskTracker::TaskId(
                    const std::vector<history::VisitID>& visit_ids,
diff --git a/chrome/browser/page_load_metrics/integration_tests/data/lcp-256x256.png b/chrome/browser/page_load_metrics/integration_tests/data/lcp-256x256.png
new file mode 100644
index 0000000..944f10f8
--- /dev/null
+++ b/chrome/browser/page_load_metrics/integration_tests/data/lcp-256x256.png
Binary files differ
diff --git a/chrome/browser/page_load_metrics/integration_tests/data/lcp-256x256.png.mock-http-headers b/chrome/browser/page_load_metrics/integration_tests/data/lcp-256x256.png.mock-http-headers
new file mode 100644
index 0000000..4534fc81
--- /dev/null
+++ b/chrome/browser/page_load_metrics/integration_tests/data/lcp-256x256.png.mock-http-headers
@@ -0,0 +1,3 @@
+HTTP/1.1 200 OK
+Content-Type: image/png
+Cache-Control: max-age=300
\ No newline at end of file
diff --git a/chrome/browser/page_load_metrics/integration_tests/data/lcp_breakdown_timings_subframe_page1.html b/chrome/browser/page_load_metrics/integration_tests/data/lcp_breakdown_timings_subframe_page1.html
new file mode 100644
index 0000000..173a095
--- /dev/null
+++ b/chrome/browser/page_load_metrics/integration_tests/data/lcp_breakdown_timings_subframe_page1.html
@@ -0,0 +1,4 @@
+<head>
+  <script src="resources/util.js"></script>
+</head>
+<img src="lcp-256x256.png">
\ No newline at end of file
diff --git a/chrome/browser/page_load_metrics/integration_tests/data/lcp_breakdown_timings_subframe_page2.html b/chrome/browser/page_load_metrics/integration_tests/data/lcp_breakdown_timings_subframe_page2.html
new file mode 100644
index 0000000..e181bba
--- /dev/null
+++ b/chrome/browser/page_load_metrics/integration_tests/data/lcp_breakdown_timings_subframe_page2.html
@@ -0,0 +1,5 @@
+<head>
+  <script src="resources/util.js"></script>
+</head>
+
+<img src="/images/lcp-100x50.png">
\ No newline at end of file
diff --git a/chrome/browser/page_load_metrics/integration_tests/data/lcp_breakdown_timings_with_subframe.html b/chrome/browser/page_load_metrics/integration_tests/data/lcp_breakdown_timings_with_subframe.html
new file mode 100644
index 0000000..b623deb
--- /dev/null
+++ b/chrome/browser/page_load_metrics/integration_tests/data/lcp_breakdown_timings_with_subframe.html
@@ -0,0 +1,28 @@
+<head>
+  <script src="resources/util.js"></script>
+</head>
+
+<img src="/images/lcp-16x16.png">
+
+<script>
+  const addSubframe = async (subframe_urls) => {
+    for (let i = 0; i < subframe_urls.length; i++) {
+      await new Promise(resolve => {
+        const iframe = document.createElement('iframe');
+        iframe.addEventListener('load', resolve);
+        iframe.src = subframe_urls[i];
+        document.body.appendChild(iframe);
+      });
+    }
+  }
+
+  const addSameSiteSubframe = async () => {
+    await addSubframe(["lcp_breakdown_timings_subframe_page1.html",
+      "lcp_breakdown_timings_subframe_page2.html"]);
+  }
+
+  const addCrossSiteSubframe = async () => {
+    await addSubframe([origin.replace("example.com", "b.com") + "/lcp_breakdown_timings_subframe_page1.html",
+      "lcp_breakdown_timings_subframe_page2.html"]);
+  }
+</script>
\ No newline at end of file
diff --git a/chrome/browser/page_load_metrics/integration_tests/data/resources/util.js b/chrome/browser/page_load_metrics/integration_tests/data/resources/util.js
index a5cb4ca..d2429b3 100644
--- a/chrome/browser/page_load_metrics/integration_tests/data/resources/util.js
+++ b/chrome/browser/page_load_metrics/integration_tests/data/resources/util.js
@@ -6,15 +6,15 @@
 // frames has occurred.
 async function waitUntilAfterNextLayout() {
   let frameCount = 2;
-    return new Promise(resolve => {
-        const handleFrame = () => {
-        if (--frameCount <= 0)
-            resolve();
-        else
-            requestAnimationFrame(handleFrame);
-        };
+  return new Promise(resolve => {
+    const handleFrame = () => {
+      if (--frameCount <= 0)
+        resolve();
+      else
         requestAnimationFrame(handleFrame);
-    });
+    };
+    requestAnimationFrame(handleFrame);
+  });
 };
 
 async function observeUntilNumEntries(n, opts) {
@@ -59,21 +59,20 @@
   })
 }
 
-const getRequestStart = async (name) => {
-  let resource_timings =
-    performance.getEntriesByType('resource').filter(e => e.name.includes(name));
-  return resource_timings[0].requestStart;
-}
 
-const getResponseEnd = async (name) => {
-  let resource_timings =
-    performance.getEntriesByType('resource').filter(e => e.name.includes(name));
-  return resource_timings[0].responseEnd;
-}
-const getStartTime = async (name) => {
-  let resource_timings =
-    performance.getEntriesByType('resource').filter(e => e.name.includes(name));
-  return resource_timings[0].startTime;
+const getRequestStart = (name) =>
+  getResourceTimingEntry(name).requestStart;
+
+const getResponseEnd = (name) =>
+  getResourceTimingEntry(name).responseEnd;
+
+const getStartTime = (name) =>
+  getResourceTimingEntry(name).startTime;
+
+
+const getResourceTimingEntry = (name) => {
+  return performance.getEntriesByType('resource').filter(
+    e => e.name.includes(name))[0];
 }
 
 const addImageWithUrl = async (url) => {
diff --git a/chrome/browser/page_load_metrics/integration_tests/largest_contentful_paint_browsertest.cc b/chrome/browser/page_load_metrics/integration_tests/largest_contentful_paint_browsertest.cc
index 98e70f7..5916bbce 100644
--- a/chrome/browser/page_load_metrics/integration_tests/largest_contentful_paint_browsertest.cc
+++ b/chrome/browser/page_load_metrics/integration_tests/largest_contentful_paint_browsertest.cc
@@ -986,6 +986,7 @@
  protected:
   void RunTest(std::string test_url,
                std::string resource,
+               std::optional<uint8_t> subframe_index,
                std::string script = "") {
     Start();
     auto waiter0 =
@@ -1005,26 +1006,28 @@
 
     waiter0->Wait();
 
+    auto* rfh =
+        !subframe_index.has_value()
+            ? web_contents()->GetPrimaryMainFrame()
+            : content::ChildFrameAt(web_contents()->GetPrimaryMainFrame(),
+                                    subframe_index.value());
+
+    timeline_lcp_list_[0] =
+        EvalJs(rfh, content::JsReplace("getLcp($1)", resource)).ExtractDouble();
+
     // Retrieve resource timing timings of initial load for validation.
     request_start_list_[0] =
-        EvalJs(web_contents()->GetPrimaryMainFrame(),
-               content::JsReplace("getRequestStart($1)", resource))
+        EvalJs(rfh, content::JsReplace("getRequestStart($1)", resource))
             .ExtractDouble();
 
     response_end_list_[0] =
-        EvalJs(web_contents()->GetPrimaryMainFrame(),
-               content::JsReplace("getResponseEnd($1)", resource))
+        EvalJs(rfh, content::JsReplace("getResponseEnd($1)", resource))
             .ExtractDouble();
 
     start_time_list_[0] =
-        EvalJs(web_contents()->GetPrimaryMainFrame(),
-               content::JsReplace("getStartTime($1)", resource))
+        EvalJs(rfh, content::JsReplace("getStartTime($1)", resource))
             .ExtractDouble();
 
-    timeline_lcp_list_[0] = EvalJs(web_contents()->GetPrimaryMainFrame(),
-                                   content::JsReplace("getLcp($1)", resource))
-                                .ExtractDouble();
-
     auto waiter1 =
         std::make_unique<page_load_metrics::PageLoadMetricsTestWaiter>(
             web_contents());
@@ -1044,30 +1047,30 @@
 
     waiter1->Wait();
 
+    rfh = !subframe_index.has_value()
+              ? web_contents()->GetPrimaryMainFrame()
+              : content::ChildFrameAt(web_contents()->GetPrimaryMainFrame(),
+                                      subframe_index.value());
+    timeline_lcp_list_[1] =
+        EvalJs(rfh, content::JsReplace("getLcp($1)", resource)).ExtractDouble();
+
     // Retrieve resource timing timings after refresh for validation.
     request_start_list_[1] =
-        EvalJs(web_contents()->GetPrimaryMainFrame(),
-               content::JsReplace("getRequestStart($1)", resource))
+        EvalJs(rfh, content::JsReplace("getRequestStart($1)", resource))
             .ExtractDouble();
 
     response_end_list_[1] =
-        EvalJs(web_contents()->GetPrimaryMainFrame(),
-               content::JsReplace("getResponseEnd($1)", resource))
+        EvalJs(rfh, content::JsReplace("getResponseEnd($1)", resource))
             .ExtractDouble();
 
     start_time_list_[1] =
-        EvalJs(web_contents()->GetPrimaryMainFrame(),
-               content::JsReplace("getStartTime($1)", resource))
+        EvalJs(rfh, content::JsReplace("getStartTime($1)", resource))
             .ExtractDouble();
 
-    timeline_lcp_list_[1] = EvalJs(web_contents()->GetPrimaryMainFrame(),
-                                   content::JsReplace("getLcp($1)", resource))
-                                .ExtractDouble();
-
     ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank")));
   }
 
-  void Validate() {
+  void Validate(bool is_lcp_from_subframe = false) {
     std::vector<double> ttfb_list = GetPageLoadMetricsAsList(
         PageLoad::kMainFrameResource_NavigationStartToReceiveHeadersStartName);
 
@@ -1100,14 +1103,23 @@
 
     EXPECT_LT(load_end_list_[0], lcp_list_[0]);
 
-    // LCP breadown timings should be the same as resource timings.
-    EXPECT_NEAR(discovery_time_list_[0], start_time_list_[0], epsilon_);
+    // LCP breadown timings should be the same as resource timings. If the LCP
+    // element is from a subframe, the navigation start offset should be
+    // accounted for.
+    auto navigation_start_offset =
+        is_lcp_from_subframe ? discovery_time_list_[0] - start_time_list_[0]
+                             : 0;
+    EXPECT_NEAR(discovery_time_list_[0], start_time_list_[0],
+                epsilon_ + navigation_start_offset);
 
-    EXPECT_NEAR(load_start_list_[0], request_start_list_[0], epsilon_);
+    EXPECT_NEAR(load_start_list_[0], request_start_list_[0],
+                epsilon_ + navigation_start_offset);
 
-    EXPECT_NEAR(load_end_list_[0], response_end_list_[0], epsilon_);
+    EXPECT_NEAR(load_end_list_[0], response_end_list_[0],
+                epsilon_ + navigation_start_offset);
 
-    EXPECT_NEAR(lcp_list_[0], timeline_lcp_list_[0], epsilon_);
+    EXPECT_NEAR(lcp_list_[0], timeline_lcp_list_[0],
+                epsilon_ + navigation_start_offset);
 
     // Validate timings after refresh. LCP breakdown timings could be equal if
     // the image is loaded from memory. Hence we use EXPECT_LE instead of
@@ -1121,13 +1133,21 @@
     EXPECT_LE(load_end_list_[1], lcp_list_[1]);
 
     // LCP breadown timings should be the same as resource timings.
-    EXPECT_NEAR(discovery_time_list_[1], start_time_list_[1], epsilon_);
+    navigation_start_offset =
+        is_lcp_from_subframe ? discovery_time_list_[1] - start_time_list_[1]
+                             : 0;
 
-    EXPECT_NEAR(load_start_list_[1], request_start_list_[1], epsilon_);
+    EXPECT_NEAR(discovery_time_list_[1], start_time_list_[1],
+                epsilon_ + navigation_start_offset);
 
-    EXPECT_NEAR(load_end_list_[1], response_end_list_[1], epsilon_);
+    EXPECT_NEAR(load_start_list_[1], request_start_list_[1],
+                epsilon_ + navigation_start_offset);
 
-    EXPECT_NEAR(lcp_list_[1], timeline_lcp_list_[1], epsilon_);
+    EXPECT_NEAR(load_end_list_[1], response_end_list_[1],
+                epsilon_ + navigation_start_offset);
+
+    EXPECT_NEAR(lcp_list_[1], timeline_lcp_list_[1],
+                epsilon_ + navigation_start_offset);
   }
 
   void ValidateForMemCacheLoadedImages() {
@@ -1148,6 +1168,26 @@
   double epsilon_ = 1.5;
 };
 
+IN_PROC_BROWSER_TEST_F(LcpBreakdownTimingsTest, Subframe) {
+  std::string url = "/lcp_breakdown_timings_with_subframe.html";
+  auto* resource_name = "lcp-256x256.png";
+  RunTest(url, resource_name, 0, "addSameSiteSubframe()");
+  Validate(true);
+  ValidateForMemCacheLoadedImages();
+}
+
+// TODO(323888356): There is an issue in the LCP size calculation in a cross
+// site subframe. This test should be enabled after that issue is resolved.
+IN_PROC_BROWSER_TEST_F(LcpBreakdownTimingsTest, DISABLED_CrossSiteSubframe) {
+  std::string url = "/lcp_breakdown_timings_with_subframe.html";
+  auto* resource_name = "lcp-256x256.png";
+  RunTest(url, resource_name, 0, "addCrossSiteSubframe()");
+  Validate(true);
+  // Image that is in cross origin subframe wouldn't be loaded from cache, so
+  // we don't verify that the breakdown timings are all set to discovery time
+  // after refresh.
+}
+
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
 #define MAYBE_MemCacheServedImage DISABLED_MemCacheServedImage
 #else
@@ -1157,7 +1197,7 @@
   std::string test_url = "/lcp_breakdown_timings_memcache_served_images.html";
   std::string resource = "green.png";
 
-  RunTest(test_url, resource);
+  RunTest(test_url, resource, std::nullopt);
   Validate();
 
   // Since after refresh, the image is loaded from mem cache, the discovery_time
@@ -1173,7 +1213,7 @@
 IN_PROC_BROWSER_TEST_F(LcpBreakdownTimingsTest, MAYBE_PreloadedImage) {
   std::string test_url = "/lcp_breakdown_timings_preloaded_images.html";
   std::string resource = "/images/lcp-16x16.png";
-  RunTest(test_url, resource,
+  RunTest(test_url, resource, std::nullopt,
           content::JsReplace("addImageWithUrl($1)", resource));
   Validate();
 }
@@ -1187,7 +1227,7 @@
   std::string test_url =
       "/lcp_breakdown_timings_preloaded_cacheable_images.html";
   std::string resource = "green.png";
-  RunTest(test_url, resource,
+  RunTest(test_url, resource, std::nullopt,
           content::JsReplace("addImageWithUrl($1)", resource));
   Validate();
   ValidateForMemCacheLoadedImages();
@@ -1202,7 +1242,7 @@
   std::string test_url =
       "/lcp_breakdown_timings_native_lazy_loading_images.html";
   std::string resource = "lcp-16x16.png";
-  RunTest(test_url, resource);
+  RunTest(test_url, resource, std::nullopt);
   Validate();
 }
 
@@ -1212,7 +1252,7 @@
   std::string test_url =
       "/lcp_breakdown_timings_manual_lazy_loading_images.html";
   std::string resource = "lcp-16x16.png";
-  RunTest(test_url, resource,
+  RunTest(test_url, resource, std::nullopt,
           content::JsReplace("(async ()=>{await scrollToLoadImage($1);})()",
                              resource));
   Validate();
@@ -1226,7 +1266,7 @@
 IN_PROC_BROWSER_TEST_F(LcpBreakdownTimingsTest, MAYBE_CssBackgroundImage) {
   std::string test_url = "/lcp_breakdown_timings_css_background_images.html";
   std::string resource = "lcp-256x256.png";
-  RunTest(test_url, resource);
+  RunTest(test_url, resource, std::nullopt);
   Validate();
 }
 
@@ -1239,7 +1279,8 @@
 IN_PROC_BROWSER_TEST_F(LcpBreakdownTimingsTest, MAYBE_WrittenAsInnerHtmlImage) {
   std::string test_url = "/lcp_breakdown_timings_written_as_html_images.html";
   std::string resource = "/images/lcp-256x256.png";
-  RunTest(test_url, resource, "AddImageByScript(WriteToDomAsInnerHtml);");
+  RunTest(test_url, resource, std::nullopt,
+          "AddImageByScript(WriteToDomAsInnerHtml);");
   Validate();
 }
 
@@ -1252,7 +1293,8 @@
 IN_PROC_BROWSER_TEST_F(LcpBreakdownTimingsTest, MAYBE_WrittenAsOuterHtmlImage) {
   std::string test_url = "/lcp_breakdown_timings_written_as_html_images.html";
   std::string resource = "/images/lcp-256x256.png";
-  RunTest(test_url, resource, "AddImageByScript(WriteToDomAsOuterHtml);");
+  RunTest(test_url, resource, std::nullopt,
+          "AddImageByScript(WriteToDomAsOuterHtml);");
   Validate();
 }
 
@@ -1264,7 +1306,7 @@
 IN_PROC_BROWSER_TEST_F(LcpBreakdownTimingsTest, MAYBE_DocumentWrittenImage) {
   std::string test_url = "/lcp_breakdown_timings_document_written_images.html";
   std::string resource = "/images/lcp-256x256.png";
-  RunTest(test_url, resource);
+  RunTest(test_url, resource, std::nullopt);
   Validate();
 }
 
@@ -1277,7 +1319,7 @@
   std::string test_url = "/lcp_breakdown_timings_srcset_images.html";
   std::string resource = "lcp-256x256.png";
 
-  RunTest(test_url, resource);
+  RunTest(test_url, resource, std::nullopt);
   Validate();
 }
 
@@ -1290,7 +1332,7 @@
   std::string test_url = "/lcp_breakdown_timings_empty.html";
   std::string resource = "/images/lcp-256x256.png";
 
-  RunTest(test_url, resource,
+  RunTest(test_url, resource, std::nullopt,
           content::JsReplace("addImageWithUrl($1)", resource));
   Validate();
 }
diff --git a/chrome/browser/page_load_metrics/observers/third_party_cookie_deprecation_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/third_party_cookie_deprecation_metrics_observer_browsertest.cc
index 03ad787d..461b0621 100644
--- a/chrome/browser/page_load_metrics/observers/third_party_cookie_deprecation_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/third_party_cookie_deprecation_metrics_observer_browsertest.cc
@@ -1323,3 +1323,75 @@
   histogram_tester.ExpectTotalCount(
       "PageLoad.Clients.TPCD.TPCAccess.CookieReadStatus", 0);
 }
+
+class ThirdPartyCookieDeprecationObserverTriggerBrowserTest
+    : public ThirdPartyCookieDeprecationObserverBaseBrowserTest {
+ public:
+  ThirdPartyCookieDeprecationObserverTriggerBrowserTest() = default;
+
+  ThirdPartyCookieDeprecationObserverTriggerBrowserTest(
+      const ThirdPartyCookieDeprecationObserverTriggerBrowserTest&) = delete;
+  ThirdPartyCookieDeprecationObserverTriggerBrowserTest& operator=(
+      const ThirdPartyCookieDeprecationObserverTriggerBrowserTest&) = delete;
+
+  ~ThirdPartyCookieDeprecationObserverTriggerBrowserTest() override = default;
+
+  void SetUp() override {
+    scoped_feature_list_.InitWithFeaturesAndParameters(
+        {}, {content_settings::features::kTrackingProtection3pcd});
+    subresource_filter::SubresourceFilterBrowserTest::SetUp();
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(ThirdPartyCookieDeprecationObserverTriggerBrowserTest,
+                       ThirdPartyCookiesSingleWrite) {
+  // Setup tracking protection onboard to block 3PC.
+  SetUpTrackingProtectionOnboard();
+  content::CookieChangeObserver observer(web_contents(), 1);
+  NavigateToPageWithFrame(kHostA);
+  // 3p cookie write
+  NavigateFrameTo(kHostB, "/set-cookie?thirdparty=1;SameSite=None;Secure");
+  observer.Wait();
+  EXPECT_EQ(0, observer.num_read_seen());
+  EXPECT_EQ(1, observer.num_write_seen());
+}
+
+IN_PROC_BROWSER_TEST_F(ThirdPartyCookieDeprecationObserverTriggerBrowserTest,
+                       ThirdPartyCookiesSingleRead) {
+  // Read|Write cookie before tracking protection onboard.
+  content::CookieChangeObserver observer1(web_contents(), 2);
+  NavigateToPageWithFrame(kHostA);
+  // 3p cookie write
+  NavigateFrameTo(kHostB, "/set-cookie?thirdparty=1;SameSite=None;Secure");
+  // 3p cookie read
+  NavigateFrameTo(kHostB, "/");
+  observer1.Wait();
+  EXPECT_EQ(1, observer1.num_read_seen());
+  EXPECT_EQ(1, observer1.num_write_seen());
+
+  // Setup tracking protection onboard to block 3PC.
+  SetUpTrackingProtectionOnboard();
+  content::CookieChangeObserver observer2(web_contents(), 1);
+  // 3p cookie read
+  NavigateFrameTo(kHostB, "/");
+  observer2.Wait();
+  EXPECT_EQ(1, observer2.num_read_seen());
+  EXPECT_EQ(0, observer2.num_write_seen());
+}
+
+IN_PROC_BROWSER_TEST_F(ThirdPartyCookieDeprecationObserverTriggerBrowserTest,
+                       ThirdPartyCookiesBothWriteRead) {
+  // Setup tracking protection onboard to block 3PC.
+  SetUpTrackingProtectionOnboard();
+  // Only 3p cookie write is triggered because the 3p cookie write is blocked
+  // and no cookie to read.
+  content::CookieChangeObserver observer(web_contents(), 1);
+  NavigateToPageWithFrame(kHostA);
+  // 3p cookie write
+  NavigateFrameTo(kHostB, "/set-cookie?thirdparty=1;SameSite=None;Secure");
+  // 3p cookie read
+  NavigateFrameTo(kHostB, "/");
+  observer.Wait();
+  EXPECT_EQ(0, observer.num_read_seen());
+  EXPECT_EQ(1, observer.num_write_seen());
+}
diff --git a/chrome/browser/pdf/pdf_extension_accessibility_test.cc b/chrome/browser/pdf/pdf_extension_accessibility_test.cc
index 989cc23..9e8ea68a 100644
--- a/chrome/browser/pdf/pdf_extension_accessibility_test.cc
+++ b/chrome/browser/pdf/pdf_extension_accessibility_test.cc
@@ -67,11 +67,9 @@
 #include "chrome/browser/renderer_context_menu/pdf_ocr_menu_observer.h"
 #endif  // BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
 
-// TODO(crbug.com/1516559): Add a dummy library that is built with Chrome for
-// memory sanitizer tests.
-// TODO(crbug.com/323792320): Times out flakily on UBSan bots.
-#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC)) && \
-    !defined(MEMORY_SANITIZER) && !defined(UNDEFINED_SANITIZER)
+// TODO(crbug.com/41489544): Add a fake library that is built with Chrome for
+// sanitizer tests.
+#if BUILDFLAG(ENABLE_SCREEN_AI_BROWSERTESTS) && !BUILDFLAG(USE_FAKE_SCREEN_AI)
 #define PDF_OCR_INTEGRATION_TEST_ENABLED
 #endif
 
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 1fdcd6e..ac19443 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -2078,6 +2078,11 @@
     optimization_guide::model_execution::prefs::kWallpaperSearchEnterprisePolicyAllowed,
     base::Value::Type::INTEGER},
 #endif
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
+  { key::kChromeForTestingAllowed,
+    prefs::kChromeForTestingAllowed,
+    base::Value::Type::BOOLEAN },
+#endif
 
 #if BUILDFLAG(CHROME_CERTIFICATE_POLICIES_SUPPORTED)
   { key::kCACertificates,
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 00b0837..6efdc4bd 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -1725,6 +1725,10 @@
   registry->RegisterBooleanPref(prefs::kOopPrintDriversAllowedByPolicy, true);
 #endif
 
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
+  registry->RegisterBooleanPref(prefs::kChromeForTestingAllowed, true);
+#endif
+
   // This is intentionally last.
   RegisterLocalStatePrefsForMigration(registry);
 }
diff --git a/chrome/browser/profiles/profile_keyed_service_browsertest.cc b/chrome/browser/profiles/profile_keyed_service_browsertest.cc
index 01987624..411b93b1 100644
--- a/chrome/browser/profiles/profile_keyed_service_browsertest.cc
+++ b/chrome/browser/profiles/profile_keyed_service_browsertest.cc
@@ -21,6 +21,7 @@
 #include "components/keyed_service/core/keyed_service_base_factory.h"
 #include "components/omnibox/common/omnibox_features.h"
 #include "components/optimization_guide/machine_learning_tflite_buildflags.h"
+#include "components/search/ntp_features.h"
 #include "components/services/screen_ai/buildflags/buildflags.h"
 #include "components/supervised_user/core/common/buildflags.h"
 #include "content/public/common/content_features.h"
@@ -191,6 +192,9 @@
           omnibox::kOnDeviceTailModel,
           omnibox::kOnDeviceHeadProviderNonIncognito,
 #endif  // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
+#if !BUILDFLAG(IS_ANDROID)
+          ntp_features::kCustomizeChromeWallpaperSearch
+#endif  // !BUILDFLAG(IS_ANDROID)
         },
         {});
     // clang-format on
@@ -550,6 +554,7 @@
     "UserPolicySigninService",
 #if !BUILDFLAG(IS_ANDROID)
     "VisualQuerySuggestionsService",
+    "WallpaperSearchService",
 #endif  // !BUILDFLAG(IS_ANDROID)
     "WarningBadgeService",
     "WarningService",
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 a5a3e47..b383aa15 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
@@ -25,7 +25,7 @@
             label="$i18n{appNotificationsTitle}"
             on-click="onClickAppNotifications_"
             role-description="$i18n{subpageArrowRoleDescription}"
-            sub-label= "[[getAppListCountDescription_(
+            sub-label="[[getAppListCountDescription_(
                 appsWithNotifications_.*, isDndEnabled_)]]">
         </cr-link-row>
       </template>
diff --git a/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_change_device_name_dialog.ts b/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_change_device_name_dialog.ts
index 20bef67..7363b64 100644
--- a/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_change_device_name_dialog.ts
+++ b/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_change_device_name_dialog.ts
@@ -11,7 +11,7 @@
 import 'chrome://resources/ash/common/cr_elements/cr_input/cr_input.js';
 import 'chrome://resources/ash/common/cr_elements/cr_dialog/cr_dialog.js';
 
-import {getDeviceName} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
+import {getDeviceNameUnsafe} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
 import {getBluetoothConfig} from 'chrome://resources/ash/common/bluetooth/cros_bluetooth_config.js';
 import {CrDialogElement} from 'chrome://resources/ash/common/cr_elements/cr_dialog/cr_dialog.js';
 import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
@@ -52,6 +52,11 @@
         value: MAX_INPUT_LENGTH,
       },
 
+      /**
+       * WARNING: This string may contain malicious HTML and should not be used
+       * for Polymer bindings in CSS code. For additional information see
+       * b/298724102.
+       */
       deviceName_: {
         type: String,
         value: '',
@@ -71,7 +76,7 @@
   private isInputInvalid_: boolean;
 
   private onDeviceChanged_(): void {
-    this.deviceName_ = getDeviceName(this.device);
+    this.deviceName_ = getDeviceNameUnsafe(this.device);
   }
 
   private onCancelClick_(): void {
@@ -118,7 +123,7 @@
   }
 
   private isDoneDisabled_(): boolean {
-    if (this.deviceName_ === getDeviceName(this.device)) {
+    if (this.deviceName_ === getDeviceNameUnsafe(this.device)) {
       return true;
     }
 
@@ -128,6 +133,10 @@
 
     return false;
   }
+
+  getNameForTest(): string|null {
+    return getDeviceNameUnsafe(this.device);
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_device_detail_subpage.html b/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_device_detail_subpage.html
index 6d6be9d..e6c4d823 100644
--- a/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_device_detail_subpage.html
+++ b/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_device_detail_subpage.html
@@ -104,7 +104,7 @@
         aria-hidden="true">
       $i18n{bluetoothDeviceDetailName}
       <div class="secondary" id="bluetoothDeviceNameLabel">
-        [[getDeviceName_(device_.*)]]
+        [[getDeviceNameUnsafe_(device_.*)]]
       </div>
     </div>
     <cr-button id="changeNameBtn"
diff --git a/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_device_detail_subpage.ts b/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_device_detail_subpage.ts
index 6d097af..2b7ff9b 100644
--- a/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_device_detail_subpage.ts
+++ b/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_device_detail_subpage.ts
@@ -17,7 +17,7 @@
 
 import {BluetoothUiSurface, recordBluetoothUiSurfaceMetrics} from 'chrome://resources/ash/common/bluetooth/bluetooth_metrics_utils.js';
 import {BatteryType} from 'chrome://resources/ash/common/bluetooth/bluetooth_types.js';
-import {getBatteryPercentage, getDeviceName, hasAnyDetailedBatteryInfo, hasDefaultImage, hasTrueWirelessImages} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
+import {getBatteryPercentage, getDeviceNameUnsafe, hasAnyDetailedBatteryInfo, hasDefaultImage, hasTrueWirelessImages} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
 import {getBluetoothConfig} from 'chrome://resources/ash/common/bluetooth/cros_bluetooth_config.js';
 import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
 import {WebUiListenerMixin} from 'chrome://resources/ash/common/cr_elements/web_ui_listener_mixin.js';
@@ -211,11 +211,8 @@
         this.i18n('bluetoothDeviceDetailDisconnected');
   }
 
-  private getDeviceName_(): string {
-    if (!this.device_) {
-      return '';
-    }
-    return getDeviceName(this.device_);
+  private getDeviceNameUnsafe_(): string {
+    return getDeviceNameUnsafe(this.device_);
   }
 
   private shouldShowConnectDisconnectBtn_(): boolean {
@@ -235,7 +232,7 @@
       return;
     }
     (this.parentNode as OsSettingsSubpageElement).pageTitle =
-        getDeviceName(this.device_);
+        getDeviceNameUnsafe(this.device_);
 
     // Special case a where user is still on detail page and has
     // tried to connect to device but failed. The current |pageState_|
@@ -299,9 +296,9 @@
       return '';
     }
 
-    return this.i18n(
+    return loadTimeData.getStringF(
         'bluetoothDeviceDetailChangeDeviceNameBtnA11yLabel',
-        this.getDeviceName_());
+        getDeviceNameUnsafe(this.device_));
   }
 
   private getMultipleBatteryInfoA11yLabel_(): string {
@@ -363,20 +360,22 @@
 
     switch (this.pageState_) {
       case PageState.CONNECTING:
-        return this.i18n(
-            'bluetoothDeviceDetailConnectingA11yLabel', this.getDeviceName_());
+        return loadTimeData.getStringF(
+            'bluetoothDeviceDetailConnectingA11yLabel',
+            getDeviceNameUnsafe(this.device_));
       case PageState.CONNECTED:
-        return this.i18n(
-            'bluetoothDeviceDetailConnectedA11yLabel', this.getDeviceName_());
+        return loadTimeData.getStringF(
+            'bluetoothDeviceDetailConnectedA11yLabel',
+            getDeviceNameUnsafe(this.device_));
       case PageState.CONNECTION_FAILED:
-        return this.i18n(
+        return loadTimeData.getStringF(
             'bluetoothDeviceDetailConnectionFailureA11yLabel',
-            this.getDeviceName_());
+            getDeviceNameUnsafe(this.device_));
       case PageState.DISCONNECTED:
       case PageState.DISCONNECTING:
-        return this.i18n(
+        return loadTimeData.getStringF(
             'bluetoothDeviceDetailDisconnectedA11yLabel',
-            this.getDeviceName_());
+            getDeviceNameUnsafe(this.device_));
       default:
         assertNotReached();
     }
@@ -525,8 +524,9 @@
   }
 
   private getForgetA11yLabel_(): string {
-    return this.i18n(
-        'bluetoothDeviceDetailForgetA11yLabel', this.getDeviceName_());
+    return loadTimeData.getStringF(
+        'bluetoothDeviceDetailForgetA11yLabel',
+        getDeviceNameUnsafe(this.device_));
   }
 
   private onForgetButtonClicked_(): void {
diff --git a/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_forget_device_dialog.ts b/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_forget_device_dialog.ts
index a977105..0736005a12 100644
--- a/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_forget_device_dialog.ts
+++ b/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_forget_device_dialog.ts
@@ -10,7 +10,7 @@
 import 'chrome://resources/ash/common/cr_elements/cr_input/cr_input.js';
 import 'chrome://resources/ash/common/cr_elements/cr_dialog/cr_dialog.js';
 
-import {getDeviceName} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
+import {getDeviceNameUnsafe} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
 import {CrDialogElement} from 'chrome://resources/ash/common/cr_elements/cr_dialog/cr_dialog.js';
 import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
@@ -45,15 +45,11 @@
   private device_: PairedBluetoothDeviceProperties;
 
   private getForgetDeviceDialogBodyText_(): string {
-    return this.i18n(
-        'bluetoothDevicesDialogLabel', this.getDeviceName_(),
+    return loadTimeData.getStringF(
+        'bluetoothDevicesDialogLabel', getDeviceNameUnsafe(this.device_),
         loadTimeData.getString('primaryUserEmail'));
   }
 
-  private getDeviceName_(): string {
-    return getDeviceName(this.device_);
-  }
-
   private onForgetClick_(event: Event): void {
     const fireEvent = new CustomEvent(
         'forget-bluetooth-device', {bubbles: true, composed: true});
diff --git a/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_summary.ts b/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_summary.ts
index bfaa093e..3523bbac 100644
--- a/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_summary.ts
+++ b/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_summary.ts
@@ -12,7 +12,7 @@
 import 'chrome://resources/ash/common/cr_elements/cr_icon_button/cr_icon_button.js';
 import 'chrome://resources/ash/common/cr_elements/icons.html.js';
 
-import {getDeviceName} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
+import {getDeviceNameUnsafe} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
 import {getBluetoothConfig} from 'chrome://resources/ash/common/bluetooth/cros_bluetooth_config.js';
 import {getInstance as getAnnouncerInstance} from 'chrome://resources/ash/common/cr_elements/cr_a11y_announcer/cr_a11y_announcer.js';
 import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
@@ -175,31 +175,32 @@
     }
 
     const isA11yLabel = labelType === LabelType.A11Y;
-    const firstConnectedDeviceName = getDeviceName(connectedDevices[0]);
+    const firstConnectedDeviceName = getDeviceNameUnsafe(connectedDevices[0]);
 
     if (connectedDevices.length === 1) {
-      return isA11yLabel ? this.i18n(
+      return isA11yLabel ? loadTimeData.getStringF(
                                'bluetoothSummaryPageConnectedA11yOneDevice',
                                firstConnectedDeviceName) :
                            firstConnectedDeviceName;
     }
 
     if (connectedDevices.length === 2) {
-      const secondConnectedDeviceName = getDeviceName(connectedDevices[1]);
+      const secondConnectedDeviceName =
+          getDeviceNameUnsafe(connectedDevices[1]);
       return isA11yLabel ?
-          this.i18n(
+          loadTimeData.getStringF(
               'bluetoothSummaryPageConnectedA11yTwoDevices',
               firstConnectedDeviceName, secondConnectedDeviceName) :
-          this.i18n(
+          loadTimeData.getStringF(
               'bluetoothSummaryPageTwoDevicesDescription',
               firstConnectedDeviceName, secondConnectedDeviceName);
     }
 
     return isA11yLabel ?
-        this.i18n(
+        loadTimeData.getStringF(
             'bluetoothSummaryPageConnectedA11yTwoOrMoreDevices',
             firstConnectedDeviceName, connectedDevices.length - 1) :
-        this.i18n(
+        loadTimeData.getStringF(
             'bluetoothSummaryPageTwoOrMoreDevicesDescription',
             firstConnectedDeviceName, connectedDevices.length - 1);
   }
diff --git a/chrome/browser/resources/ash/settings/os_bluetooth_page/os_paired_bluetooth_list_item.html b/chrome/browser/resources/ash/settings/os_bluetooth_page/os_paired_bluetooth_list_item.html
index e66405f5..a191d9ff 100644
--- a/chrome/browser/resources/ash/settings/os_bluetooth_page/os_paired_bluetooth_list_item.html
+++ b/chrome/browser/resources/ash/settings/os_bluetooth_page/os_paired_bluetooth_list_item.html
@@ -14,7 +14,7 @@
       on-click="onSelected_">
     <bluetooth-icon device="[[device.deviceProperties]]"></bluetooth-icon>
     <div class="middle" aria-hidden="true">
-      <div id="deviceName">[[getDeviceName_(device)]]</div>
+      <div id="deviceName">[[getDeviceNameUnsafe_(device)]]</div>
       <template is="dom-if"
           if="[[shouldShowBatteryInfo_(device)]]" restamp>
         <bluetooth-device-battery-info
diff --git a/chrome/browser/resources/ash/settings/os_bluetooth_page/os_paired_bluetooth_list_item.ts b/chrome/browser/resources/ash/settings/os_bluetooth_page/os_paired_bluetooth_list_item.ts
index a8a9a2f..e64f16fe 100644
--- a/chrome/browser/resources/ash/settings/os_bluetooth_page/os_paired_bluetooth_list_item.ts
+++ b/chrome/browser/resources/ash/settings/os_bluetooth_page/os_paired_bluetooth_list_item.ts
@@ -16,10 +16,11 @@
 import 'chrome://resources/ash/common/bluetooth/bluetooth_device_battery_info.js';
 
 import {BatteryType} from 'chrome://resources/ash/common/bluetooth/bluetooth_types.js';
-import {getBatteryPercentage, getDeviceName, hasAnyDetailedBatteryInfo} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
+import {getBatteryPercentage, getDeviceNameUnsafe, hasAnyDetailedBatteryInfo} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
 import {FocusRowMixin} from 'chrome://resources/ash/common/cr_elements/focus_row_mixin.js';
 import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
 import {assert, assertNotReached} from 'chrome://resources/js/assert.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {DeviceConnectionState, DeviceType, PairedBluetoothDeviceProperties} from 'chrome://resources/mojo/chromeos/ash/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom-webui.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
@@ -101,8 +102,9 @@
     Router.getInstance().navigateTo(routes.BLUETOOTH_DEVICE_DETAIL, params);
   }
 
-  private getDeviceName_(device: PairedBluetoothDeviceProperties): string {
-    return getDeviceName(device);
+  private getDeviceNameUnsafe_(device: PairedBluetoothDeviceProperties):
+      string {
+    return getDeviceNameUnsafe(device);
   }
 
   private shouldShowBatteryInfo_(device: PairedBluetoothDeviceProperties):
@@ -159,9 +161,9 @@
   private getAriaLabel_(device: PairedBluetoothDeviceProperties): string {
     // Start with the base information of the device name and location within
     // the list of devices with the same connection state.
-    let a11yLabel = this.i18n(
+    let a11yLabel = loadTimeData.getStringF(
         'bluetoothA11yDeviceName', this.itemIndex + 1, this.listSize,
-        this.getDeviceName_(device));
+        this.getDeviceNameUnsafe_(device));
 
     // Include the connection status.
     a11yLabel +=
diff --git a/chrome/browser/resources/ash/settings/os_bluetooth_page/os_saved_devices_list_item.html b/chrome/browser/resources/ash/settings/os_bluetooth_page/os_saved_devices_list_item.html
index 0b0ee475..2c82489 100644
--- a/chrome/browser/resources/ash/settings/os_bluetooth_page/os_saved_devices_list_item.html
+++ b/chrome/browser/resources/ash/settings/os_bluetooth_page/os_saved_devices_list_item.html
@@ -28,7 +28,7 @@
       </template>
     </div>
     <div class="middle" aria-hidden="true">
-      <div id="deviceName">[[getDeviceName_(device)]]</div>
+      <div id="deviceName">[[getDeviceNameUnsafe_(device)]]</div>
     </div>
     <div>
       <cr-icon-button class="icon-more-vert"
diff --git a/chrome/browser/resources/ash/settings/os_bluetooth_page/os_saved_devices_list_item.ts b/chrome/browser/resources/ash/settings/os_bluetooth_page/os_saved_devices_list_item.ts
index 154b0c2..f8ac08f 100644
--- a/chrome/browser/resources/ash/settings/os_bluetooth_page/os_saved_devices_list_item.ts
+++ b/chrome/browser/resources/ash/settings/os_bluetooth_page/os_saved_devices_list_item.ts
@@ -16,8 +16,8 @@
 import {FastPairSavedDevicesUiEvent, recordSavedDevicesUiEventMetrics} from 'chrome://resources/ash/common/bluetooth/bluetooth_metrics_utils.js';
 import {CrActionMenuElement} from 'chrome://resources/ash/common/cr_elements/cr_action_menu/cr_action_menu.js';
 import {FocusRowMixin} from 'chrome://resources/ash/common/cr_elements/focus_row_mixin.js';
-import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
 import {WebUiListenerMixin} from 'chrome://resources/ash/common/cr_elements/web_ui_listener_mixin.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getTemplate} from './os_saved_devices_list_item.html.js';
@@ -30,7 +30,7 @@
 }
 
 const SettingsSavedDevicesListItemElementBase =
-    FocusRowMixin(WebUiListenerMixin(I18nMixin(PolymerElement)));
+    FocusRowMixin(WebUiListenerMixin(PolymerElement));
 
 class SettingsSavedDevicesListItemElement extends
     SettingsSavedDevicesListItemElementBase {
@@ -69,7 +69,7 @@
   listSize: number;
   private shouldShowRemoveSavedDeviceDialog_: boolean;
 
-  private getDeviceName_(device: FastPairSavedDevice): string {
+  private getDeviceNameUnsafe_(device: FastPairSavedDevice): string {
     return device.name;
   }
 
@@ -95,15 +95,16 @@
   }
 
   private getAriaLabel_(device: FastPairSavedDevice): string {
-    const deviceName = this.getDeviceName_(device);
-    return this.i18n(
+    const deviceName = this.getDeviceNameUnsafe_(device);
+    return loadTimeData.getStringF(
         'savedDeviceItemA11yLabel', this.itemIndex + 1, this.listSize,
         deviceName);
   }
 
   private getSubpageButtonA11yLabel_(device: FastPairSavedDevice): string {
-    const deviceName = this.getDeviceName_(device);
-    return this.i18n('savedDeviceItemButtonA11yLabel', deviceName);
+    const deviceName = this.getDeviceNameUnsafe_(device);
+    return loadTimeData.getStringF(
+        'savedDeviceItemButtonA11yLabel', deviceName);
   }
 }
 
diff --git a/chrome/browser/resources/ash/settings/os_settings_menu/os_settings_menu.ts b/chrome/browser/resources/ash/settings/os_settings_menu/os_settings_menu.ts
index a9bd2d2..db9fb0f 100644
--- a/chrome/browser/resources/ash/settings/os_settings_menu/os_settings_menu.ts
+++ b/chrome/browser/resources/ash/settings/os_settings_menu/os_settings_menu.ts
@@ -15,13 +15,13 @@
 import '../os_settings_icons.html.js';
 import './menu_item.js';
 
-import {getDeviceName} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
+import {getDeviceNameUnsafe} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
 import {getBluetoothConfig} from 'chrome://resources/ash/common/bluetooth/cros_bluetooth_config.js';
+import {I18nMixin, I18nMixinInterface} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
+import {WebUiListenerMixin, WebUiListenerMixinInterface} from 'chrome://resources/ash/common/cr_elements/web_ui_listener_mixin.js';
 import {MojoInterfaceProviderImpl} from 'chrome://resources/ash/common/network/mojo_interface_provider.js';
 import {NetworkListenerBehavior, NetworkListenerBehaviorInterface} from 'chrome://resources/ash/common/network/network_listener_behavior.js';
 import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js';
-import {I18nMixin, I18nMixinInterface} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
-import {WebUiListenerMixin, WebUiListenerMixinInterface} from 'chrome://resources/ash/common/cr_elements/web_ui_listener_mixin.js';
 import {BluetoothSystemProperties, BluetoothSystemState, DeviceConnectionState, PairedBluetoothDeviceProperties, SystemPropertiesObserverReceiver as BluetoothPropertiesObserverReceiver} from 'chrome://resources/mojo/chromeos/ash/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom-webui.js';
 import {CrosNetworkConfigInterface, FilterType, NO_LIMIT} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
 import {NetworkType} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
@@ -664,7 +664,7 @@
 
     if (connectedDevices.length === 1) {
       const device = castExists(connectedDevices[0]);
-      this.bluetoothMenuItemDescription_ = getDeviceName(device);
+      this.bluetoothMenuItemDescription_ = getDeviceNameUnsafe(device);
       return;
     }
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index 68cb095..e7335d6 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -56,6 +56,7 @@
   "common/tts_types.ts",
   "panel/panel.ts",
   "panel/panel_captions.ts",
+  "panel/panel_menu_item.ts",
   "chromevox_loader.ts",
 ]
 
@@ -169,7 +170,6 @@
   "panel/menu_manager.js",
   "panel/panel_interface.js",
   "panel/panel_menu.js",
-  "panel/panel_menu_item.js",
   "panel/panel_mode.js",
   "third_party/tamachiyomi/ja_phonetic_data.js",
   "third_party/tamachiyomi/ja_phonetic_map.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_menu_item.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_menu_item.js
deleted file mode 100644
index 498b7f97..0000000
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_menu_item.js
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright 2016 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview An item in a drop-down menu in the ChromeVox panel.
- */
-import {LocalStorage} from '/common/local_storage.js';
-
-import {BackgroundBridge} from '../common/background_bridge.js';
-import {EventSourceType} from '../common/event_source_type.js';
-import {SettingsManager} from '../common/settings_manager.js';
-
-export class PanelMenuItem {
-  /**
-   * @param {string} menuItemTitle The title of the menu item.
-   * @param {string|undefined} menuItemShortcut The keystrokes to select this
-   *     item.
-   * @param {string|undefined} menuItemBraille The braille keystrokes to select
-   *     this item.
-   * @param {string|undefined} gesture The gesture to select this item.
-   * @param {function(): !Promise} callback The function to call if this item
-   *     is selected.
-   * @param {string=} opt_id An optional id for the menu item element.
-   */
-  constructor(
-      menuItemTitle, menuItemShortcut, menuItemBraille, gesture, callback,
-      opt_id) {
-    /** @type {string} */
-    this.menuItemTitle = menuItemTitle;
-    /** @type {string|undefined} */
-    this.menuItemShortcut = menuItemShortcut;
-    /** @type {string|undefined} */
-    this.menuItemBraille = menuItemBraille;
-    /** @type {string|undefined} */
-    this.gesture = gesture;
-    /** @type {function(): !Promise} */
-    this.callback = callback;
-
-    /** @type {Element} */
-    this.element;
-    /** @type {boolean} */
-    this.enabled_ = true;
-
-    this.init_(opt_id);
-  }
-
-  /**
-   * @param {string=} opt_id
-   * @private
-   */
-  async init_(opt_id) {
-    this.element = document.createElement('tr');
-    this.element.className = 'menu-item';
-    this.element.tabIndex = -1;
-    this.element.setAttribute('role', 'menuitem');
-    if (opt_id) {
-      this.element.id = opt_id;
-    }
-
-    this.element.addEventListener(
-        'mouseover', () => this.element.focus(), false);
-
-    const title = document.createElement('td');
-    title.className = 'menu-item-title';
-    title.textContent = this.menuItemTitle;
-
-    // Tooltip in case the menu item is cut off.
-    title.title = this.menuItemTitle;
-    this.element.appendChild(title);
-
-    const eventSource = await BackgroundBridge.EventSource.get();
-    if (eventSource === EventSourceType.TOUCH_GESTURE) {
-      const gestureNode = document.createElement('td');
-      gestureNode.className = 'menu-item-shortcut';
-      gestureNode.textContent = this.gesture;
-      this.element.appendChild(gestureNode);
-      return;
-    }
-
-    const shortcut = document.createElement('td');
-    shortcut.className = 'menu-item-shortcut';
-    shortcut.textContent = this.menuItemShortcut;
-    this.element.appendChild(shortcut);
-
-    if (LocalStorage.get('brailleCaptions') ||
-        SettingsManager.get('menuBrailleCommands')) {
-      const braille = document.createElement('td');
-      braille.className = 'menu-item-shortcut';
-      braille.textContent = this.menuItemBraille;
-      this.element.appendChild(braille);
-    }
-  }
-
-  /**
-   * @return {string} The text content of this menu item.
-   */
-  get text() {
-    return this.element.textContent;
-  }
-
-  /**
-   * @return {boolean} The enabled state of this item.
-   */
-  get enabled() {
-    return this.enabled_;
-  }
-
-  /**
-   * Marks this item as disabled.
-   */
-  disable() {
-    this.enabled_ = false;
-    this.element.classList.add('disabled');
-    this.element.setAttribute('aria-disabled', true);
-  }
-}
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_menu_item.ts b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_menu_item.ts
new file mode 100644
index 0000000..c0affac
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_menu_item.ts
@@ -0,0 +1,107 @@
+// Copyright 2016 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview An item in a drop-down menu in the ChromeVox panel.
+ */
+import {LocalStorage} from '/common/local_storage.js';
+
+import {BackgroundBridge} from '../common/background_bridge.js';
+import {EventSourceType} from '../common/event_source_type.js';
+import {SettingsManager} from '../common/settings_manager.js';
+
+export class PanelMenuItem {
+  callback: () => Promise<void>;
+  element?: HTMLElement;
+  gesture?: string;
+  menuItemBraille?: string;
+  menuItemShortcut?: string;
+  menuItemTitle: string;
+
+  private enabled_ = true;
+
+  /**
+   * @param menuItemTitle The title of the menu item.
+   * @param menuItemShortcut The keystrokes to select this item.
+   * @param menuItemBraille The braille keystrokes to select this item.
+   * @param gesture The gesture to select this item.
+   * @param callback The function to call if this item is selected.
+   * @param optId An optional id for the menu item element.
+   */
+  constructor(
+      menuItemTitle: string, menuItemShortcut: string | undefined,
+      menuItemBraille: string | undefined, gesture: string | undefined,
+      callback: () => Promise<void>, optId?: string) {
+    this.menuItemTitle = menuItemTitle;
+    this.menuItemShortcut = menuItemShortcut;
+    this.menuItemBraille = menuItemBraille;
+    this.gesture = gesture;
+    this.callback = callback;
+
+    this.init_(optId);
+  }
+
+  private async init_(optId?: string): Promise<void> {
+    this.element = document.createElement('tr');
+    this.element.className = 'menu-item';
+    this.element.tabIndex = -1;
+    this.element.setAttribute('role', 'menuitem');
+    if (optId) {
+      this.element.id = optId;
+    }
+
+    // TODO(b/314203187): Not null asserted, check that this is correct.
+    this.element.addEventListener(
+        'mouseover', () => this.element!.focus(), false);
+
+    const title = document.createElement('td');
+    title.className = 'menu-item-title';
+    title.textContent = this.menuItemTitle;
+
+    // Tooltip in case the menu item is cut off.
+    title.title = this.menuItemTitle;
+    this.element.appendChild(title);
+
+    const eventSource = await BackgroundBridge.EventSource.get();
+    if (eventSource === EventSourceType.TOUCH_GESTURE) {
+      const gestureNode = document.createElement('td');
+      gestureNode.className = 'menu-item-shortcut';
+      gestureNode.textContent = this.gesture ?? null;
+      this.element.appendChild(gestureNode);
+      return;
+    }
+
+    const shortcut = document.createElement('td');
+    shortcut.className = 'menu-item-shortcut';
+    shortcut.textContent = this.menuItemShortcut ?? null;
+    this.element.appendChild(shortcut);
+
+    if (LocalStorage.get('brailleCaptions') ||
+        SettingsManager.get('menuBrailleCommands')) {
+      const braille = document.createElement('td');
+      braille.className = 'menu-item-shortcut';
+      braille.textContent = this.menuItemBraille ?? null;
+      this.element.appendChild(braille);
+    }
+  }
+
+  /** @return The text content of this menu item. */
+  get text(): string {
+    // TODO(b/314203187): Not null asserted, check that this is correct.
+    return this.element!.textContent!;
+  }
+
+  /** @return The enabled state of this item. */
+  get enabled(): boolean {
+    return this.enabled_;
+  }
+
+  /** Marks this item as disabled. */
+  disable(): void {
+    this.enabled_ = false;
+    // TODO(b/314203187): Not null asserted, check that this is correct.
+    this.element!.classList.add('disabled');
+    this.element!.setAttribute('aria-disabled', String(true));
+  }
+}
diff --git a/chrome/browser/resources/chromeos/emoji_picker/BUILD.gn b/chrome/browser/resources/chromeos/emoji_picker/BUILD.gn
index 20b30602..9de7849 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/BUILD.gn
+++ b/chrome/browser/resources/chromeos/emoji_picker/BUILD.gn
@@ -43,11 +43,14 @@
 
   icons_html_files = [ "icons.html" ]
 
-  mojo_files_deps =
-      [ "//chrome/browser/ui/webui/ash/emoji:mojo_bindings_ts__generator" ]
+  mojo_files_deps = [
+    "//chrome/browser/ui/webui/ash/emoji:mojo_bindings_ts__generator",
+    "//chromeos/ash/components/emoji:mojo_bindings_ts__generator",
+  ]
   mojo_files = [
     "$root_gen_dir/chrome/browser/ui/webui/ash/emoji/emoji_picker.mojom-webui.ts",
     "$root_gen_dir/chrome/browser/ui/webui/ash/emoji/new_window_proxy.mojom-webui.ts",
+    "$root_gen_dir/chromeos/ash/components/emoji/emoji_search.mojom-webui.ts",
   ]
 
   ts_composite = true
diff --git a/chrome/browser/resources/chromeos/emoji_picker/app.ts b/chrome/browser/resources/chromeos/emoji_picker/app.ts
index b342b761..69234bf 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/app.ts
+++ b/chrome/browser/resources/chromeos/emoji_picker/app.ts
@@ -106,6 +106,7 @@
       nextGifPos: {type: Object, value: () => ({})},
       status: {type: Status, value: null},
       errorMessage: {type: String, value: constants.NO_INTERNET_VIEW_ERROR_MSG},
+      useMojoSearch: {type: Boolean, value: false},
     };
   }
   private category: CategoryEnum;
@@ -136,6 +137,7 @@
   private status: Status|null;
   private previousGifValidation: Date;
   private fetchAndProcessDataPromise: Promise<void>|null;
+  private useMojoSearch = false;
 
   constructor() {
     super();
@@ -464,6 +466,7 @@
     this.searchExtensionEnabled =
         featureList.includes(Feature.EMOJI_PICKER_SEARCH_EXTENSION);
     this.gifSupport = featureList.includes(Feature.EMOJI_PICKER_GIF_SUPPORT);
+    this.useMojoSearch = featureList.includes(Feature.EMOJI_PICKER_MOJO_SEARCH);
     this.sealSupport = featureList.includes(Feature.EMOJI_PICKER_SEAL_SUPPORT);
     this.variantGroupingSupport =
         featureList.includes(Feature.EMOJI_PICKER_VARIANT_GROUPING_SUPPORT);
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_picker_api_proxy.ts b/chrome/browser/resources/chromeos/emoji_picker/emoji_picker_api_proxy.ts
index d003dfe..aacf0a3 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_picker_api_proxy.ts
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_picker_api_proxy.ts
@@ -4,6 +4,7 @@
 import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 
 import {PageHandlerFactory, PageHandlerRemote, Status, TenorGifResponse} from './emoji_picker.mojom-webui.js';
+import {EmojiSearch, SearchResults} from './emoji_search.mojom-webui.js';
 import {NewWindowProxy} from './new_window_proxy.mojom-webui.js';
 import {EmojiVariants, GifSubcategoryData, VisualContent} from './types.js';
 
@@ -29,6 +30,12 @@
   searchGifs(query: string, pos?: string):
       Promise<{status: Status, searchGifs: TenorGifResponse}>;
 
+  searchEmoji(query: string): Promise<{
+    emojiResults: SearchResults,
+    symbolResults: SearchResults,
+    emoticonResults: SearchResults,
+  }>;
+
   getGifsByIds(ids: string[]):
       Promise<{status: Status, selectedGifs: VisualContent[]}>;
 
@@ -42,6 +49,8 @@
 export class EmojiPickerApiProxyImpl implements EmojiPickerApiProxy {
   handler = new PageHandlerRemote();
   newWindowProxy = NewWindowProxy.getRemote();
+  // TODO(b/309343774): Once search is always on, remove function wrapper.
+  searchProxy = () => EmojiSearch.getRemote();
   static instance: EmojiPickerApiProxy|null = null;
   constructor() {
     const factory = PageHandlerFactory.getRemote();
@@ -122,6 +131,10 @@
     return this.handler.searchGifs(query, pos || null);
   }
 
+  searchEmoji(query: string) {
+    return this.searchProxy().searchEmoji(query);
+  }
+
   /** @override */
   getGifsByIds(ids: string[]):
       Promise<{status: Status, selectedGifs: VisualContent[]}> {
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_search.ts b/chrome/browser/resources/chromeos/emoji_picker/emoji_search.ts
index 3c4a914..30f2b0b 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_search.ts
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_search.ts
@@ -7,10 +7,11 @@
 import './emoji_group.js';
 
 import {CrSearchFieldElement} from 'chrome://resources/ash/common/cr_elements/cr_search_field/cr_search_field.js';
-import {PolymerSpliceChange} from 'chrome://resources/polymer/v3_0/polymer/interfaces.js';
-import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {assertNotReached} from 'chrome://resources/js/assert.js';
 import {Size} from 'chrome://resources/mojo/ui/gfx/geometry/mojom/geometry.mojom-webui.js';
 import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
+import {PolymerSpliceChange} from 'chrome://resources/polymer/v3_0/polymer/interfaces.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {NO_INTERNET_SEARCH_ERROR_MSG} from './constants.js';
 import {Status} from './emoji_picker.mojom-webui.js';
@@ -63,6 +64,7 @@
       nextGifPos: {type: String, value: ''},
       errorMessage: {type: String, value: NO_INTERNET_SEARCH_ERROR_MSG},
       closeGifNudgeOverlay: {type: Object},
+      useMojoSearch: {type: Boolean, value: false},
       useGroupedPreference: {type: Boolean, value: false},
       globalTone: {type: Number, value: null, readonly: true},
       globalGender: {type: Number, value: null, readonly: true},
@@ -78,6 +80,7 @@
   private sealSupport: boolean;
   private status: Status|null;
   private closeGifNudgeOverlay: () => void;
+  private useMojoSearch = false;
   private useGroupedPreference: boolean;
   private globalTone: Tone|null = null;
   private globalGender: Gender|null = null;
@@ -115,13 +118,16 @@
     this.addEventListener(GIF_ERROR_TRY_AGAIN, this.onClickTryAgain);
   }
 
-  private onSearch(newSearch: string): void {
+  private async onSearch(newSearch: string): Promise<void> {
     this.sealMode = this.isSealMode(newSearch);
     if (this.sealMode) {
       return;
     }
 
-    const localSearchResults = this.computeLocalSearchResults(newSearch);
+    const localSearchResults = this.useMojoSearch ?
+        await this.computeEmojiSearchResults(newSearch) :
+        this.computeLocalSearchResults(newSearch);
+
     if (!this.gifSupport) {
       this.set('searchResults', localSearchResults);
     } else {
@@ -273,6 +279,48 @@
     this.needIndexing = false;
   }
 
+  private findEmoji(category: CategoryEnum, emojiString: string):
+      EmojiVariants {
+    for (const group of this.categoriesData) {
+      if (group.category !== category) {
+        continue;
+      }
+      for (const emoji of group.emoji) {
+        if (emoji.base.string === emojiString) {
+          return emoji;
+        }
+      }
+    }
+    assertNotReached('Not able to find matching emoji');
+  }
+
+  private async computeEmojiSearchResults(search: string):
+      Promise<EmojiGroupData> {
+    const results =
+        await EmojiPickerApiProxyImpl.getInstance().searchEmoji(search);
+
+    return [
+      {
+        category: CategoryEnum.EMOJI,
+        group: '',
+        emoji: results.emojiResults.results.map(
+            (emoji) => this.findEmoji(CategoryEnum.EMOJI, emoji)),
+      },
+      {
+        category: CategoryEnum.SYMBOL,
+        group: '',
+        emoji: results.symbolResults.results.map(
+            (emoji) => this.findEmoji(CategoryEnum.SYMBOL, emoji)),
+      },
+      {
+        category: CategoryEnum.EMOTICON,
+        group: '',
+        emoji: results.emoticonResults.results.map(
+            (emoji) => this.findEmoji(CategoryEnum.EMOTICON, emoji)),
+      },
+    ];
+  }
+
   /**
    * Computes search results for a keyword.
    *
diff --git a/chrome/browser/resources/chromeos/network_ui/network_ui.ts b/chrome/browser/resources/chromeos/network_ui/network_ui.ts
index 542836e..0e252d03 100644
--- a/chrome/browser/resources/chromeos/network_ui/network_ui.ts
+++ b/chrome/browser/resources/chromeos/network_ui/network_ui.ts
@@ -129,6 +129,9 @@
   private tetheringChangeInProgress_: boolean;
   private invalidJSON_: boolean;
   private showNetworkSelect_: boolean;
+  private onHashChange_: () => void = () => {
+    this.selectTabFromHash_();
+  };
 
   private networkConfig_: CrosNetworkConfigRemote =
       CrosNetworkConfig.getRemote();
@@ -147,9 +150,12 @@
     this.getTetheringStatus_();
     this.getHostname_();
     this.selectTabFromHash_();
-    window.addEventListener('hashchange', () => {
-      this.selectTabFromHash_();
-    });
+    window.addEventListener('hashchange', this.onHashChange_);
+  }
+
+  override disconnectedCallback(): void {
+    super.disconnectedCallback();
+    window.removeEventListener('hashchange', this.onHashChange_);
   }
 
   private computeTabNames_(): string[] {
@@ -181,11 +187,10 @@
     }
   }
 
-  private openCellularActivationUi_() {
-    this.browserProxy_.openCellularActivationUi().then(([result]) => {
-      this.shadowRoot!.querySelector<HTMLElement>(
-                          '#cellular-error-text')!.hidden = result;
-    });
+  private async openCellularActivationUi_() {
+    const response = await this.browserProxy_.openCellularActivationUi();
+    this.shadowRoot!.querySelector<HTMLElement>(
+                        '#cellular-error-text')!.hidden = response[0];
   }
 
   private onResetEsimCacheClick_() {
@@ -248,66 +253,59 @@
   /**
    * Requests the global policy dictionary and updates the page.
    */
-  private requestGlobalPolicy_() {
-    this.networkConfig_.getGlobalPolicy().then(result => {
-      this.shadowRoot!.querySelector('#global-policy')!.textContent =
-          stringifyJson(result.result);
-    });
+  private async requestGlobalPolicy_() {
+    const result = await this.networkConfig_.getGlobalPolicy();
+    this.shadowRoot!.querySelector('#global-policy')!.textContent =
+        stringifyJson(result.result);
   }
 
-  private getTetheringCapabilities_() {
-    this.browserProxy_.getTetheringCapabilities().then(result => {
-      this.shadowRoot!.querySelector(
-                          '#tethering-capabilities-div')!.textContent =
-          stringifyJson(result);
-    });
+  private async getTetheringCapabilities_() {
+    const result = await this.browserProxy_.getTetheringCapabilities();
+    this.shadowRoot!.querySelector('#tethering-capabilities-div')!.textContent =
+        stringifyJson(result);
   }
 
-  private getTetheringStatus_() {
-    this.browserProxy_.getTetheringStatus().then(result => {
-      this.shadowRoot!.querySelector('#tethering-status-div')!.textContent =
-          stringifyJson(result);
-      const state = result['state'];
-      const startingState = loadTimeData.getString('tetheringStateStarting');
-      const activeState = loadTimeData.getString('tetheringStateActive');
-      if (!!state && (state === startingState || state === activeState)) {
-        this.isTetheringEnabled_ = true;
-        return;
-      }
-      this.isTetheringEnabled_ = false;
-    });
+  private async getTetheringStatus_() {
+    const result = await this.browserProxy_.getTetheringStatus();
+    this.shadowRoot!.querySelector('#tethering-status-div')!.textContent =
+        stringifyJson(result);
+    const state = result.state;
+    const startingState = loadTimeData.getString('tetheringStateStarting');
+    const activeState = loadTimeData.getString('tetheringStateActive');
+    if (!!state && (state === startingState || state === activeState)) {
+      this.isTetheringEnabled_ = true;
+      return;
+    }
+    this.isTetheringEnabled_ = false;
   }
 
-  private getTetheringConfig_() {
-    this.browserProxy_.getTetheringConfig().then(result => {
-      this.shadowRoot!.querySelector('#tethering-config-div')!.textContent =
-          stringifyJson(result);
-    });
+  private async getTetheringConfig_() {
+    const result = await this.browserProxy_.getTetheringConfig();
+    this.shadowRoot!.querySelector('#tethering-config-div')!.textContent =
+        stringifyJson(result);
   }
 
-  private setTetheringConfig_() {
-    this.browserProxy_.setTetheringConfig(this.tetheringConfigToSet_)
-        .then((result) => {
-          const success = result === 'success';
-          const resultDiv = this.shadowRoot!.querySelector<HTMLElement>(
-              '#set-tethering-config-result');
-          assert(resultDiv);
-          resultDiv.innerText = result;
-          resultDiv.classList.toggle('error', !success);
-          if (success) {
-            this.getTetheringConfig_();
-          }
-        });
+  private async setTetheringConfig_() {
+    const result =
+        await this.browserProxy_.setTetheringConfig(this.tetheringConfigToSet_);
+    const success = result === 'success';
+    const resultDiv = this.shadowRoot!.querySelector<HTMLElement>(
+        '#set-tethering-config-result');
+    assert(resultDiv);
+    resultDiv.innerText = result;
+    resultDiv.classList.toggle('error', !success);
+    if (success) {
+      this.getTetheringConfig_();
+    }
   }
 
-  private checkTetheringReadiness_() {
-    this.browserProxy_.checkTetheringReadiness().then(result => {
-      const resultDiv = this.shadowRoot!.querySelector<HTMLElement>(
-          '#check-tethering-readiness-result');
-      assert(resultDiv);
-      resultDiv.innerText = result;
-      resultDiv.classList.toggle('error', result !== 'ready');
-    });
+  private async checkTetheringReadiness_() {
+    const result = await this.browserProxy_.checkTetheringReadiness();
+    const resultDiv = this.shadowRoot!.querySelector<HTMLElement>(
+        '#check-tethering-readiness-result');
+    assert(resultDiv);
+    resultDiv.innerText = result;
+    resultDiv.classList.toggle('error', result !== 'ready');
   }
 
   /**
@@ -332,18 +330,17 @@
     }
   }
 
-  private onTetheringToggleChanged_() {
+  private async onTetheringToggleChanged_() {
     this.tetheringChangeInProgress_ = true;
-    this.browserProxy_.setTetheringEnabled(this.isTetheringEnabled_)
-        .then(result => {
-          const resultDiv = this.shadowRoot!.querySelector<HTMLElement>(
-              '#set-tethering-enabled-result');
-          assert(resultDiv);
-          resultDiv.innerText = result;
-          resultDiv.classList.toggle('error', result !== 'success');
-          this.getTetheringStatus_();
-          this.tetheringChangeInProgress_ = false;
-        });
+    const result =
+        await this.browserProxy_.setTetheringEnabled(this.isTetheringEnabled_);
+    const resultDiv = this.shadowRoot!.querySelector<HTMLElement>(
+        '#set-tethering-enabled-result');
+    assert(resultDiv);
+    resultDiv.innerText = result;
+    resultDiv.classList.toggle('error', result !== 'success');
+    this.getTetheringStatus_();
+    this.tetheringChangeInProgress_ = false;
   }
 
   private onHostnameChanged_(_: Event) {
diff --git a/chrome/browser/resources/chromeos/network_ui/network_ui_browser_proxy.ts b/chrome/browser/resources/chromeos/network_ui/network_ui_browser_proxy.ts
index a000190..853f6a8f 100644
--- a/chrome/browser/resources/chromeos/network_ui/network_ui_browser_proxy.ts
+++ b/chrome/browser/resources/chromeos/network_ui/network_ui_browser_proxy.ts
@@ -5,7 +5,7 @@
 import {sendWithPromise} from 'chrome://resources/js/cr.js';
 
 export interface TetheringStatus {
-  [key: string]: string;
+  state?: string;
 }
 
 export interface NetworkUiBrowserProxy {
diff --git a/chrome/browser/resources/compose/animations/animator.ts b/chrome/browser/resources/compose/animations/animator.ts
index 588b262..389ff8c 100644
--- a/chrome/browser/resources/compose/animations/animator.ts
+++ b/chrome/browser/resources/compose/animations/animator.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {assert} from 'chrome://resources/js/assert.js';
+import {assert} from '//resources/js/assert.js';
 
 export const STANDARD_EASING = 'cubic-bezier(0.2, 0.0, 0, 1.0)';
 
diff --git a/chrome/browser/resources/compose/compose.html b/chrome/browser/resources/compose/compose.html
index 38bab7f..78d6034 100644
--- a/chrome/browser/resources/compose/compose.html
+++ b/chrome/browser/resources/compose/compose.html
@@ -4,9 +4,9 @@
   <head>
     <meta charset="utf-8">
     <title>Compose</title>
-    <link rel="stylesheet" href="chrome://resources/css/md_colors.css">
-    <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
-    <link rel="stylesheet" href="chrome://theme/colors.css?sets=ui,chrome">
+    <link rel="stylesheet" href="//resources/css/md_colors.css">
+    <link rel="stylesheet" href="//resources/css/text_defaults_md.css">
+    <link rel="stylesheet" href="//theme/colors.css?sets=ui,chrome">
     <style>
       html, body {
         cursor: default;
diff --git a/chrome/browser/resources/nearby_internals/BUILD.gn b/chrome/browser/resources/nearby_internals/BUILD.gn
index 7636939..8345138 100644
--- a/chrome/browser/resources/nearby_internals/BUILD.gn
+++ b/chrome/browser/resources/nearby_internals/BUILD.gn
@@ -40,9 +40,9 @@
   ts_definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
 
   ts_deps = [
+    "//ash/webui/common/resources/cr_elements:build_ts",
     "//third_party/polymer/v3_0:library",
     "//ui/webui/resources/cr_components/color_change_listener:build_ts",
-    "//ui/webui/resources/cr_elements:build_ts",
     "//ui/webui/resources/js:build_ts",
   ]
 }
diff --git a/chrome/browser/resources/nearby_internals/contact_tab.ts b/chrome/browser/resources/nearby_internals/contact_tab.ts
index ba51c92..097419c 100644
--- a/chrome/browser/resources/nearby_internals/contact_tab.ts
+++ b/chrome/browser/resources/nearby_internals/contact_tab.ts
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://resources/cr_elements/cr_expand_button/cr_expand_button.js';
-import 'chrome://resources/cr_elements/cr_shared_style.css.js';
+import 'chrome://resources/ash/common/cr_elements/cr_expand_button/cr_expand_button.js';
+import 'chrome://resources/ash/common/cr_elements/cr_shared_style.css.js';
 import 'chrome://resources/polymer/v3_0/iron-collapse/iron-collapse.js';
 import './contact_object.js';
 import './shared_style.css.js';
 
-import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
+import {WebUiListenerMixin} from 'chrome://resources/ash/common/cr_elements/web_ui_listener_mixin.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getTemplate} from './contact_tab.html.js';
diff --git a/chrome/browser/resources/nearby_internals/cross_device_internals.ts b/chrome/browser/resources/nearby_internals/cross_device_internals.ts
index 6ea9b27..d2ae5bf 100644
--- a/chrome/browser/resources/nearby_internals/cross_device_internals.ts
+++ b/chrome/browser/resources/nearby_internals/cross_device_internals.ts
@@ -2,19 +2,19 @@
 // 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_button/cr_button.js';
-import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
+import 'chrome://resources/ash/common/cr_elements/cr_button/cr_button.js';
+import 'chrome://resources/ash/common/cr_elements/cr_shared_vars.css.js';
 import './shared_style.css.js';
 import './np_list_object.js';
 import './logging_tab.js';
 import './log_object.js';
 import './log_types.js';
-import '//resources/cr_elements/md_select.css.js';
-import '//resources/cr_elements/chromeos/cros_color_overrides.css.js';
+import '//resources/ash/common/cr_elements/md_select.css.js';
+import '//resources/ash/common/cr_elements/cros_color_overrides.css.js';
 import 'chrome://resources/polymer/v3_0/iron-location/iron-location.js';
 import 'chrome://resources/polymer/v3_0/iron-pages/iron-pages.js';
 
-import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
+import {WebUiListenerMixin} from 'chrome://resources/ash/common/cr_elements/web_ui_listener_mixin.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getTemplate} from './cross_device_internals.html.js';
diff --git a/chrome/browser/resources/nearby_internals/http_message_object.ts b/chrome/browser/resources/nearby_internals/http_message_object.ts
index 64b1e6f0..0e2c2a87 100644
--- a/chrome/browser/resources/nearby_internals/http_message_object.ts
+++ b/chrome/browser/resources/nearby_internals/http_message_object.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://resources/cr_elements/cr_expand_button/cr_expand_button.js';
+import 'chrome://resources/ash/common/cr_elements/cr_expand_button/cr_expand_button.js';
 import 'chrome://resources/polymer/v3_0/iron-collapse/iron-collapse.js';
 
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/browser/resources/nearby_internals/http_tab.ts b/chrome/browser/resources/nearby_internals/http_tab.ts
index cb04708..201358f 100644
--- a/chrome/browser/resources/nearby_internals/http_tab.ts
+++ b/chrome/browser/resources/nearby_internals/http_tab.ts
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://resources/cr_elements/cr_expand_button/cr_expand_button.js';
-import 'chrome://resources/cr_elements/cr_shared_style.css.js';
+import 'chrome://resources/ash/common/cr_elements/cr_expand_button/cr_expand_button.js';
+import 'chrome://resources/ash/common/cr_elements/cr_shared_style.css.js';
 import 'chrome://resources/polymer/v3_0/iron-collapse/iron-collapse.js';
 import './http_message_object.js';
 import './shared_style.css.js';
 
-import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
+import {WebUiListenerMixin} from 'chrome://resources/ash/common/cr_elements/web_ui_listener_mixin.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getTemplate} from './http_tab.html.js';
diff --git a/chrome/browser/resources/nearby_internals/log_types.ts b/chrome/browser/resources/nearby_internals/log_types.ts
index 95c571a8..b0339fe 100644
--- a/chrome/browser/resources/nearby_internals/log_types.ts
+++ b/chrome/browser/resources/nearby_internals/log_types.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
+import 'chrome://resources/ash/common/cr_elements/cr_shared_vars.css.js';
 import './shared_style.css.js';
 import 'chrome://resources/polymer/v3_0/iron-location/iron-location.js';
 import 'chrome://resources/polymer/v3_0/iron-pages/iron-pages.js';
diff --git a/chrome/browser/resources/nearby_internals/logging_tab.ts b/chrome/browser/resources/nearby_internals/logging_tab.ts
index ee42fbbb..5e469b5d 100644
--- a/chrome/browser/resources/nearby_internals/logging_tab.ts
+++ b/chrome/browser/resources/nearby_internals/logging_tab.ts
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import 'chrome://resources/ash/common/cr_elements/cr_button/cr_button.js';
 import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
-import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
+import 'chrome://resources/ash/common/cr_elements/cr_shared_vars.css.js';
 import './log_object.js';
 import './shared_style.css.js';
 
-import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
+import {WebUiListenerMixin} from 'chrome://resources/ash/common/cr_elements/web_ui_listener_mixin.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {NearbyLogsBrowserProxy} from './cross_device_logs_browser_proxy.js';
diff --git a/chrome/browser/resources/nearby_internals/nearby_internals.ts b/chrome/browser/resources/nearby_internals/nearby_internals.ts
index fe8c67a..a95d994 100644
--- a/chrome/browser/resources/nearby_internals/nearby_internals.ts
+++ b/chrome/browser/resources/nearby_internals/nearby_internals.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://resources/cr_elements/cr_tabs/cr_tabs.js';
+import 'chrome://resources/ash/common/cr_elements/cr_tabs/cr_tabs.js';
 import 'chrome://resources/polymer/v3_0/iron-location/iron-location.js';
 import 'chrome://resources/polymer/v3_0/iron-pages/iron-pages.js';
 import './http_tab.js';
diff --git a/chrome/browser/resources/nearby_internals/shared_style.css b/chrome/browser/resources/nearby_internals/shared_style.css
index f3e58cdf..808479f 100644
--- a/chrome/browser/resources/nearby_internals/shared_style.css
+++ b/chrome/browser/resources/nearby_internals/shared_style.css
@@ -4,7 +4,7 @@
 
 /* #css_wrapper_metadata_start
  * #type=style
- * #import=chrome://resources/cr_elements/cr_shared_style.css.js
+ * #import=chrome://resources/ash/common/cr_elements/cr_shared_style.css.js
  * #include=cr-shared-style
  * #css_wrapper_metadata_end */
 
diff --git a/chrome/browser/resources/nearby_internals/ui_trigger_tab.ts b/chrome/browser/resources/nearby_internals/ui_trigger_tab.ts
index 0171cd3..2251acd 100644
--- a/chrome/browser/resources/nearby_internals/ui_trigger_tab.ts
+++ b/chrome/browser/resources/nearby_internals/ui_trigger_tab.ts
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://resources/cr_elements/cr_button/cr_button.js';
-import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
+import 'chrome://resources/ash/common/cr_elements/cr_button/cr_button.js';
+import 'chrome://resources/ash/common/cr_elements/cr_shared_vars.css.js';
 import './ui_trigger_list_object.js';
 import './cross_device_internals.js';
 import './shared_style.css.js';
 
-import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
+import {WebUiListenerMixin} from 'chrome://resources/ash/common/cr_elements/web_ui_listener_mixin.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {NearbyPrefsBrowserProxy} from './nearby_prefs_browser_proxy.js';
diff --git a/chrome/browser/resources/settings/privacy_page/security_page.ts b/chrome/browser/resources/settings/privacy_page/security_page.ts
index 7b5bd2c..348dc96 100644
--- a/chrome/browser/resources/settings/privacy_page/security_page.ts
+++ b/chrome/browser/resources/settings/privacy_page/security_page.ts
@@ -314,6 +314,10 @@
    */
   override currentRouteChanged(route: Route) {
     if (route !== routes.SECURITY) {
+      // If the user navigates to other settings page from security page, call
+      // onBeforeUnload_ method to check if the security page survey should be
+      // shown.
+      this.onBeforeUnload_();
       this.isRouteSecurity_ = false;
       this.eventTracker_.removeAll();
       return;
diff --git a/chrome/browser/resources/settings/privacy_sandbox/privacy_sandbox_interest_item.ts b/chrome/browser/resources/settings/privacy_sandbox/privacy_sandbox_interest_item.ts
index 99914af6..bc4e2a4 100644
--- a/chrome/browser/resources/settings/privacy_sandbox/privacy_sandbox_interest_item.ts
+++ b/chrome/browser/resources/settings/privacy_sandbox/privacy_sandbox_interest_item.ts
@@ -54,7 +54,7 @@
       assert(!this.interest.site);
       return this.i18n(
           this.interest.removed ?
-              ((loadTimeData.getBoolean('isProactiveTopicsBlockingEnabled')) ?
+              (loadTimeData.getBoolean('isProactiveTopicsBlockingEnabled') ?
                    'unblockTopicButtonTextV2' :
                    'topicsPageAllowTopic') :
               'topicsPageBlockTopic');
@@ -70,8 +70,11 @@
     if (this.interest.topic !== undefined) {
       assert(!this.interest.site);
       return this.i18n(
-          this.interest.removed ? 'topicsPageAllowTopicA11yLabel' :
-                                  'topicsPageBlockTopicA11yLabel',
+          this.interest.removed ?
+              (loadTimeData.getBoolean('isProactiveTopicsBlockingEnabled') ?
+                   'topicsPageUnblockTopicA11yLabel' :
+                   'topicsPageAllowTopicA11yLabel') :
+              'topicsPageBlockTopicA11yLabel',
           this.interest.topic.displayString!);
     } else {
       assert(!this.interest.topic);
diff --git a/chrome/browser/resources/side_panel/read_anything/app.ts b/chrome/browser/resources/side_panel/read_anything/app.ts
index a955c4d..666ed8f 100644
--- a/chrome/browser/resources/side_panel/read_anything/app.ts
+++ b/chrome/browser/resources/side_panel/read_anything/app.ts
@@ -428,10 +428,12 @@
     // first text node.
     this.firstTextNodeSetForReadAloud = -1;
 
-    this.updateContentInternal();
+    this.refreshContent();
   }
 
-  private updateContentInternal() {
+  // Refreshes the content. This should only be called from the UI to avoid
+  // clearing state, such as the first text node.
+  private refreshContent() {
     const shadowRoot = this.shadowRoot;
     assert(shadowRoot);
     const container = shadowRoot.getElementById('container');
@@ -616,7 +618,7 @@
     // if it's paused from a non-pause button (e.g. voice previews) so the links
     // don't flash off and on.
     if (chrome.readingMode.linksEnabled && pausedFromPlayClickButton) {
-      this.updateContent();
+      this.refreshContent();
       this.highlightNodes(chrome.readingMode.getCurrentText());
     }
   }
@@ -662,7 +664,7 @@
       // Hide links when speech resumes. We only hide links when the page was
       // paused from the play/pause button.
       if (chrome.readingMode.linksEnabled && pausedFromPlayClickButton) {
-        this.updateContent();
+        this.refreshContent();
       }
 
       // If the current read highlight has been cleared from a call to
@@ -682,7 +684,7 @@
       this.pausedFromPlayClickButton = false;
       // Hide links when speech begins playing.
       if (chrome.readingMode.linksEnabled) {
-        this.updateContent();
+        this.refreshContent();
       }
 
       // TODO(crbug.com/1474951): There should be a way to use AXPosition so
@@ -929,7 +931,7 @@
 
     // Hide links when speech finishes playing.
     if (chrome.readingMode.linksEnabled) {
-      this.updateContent();
+      this.refreshContent();
     }
   }
 
diff --git a/chrome/browser/screen_ai/screen_ai_service_router_browsertest.cc b/chrome/browser/screen_ai/screen_ai_service_router_browsertest.cc
index 8418725b..1f1dd51 100644
--- a/chrome/browser/screen_ai/screen_ai_service_router_browsertest.cc
+++ b/chrome/browser/screen_ai/screen_ai_service_router_browsertest.cc
@@ -99,8 +99,11 @@
   ~ScreenAIServiceRouterTest() override = default;
 
   bool IsLibraryAvailable() {
-    return screen_ai::PlatformSupportsBrowserTests() ? std::get<0>(GetParam())
-                                                     : false;
+#if BUILDFLAG(ENABLE_SCREEN_AI_BROWSERTESTS)
+    return std::get<0>(GetParam());
+#else
+    return false;
+#endif
   }
 
   bool IsOCREnabled() { return std::get<1>(GetParam()); }
diff --git a/chrome/browser/send_tab_to_self/receiving_ui_handler.h b/chrome/browser/send_tab_to_self/receiving_ui_handler.h
index a78c886..b4811bb6 100644
--- a/chrome/browser/send_tab_to_self/receiving_ui_handler.h
+++ b/chrome/browser/send_tab_to_self/receiving_ui_handler.h
@@ -15,7 +15,7 @@
 class SendTabToSelfEntry;
 
 // Interface implemented by platforms to handle changes to the SendTabToSelf
-// model. sImplementors of this interface should override all functions and
+// model. Implementors of this interface should override all functions and
 // update the UI accordingly. They should also register themselves with the
 // ReceivingUIRegistry.
 class ReceivingUiHandler {
diff --git a/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.cc b/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.cc
index 0994cf0..9a5f4e3 100644
--- a/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.cc
+++ b/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.cc
@@ -17,7 +17,6 @@
 #include "chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.h"
 #include "chrome/browser/signin/bound_session_credentials/bound_session_switches.h"
 #include "chrome/browser/signin/bound_session_credentials/session_binding_helper.h"
-#include "chrome/browser/signin/wait_for_network_callback_helper_chrome.h"
 #include "chrome/common/renderer_configuration.mojom.h"
 #include "content/public/browser/storage_partition.h"
 #include "services/network/public/cpp/network_connection_tracker.h"
@@ -45,9 +44,7 @@
     : BoundSessionCookieController(bound_session_params, delegate),
       key_service_(key_service),
       storage_partition_(storage_partition),
-      network_connection_tracker_(network_connection_tracker),
-      wait_for_network_callback_helper_(
-          std::make_unique<WaitForNetworkCallbackHelperChrome>()) {
+      network_connection_tracker_(network_connection_tracker) {
   CHECK(!bound_session_params.wrapped_key().empty());
   base::span<const uint8_t> wrapped_key =
       base::as_bytes(base::make_span(bound_session_params.wrapped_key()));
@@ -84,29 +81,19 @@
 
 void BoundSessionCookieControllerImpl::Initialize() {
   network_connection_observer_.Observe(network_connection_tracker_);
+  is_offline_ = network_connection_tracker_->IsOffline();
   CreateBoundCookiesObservers();
   MaybeRefreshCookie();
 }
 
 void BoundSessionCookieControllerImpl::OnConnectionChanged(
     network::mojom::ConnectionType type) {
-  if (type == network::mojom::ConnectionType::CONNECTION_NONE) {
-    // Let network requests fail now while there is no internet connection,
-    // instead of holding them up until the network is back or timeout occurs.
-    // The network could come back shortly before the timeout which would result
-    // in requests being released without a valid cookie.
-    ResumeBlockedRequests(
-        chrome::mojom::ResumeBlockedRequestsTrigger::kNetworkConnectionOffline);
+  if (is_offline_ && type != network::mojom::ConnectionType::CONNECTION_NONE) {
+    // We are back online. Schedule a new cookie rotation if needed.
+    MaybeScheduleCookieRotation();
   }
-}
 
-bool BoundSessionCookieControllerImpl::IsConnectionTypeAvailableAndOffline() {
-  network::mojom::ConnectionType type;
-  return network_connection_tracker_->GetConnectionType(
-             &type, base::BindOnce(
-                        &BoundSessionCookieControllerImpl::OnConnectionChanged,
-                        weak_ptr_factory_.GetWeakPtr())) &&
-         type == network::mojom::ConnectionType::CONNECTION_NONE;
+  is_offline_ = type == network::mojom::ConnectionType::CONNECTION_NONE;
 }
 
 void BoundSessionCookieControllerImpl::HandleRequestBlockedOnCookie(
@@ -122,13 +109,6 @@
   resume_blocked_requests_.push_back(std::move(resume_blocked_request));
   MaybeRefreshCookie();
 
-  if (IsConnectionTypeAvailableAndOffline()) {
-    // See the comment in `OnConnectionChanged()` for explanation.
-    ResumeBlockedRequests(
-        chrome::mojom::ResumeBlockedRequestsTrigger::kNetworkConnectionOffline);
-    return;
-  }
-
   if (!resume_blocked_requests_timer_.IsRunning() &&
       !resume_blocked_requests_.empty()) {
     // Ensure all blocked requests are released after a timeout.
@@ -196,8 +176,7 @@
   return refresh_cookie_fetcher_factory_for_testing_.is_null()
              ? std::make_unique<BoundSessionRefreshCookieFetcherImpl>(
                    storage_partition_->GetURLLoaderFactoryForBrowserProcess(),
-                   *wait_for_network_callback_helper_, *session_binding_helper_,
-                   url_, std::move(cookie_names))
+                   *session_binding_helper_, url_, std::move(cookie_names))
              : refresh_cookie_fetcher_factory_for_testing_.Run(
                    storage_partition_->GetCookieManagerForBrowserProcess(),
                    url_, std::move(cookie_names));
diff --git a/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.h b/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.h
index 0d68538..a12a4c2b 100644
--- a/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.h
+++ b/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.h
@@ -29,13 +29,11 @@
 
 class BoundSessionCookieObserver;
 class SessionBindingHelper;
-class WaitForNetworkCallbackHelper;
 
 class BoundSessionCookieControllerImpl
     : public BoundSessionCookieController,
       public network::NetworkConnectionTracker::NetworkConnectionObserver {
  public:
-
   BoundSessionCookieControllerImpl(
       unexportable_keys::UnexportableKeyService& key_service,
       content::StoragePartition* storage_partition,
@@ -71,8 +69,6 @@
           const GURL& url,
           base::flat_set<std::string> cookie_names)>;
 
-  bool IsConnectionTypeAvailableAndOffline();
-
   std::unique_ptr<BoundSessionRefreshCookieFetcher> CreateRefreshCookieFetcher()
       const;
   void CreateBoundCookiesObservers();
@@ -108,9 +104,9 @@
       network::NetworkConnectionTracker,
       network::NetworkConnectionTracker::NetworkConnectionObserver>
       network_connection_observer_{this};
+  // Also `true` while the initial connection state is unknown.
+  bool is_offline_ = true;
 
-  std::unique_ptr<WaitForNetworkCallbackHelper>
-      wait_for_network_callback_helper_;
   std::unique_ptr<SessionBindingHelper> session_binding_helper_;
   std::unique_ptr<BoundSessionRefreshCookieFetcher> refresh_cookie_fetcher_;
 
@@ -130,9 +126,6 @@
   std::unique_ptr<base::RetainingOneShotTimer> artifical_cookie_rotation_delay_;
   std::optional<BoundSessionRefreshCookieFetcher::Result>
       artificial_cookie_rotation_result_;
-
-  base::WeakPtrFactory<BoundSessionCookieControllerImpl> weak_ptr_factory_{
-      this};
 };
 
 #endif  // CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_BOUND_SESSION_COOKIE_CONTROLLER_IMPL_H_
diff --git a/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl_unittest.cc b/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl_unittest.cc
index a59515d..8637068 100644
--- a/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl_unittest.cc
+++ b/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/time/time.h"
 #include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller.h"
 #include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_observer.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_params.pb.h"
 #include "chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.h"
 #include "chrome/browser/signin/bound_session_credentials/bound_session_test_cookie_manager.h"
 #include "chrome/browser/signin/bound_session_credentials/fake_bound_session_refresh_cookie_fetcher.h"
@@ -68,36 +69,17 @@
     : public testing::Test,
       public BoundSessionCookieController::Delegate {
  public:
-  BoundSessionCookieControllerImplTest()
+  explicit BoundSessionCookieControllerImplTest(bool build_controller = true)
       : unexportable_key_service_(unexportable_key_task_manager_),
         key_id_(GenerateNewKey()) {
-    std::vector<uint8_t> wrapped_key = GetWrappedKey(key_id_);
-    bound_session_credentials::BoundSessionParams bound_session_params;
-    bound_session_params.set_site("https://google.com");
-    bound_session_params.set_session_id("test_session_id");
-    bound_session_params.set_wrapped_key(
-        std::string(wrapped_key.begin(), wrapped_key.end()));
-    *bound_session_params.add_credentials() =
-        CreateCookieCredential(k1PSIDTSCookieName);
-    *bound_session_params.add_credentials() =
-        CreateCookieCredential(k3PSIDTSCookieName);
-
     storage_partition_.set_cookie_manager_for_browser_process(&cookie_manager_);
 
     SetUpNetworkConnection(true,
                            network::mojom::ConnectionType::CONNECTION_WIFI);
 
-    bound_session_cookie_controller_ =
-        std::make_unique<BoundSessionCookieControllerImpl>(
-            unexportable_key_service_, &storage_partition_,
-            content::GetNetworkConnectionTracker(), bound_session_params, this);
-
-    bound_session_cookie_controller_
-        ->set_refresh_cookie_fetcher_factory_for_testing(
-            base::BindRepeating(&BoundSessionCookieControllerImplTest::
-                                    CreateBoundSessionRefreshCookieFetcher,
-                                base::Unretained(this)));
-    bound_session_cookie_controller_->Initialize();
+    if (build_controller) {
+      BuildBoundSessionCookieController();
+    }
   }
 
   ~BoundSessionCookieControllerImplTest() override = default;
@@ -127,7 +109,7 @@
       network::mojom::CookieManager* cookie_manager,
       const GURL& url,
       base::flat_set<std::string> cookie_names) {
-    // `SimulateCompleteRefreshRequest()` must be called for the
+    // Call `SimulateCompleteRefreshRequest()` to complete request.
     return std::make_unique<FakeBoundSessionRefreshCookieFetcher>(
         cookie_manager, url, std::move(cookie_names));
   }
@@ -245,6 +227,32 @@
     on_bound_session_throttler_params_changed_call_count_ = 0;
   }
 
+  // This shouldn't be called more than once per test. The second controller
+  // won't be able to register itself properly with `cookie_manager_`.
+  void BuildBoundSessionCookieController() {
+    std::vector<uint8_t> wrapped_key = GetWrappedKey(key_id_);
+    bound_session_credentials::BoundSessionParams bound_session_params;
+    bound_session_params.set_site("https://google.com");
+    bound_session_params.set_session_id("test_session_id");
+    bound_session_params.set_wrapped_key(
+        std::string(wrapped_key.begin(), wrapped_key.end()));
+    *bound_session_params.add_credentials() =
+        CreateCookieCredential(k1PSIDTSCookieName);
+    *bound_session_params.add_credentials() =
+        CreateCookieCredential(k3PSIDTSCookieName);
+
+    bound_session_cookie_controller_ =
+        std::make_unique<BoundSessionCookieControllerImpl>(
+            unexportable_key_service_, &storage_partition_,
+            content::GetNetworkConnectionTracker(), bound_session_params, this);
+    bound_session_cookie_controller_
+        ->set_refresh_cookie_fetcher_factory_for_testing(
+            base::BindRepeating(&BoundSessionCookieControllerImplTest::
+                                    CreateBoundSessionRefreshCookieFetcher,
+                                base::Unretained(this)));
+    bound_session_cookie_controller_->Initialize();
+  }
+
   void ResetBoundSessionCookieController() {
     bound_session_cookie_controller_.reset();
   }
@@ -260,12 +268,9 @@
   void SetConnectionType(network::mojom::ConnectionType connection_type) {
     network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
         connection_type);
-  }
-
-  bool IsConnectionTypeAvailableAndOffline() {
-    CHECK(bound_session_cookie_controller_);
-    return bound_session_cookie_controller_
-        ->IsConnectionTypeAvailableAndOffline();
+    // Ensure that the network connection observers have been notified before
+    // this call returns.
+    task_environment_.RunUntilIdle();
   }
 
   base::HistogramTester* histogram_tester() { return &histogram_tester_; }
@@ -386,7 +391,6 @@
 
 TEST_F(BoundSessionCookieControllerImplTest,
        RequestBlockedOnCookieWhenCookieFresh) {
-  ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
   // Set fresh cookie.
   CompletePendingRefreshRequestIfAny();
   BoundSessionCookieController* controller = bound_session_cookie_controller();
@@ -403,7 +407,6 @@
 
 TEST_F(BoundSessionCookieControllerImplTest,
        RequestBlockedOnCookieWhenCookieStaleTriggersARefresh) {
-  ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
   CompletePendingRefreshRequestIfAny();
 
   BoundSessionCookieController* controller = bound_session_cookie_controller();
@@ -442,7 +445,6 @@
 
 TEST_F(BoundSessionCookieControllerImplTest,
        RequestBlockedWhenNotAllCookiesFresh) {
-  ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
   CompletePendingRefreshRequestIfAny();
   BoundSessionCookieController* controller = bound_session_cookie_controller();
 
@@ -481,7 +483,6 @@
 
 TEST_F(BoundSessionCookieControllerImplTest,
        RequestBlockedOnCookieRefreshFailedWithPersistentError) {
-  ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
   CompletePendingRefreshRequestIfAny();
   EXPECT_FALSE(on_persistent_error_encountered_called());
 
@@ -519,7 +520,6 @@
 }
 
 TEST_F(BoundSessionCookieControllerImplTest, RefreshFailedTransient) {
-  ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
   CompletePendingRefreshRequestIfAny();
   task_environment()->FastForwardBy(base::Minutes(12));
   EXPECT_FALSE(AreAllCookiesFresh());
@@ -565,7 +565,6 @@
 
 TEST_F(BoundSessionCookieControllerImplTest,
        RequestBlockedOnCookieMultipleRequests) {
-  ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
   CompletePendingRefreshRequestIfAny();
   ResetOnBoundSessionThrottlerParamsChangedCallCount();
   // Cookie stale.
@@ -599,7 +598,6 @@
 
 TEST_F(BoundSessionCookieControllerImplTest,
        CookieChangesToFreshWhileRequestBlockedOnCookieIsPending) {
-  ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
   CompletePendingRefreshRequestIfAny();
   // Stale cookie.
   task_environment()->FastForwardBy(base::Minutes(12));
@@ -634,7 +632,6 @@
 
 TEST_F(BoundSessionCookieControllerImplTest,
        ControllerDestroyedRequestBlockedOnCookieIsPending) {
-  ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
   BoundSessionCookieController* controller = bound_session_cookie_controller();
   std::array<base::test::TestFuture<ResumeBlockedRequestsTrigger>, 5> futures;
   for (auto& future : futures) {
@@ -657,7 +654,6 @@
 }
 
 TEST_F(BoundSessionCookieControllerImplTest, ResumeBlockedRequestsOnTimeout) {
-  ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
   base::test::TestFuture<ResumeBlockedRequestsTrigger> future;
   bound_session_cookie_controller()->HandleRequestBlockedOnCookie(
       future.GetCallback());
@@ -686,7 +682,6 @@
 }
 
 TEST_F(BoundSessionCookieControllerImplTest, SuccessiveRequestTimeout) {
-  ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
   size_t successive_timeout = 5;
   for (size_t i = 0; i < successive_timeout; i++) {
     base::test::TestFuture<ResumeBlockedRequestsTrigger> future;
@@ -706,7 +701,6 @@
 }
 
 TEST_F(BoundSessionCookieControllerImplTest, SuccessiveRequestTimeoutReset) {
-  ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
   base::test::TestFuture<ResumeBlockedRequestsTrigger> future;
   bound_session_cookie_controller()->HandleRequestBlockedOnCookie(
       future.GetCallback());
@@ -735,7 +729,6 @@
 
 TEST_F(BoundSessionCookieControllerImplTest,
        BlockedRequestsCalculateTimeoutFromFirstRequest) {
-  ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
   constexpr int kBlockedRequestCount = 2;
   constexpr base::TimeDelta kDeltaBetweenRequests = base::Seconds(1);
   BoundSessionCookieController* controller = bound_session_cookie_controller();
@@ -765,7 +758,6 @@
 }
 
 TEST_F(BoundSessionCookieControllerImplTest, ResumeBlockedRequestsTimerReset) {
-  ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
   base::test::TestFuture<ResumeBlockedRequestsTrigger> future;
   bound_session_cookie_controller()->HandleRequestBlockedOnCookie(
       future.GetCallback());
@@ -781,68 +773,80 @@
 }
 
 TEST_F(BoundSessionCookieControllerImplTest,
-       ResumeNewBlockedRequestsWhenOffline) {
-  SetUpNetworkConnection(true, network::mojom::ConnectionType::CONNECTION_NONE);
-  ASSERT_TRUE(IsConnectionTypeAvailableAndOffline());
-  base::test::TestFuture<ResumeBlockedRequestsTrigger> future;
-  bound_session_cookie_controller()->HandleRequestBlockedOnCookie(
-      future.GetCallback());
-  EXPECT_FALSE(AreAllCookiesFresh());
-  EXPECT_TRUE(cookie_fetcher());
-  EXPECT_TRUE(future.IsReady());
-  EXPECT_EQ(future.Get(),
-            ResumeBlockedRequestsTrigger::kNetworkConnectionOffline);
-  EXPECT_FALSE(resume_blocked_requests_timer()->IsRunning());
-}
-
-TEST_F(BoundSessionCookieControllerImplTest,
-       BlockRequestsWhenConnectionTypeUnavailable) {
-  SetUpNetworkConnection(false,
-                         network::mojom::ConnectionType::CONNECTION_WIFI);
-  ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
-  base::test::TestFuture<ResumeBlockedRequestsTrigger> future;
-  bound_session_cookie_controller()->HandleRequestBlockedOnCookie(
-      future.GetCallback());
-  EXPECT_FALSE(future.IsReady());
-  EXPECT_TRUE(resume_blocked_requests_timer()->IsRunning());
-  task_environment()->RunUntilIdle();
-  EXPECT_FALSE(future.IsReady());
+       ConnectionTypeChangesDontAffectScheduledPreemptiveRefresh) {
   CompletePendingRefreshRequestIfAny();
-  EXPECT_TRUE(future.IsReady());
-  EXPECT_EQ(future.Get(),
-            ResumeBlockedRequestsTrigger::kCookieRefreshFetchSuccess);
-  EXPECT_THAT(
-      histogram_tester()->GetAllSamples(
-          "Signin.BoundSessionCredentials.ResumeThrottledRequestsTrigger"),
-      testing::ElementsAre(
-          base::Bucket(ResumeBlockedRequestsTrigger::kCookieRefreshFetchSuccess,
-                       /*count=*/1)));
-}
 
-TEST_F(BoundSessionCookieControllerImplTest,
-       ResumePendingBlockedRequestsOnNetworkConnectionChangedToOffline) {
-  ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
-  base::test::TestFuture<ResumeBlockedRequestsTrigger> future;
-  bound_session_cookie_controller()->HandleRequestBlockedOnCookie(
-      future.GetCallback());
-  EXPECT_FALSE(future.IsReady());
-  EXPECT_TRUE(resume_blocked_requests_timer()->IsRunning());
+  // Cookie is valid and preemptive refresh is scheduled.
+  EXPECT_FALSE(cookie_fetcher());
+  EXPECT_TRUE(preemptive_cookie_refresh_timer()->IsRunning());
+
+  // Flip through different connection states and check that it doesn't affect
+  // the fetcher state.
+  SetConnectionType(network::mojom::ConnectionType::CONNECTION_5G);
+  EXPECT_FALSE(cookie_fetcher());
+  EXPECT_TRUE(preemptive_cookie_refresh_timer()->IsRunning());
 
   SetConnectionType(network::mojom::ConnectionType::CONNECTION_NONE);
-  ASSERT_TRUE(IsConnectionTypeAvailableAndOffline());
-  task_environment()->RunUntilIdle();
-  EXPECT_FALSE(AreAllCookiesFresh());
+  EXPECT_FALSE(cookie_fetcher());
+  EXPECT_TRUE(preemptive_cookie_refresh_timer()->IsRunning());
+
+  SetConnectionType(network::mojom::ConnectionType::CONNECTION_5G);
+  EXPECT_FALSE(cookie_fetcher());
+  EXPECT_TRUE(preemptive_cookie_refresh_timer()->IsRunning());
+}
+
+TEST_F(BoundSessionCookieControllerImplTest,
+       ScheduleCookieRefreshIfComingOnline) {
+  // Set up a situation where cookies are stale and there is no ongoing refresh.
+  // `kServerTransientError` is used to complete the refresh request without
+  // updating the cookies.
+  SimulateCompleteRefreshRequest(
+      BoundSessionRefreshCookieFetcher::Result::kServerTransientError,
+      std::nullopt);
+  EXPECT_FALSE(cookie_fetcher());
+  EXPECT_FALSE(preemptive_cookie_refresh_timer()->IsRunning());
+
+  // Switch to another online type doesn't change anything.
+  SetConnectionType(network::mojom::ConnectionType::CONNECTION_5G);
+  EXPECT_FALSE(cookie_fetcher());
+  EXPECT_FALSE(preemptive_cookie_refresh_timer()->IsRunning());
+
+  // Setting up an offline state.
+  SetConnectionType(network::mojom::ConnectionType::CONNECTION_NONE);
+  EXPECT_FALSE(cookie_fetcher());
+  EXPECT_FALSE(preemptive_cookie_refresh_timer()->IsRunning());
+
+  // Cookie fetcher should start immediately as the cookie is expired.
+  SetConnectionType(network::mojom::ConnectionType::CONNECTION_5G);
   EXPECT_TRUE(cookie_fetcher());
-  EXPECT_TRUE(future.IsReady());
-  EXPECT_EQ(future.Get(),
-            ResumeBlockedRequestsTrigger::kNetworkConnectionOffline);
-  EXPECT_FALSE(resume_blocked_requests_timer()->IsRunning());
-  EXPECT_THAT(
-      histogram_tester()->GetAllSamples(
-          "Signin.BoundSessionCredentials.ResumeThrottledRequestsTrigger"),
-      testing::ElementsAre(
-          base::Bucket(ResumeBlockedRequestsTrigger::kNetworkConnectionOffline,
-                       /*count=*/1)));
+  EXPECT_FALSE(preemptive_cookie_refresh_timer()->IsRunning());
+}
+
+class BoundSessionCookieControllerImplNoDefaultControllerTest
+    : public BoundSessionCookieControllerImplTest {
+ public:
+  BoundSessionCookieControllerImplNoDefaultControllerTest()
+      : BoundSessionCookieControllerImplTest(/*build_controller=*/false) {}
+};
+
+TEST_F(BoundSessionCookieControllerImplNoDefaultControllerTest,
+       ScheduleCookieRefreshIfComingOnlineStartingOffline) {
+  SetUpNetworkConnection(true, network::mojom::ConnectionType::CONNECTION_NONE);
+  BuildBoundSessionCookieController();
+
+  // Set up a situation where cookies are stale and there is no ongoing refresh.
+  // `kServerTransientError` is used to complete the refresh request without
+  // updating the cookies.
+  SimulateCompleteRefreshRequest(
+      BoundSessionRefreshCookieFetcher::Result::kServerTransientError,
+      std::nullopt);
+  EXPECT_FALSE(cookie_fetcher());
+  EXPECT_FALSE(preemptive_cookie_refresh_timer()->IsRunning());
+
+  // Cookie fetcher should start immediately as the cookie is expired.
+  SetConnectionType(network::mojom::ConnectionType::CONNECTION_WIFI);
+  EXPECT_TRUE(cookie_fetcher());
+  EXPECT_FALSE(preemptive_cookie_refresh_timer()->IsRunning());
 }
 
 TEST_F(BoundSessionCookieControllerImplTest,
diff --git a/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.cc b/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.cc
index 4ddbeb7..2568166 100644
--- a/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.cc
+++ b/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.cc
@@ -16,7 +16,6 @@
 #include "base/timer/elapsed_timer.h"
 #include "base/trace_event/typed_macros.h"
 #include "chrome/browser/signin/bound_session_credentials/session_binding_helper.h"
-#include "components/signin/public/base/wait_for_network_callback_helper.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "net/cookies/canonical_cookie.h"
@@ -51,12 +50,10 @@
 
 BoundSessionRefreshCookieFetcherImpl::BoundSessionRefreshCookieFetcherImpl(
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    WaitForNetworkCallbackHelper& wait_for_network_callback_helper,
     SessionBindingHelper& session_binding_helper,
     const GURL& cookie_url,
     base::flat_set<std::string> cookie_names)
     : url_loader_factory_(std::move(url_loader_factory)),
-      wait_for_network_callback_helper_(wait_for_network_callback_helper),
       session_binding_helper_(session_binding_helper),
       expected_cookie_domain_(cookie_url),
       expected_cookie_names_(std::move(cookie_names)) {}
@@ -72,9 +69,7 @@
   CHECK(!callback_);
   CHECK(callback);
   callback_ = std::move(callback);
-  wait_for_network_callback_helper_->DelayNetworkCall(
-      base::BindOnce(&BoundSessionRefreshCookieFetcherImpl::StartRefreshRequest,
-                     weak_ptr_factory_.GetWeakPtr(), std::nullopt));
+  StartRefreshRequest(/*sec_session_challenge_response=*/std::nullopt);
 }
 
 void BoundSessionRefreshCookieFetcherImpl::StartRefreshRequest(
@@ -334,9 +329,7 @@
     return;
   }
 
-  wait_for_network_callback_helper_->DelayNetworkCall(
-      base::BindOnce(&BoundSessionRefreshCookieFetcherImpl::StartRefreshRequest,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(assertion)));
+  StartRefreshRequest(std::move(assertion));
 }
 
 void BoundSessionRefreshCookieFetcherImpl::OnCookiesAccessed(
diff --git a/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.h b/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.h
index 8828e0b..bfbf97f 100644
--- a/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.h
+++ b/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.h
@@ -23,7 +23,6 @@
 class SharedURLLoaderFactory;
 }  // namespace network
 
-class WaitForNetworkCallbackHelper;
 class SessionBindingHelper;
 
 class BoundSessionRefreshCookieFetcherImpl
@@ -32,7 +31,6 @@
  public:
   BoundSessionRefreshCookieFetcherImpl(
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      WaitForNetworkCallbackHelper& wait_for_network_callback_helper,
       SessionBindingHelper& session_binding_helper,
       const GURL& cookie_url,
       base::flat_set<std::string> cookie_names);
@@ -83,7 +81,6 @@
                  observer) override;
 
   const scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-  const raw_ref<WaitForNetworkCallbackHelper> wait_for_network_callback_helper_;
   const raw_ref<SessionBindingHelper> session_binding_helper_;
 
   // Used to check whether the refresh request has set the required cookie.
diff --git a/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl_unittest.cc b/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl_unittest.cc
index 3f6ce09..7f6fac3 100644
--- a/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl_unittest.cc
+++ b/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl_unittest.cc
@@ -101,8 +101,8 @@
         unexportable_key_service_,
         *unexportable_key_service_.GetWrappedKey(binding_key_id_), kSessionId);
     fetcher_ = std::make_unique<BoundSessionRefreshCookieFetcherImpl>(
-        test_url_loader_factory_.GetSafeWeakWrapper(),
-        wait_for_network_callback_helper_, *session_binding_helper_, kGairaUrl,
+        test_url_loader_factory_.GetSafeWeakWrapper(), *session_binding_helper_,
+        kGairaUrl,
         base::flat_set<std::string>{k1PSIDTSCookieName, k3PSIDTSCookieName});
     UpdateCookieList();
   }
@@ -193,14 +193,12 @@
   UnexportableKeyId binding_key_id_;
   std::unique_ptr<SessionBindingHelper> session_binding_helper_;
   network::TestURLLoaderFactory test_url_loader_factory_;
-  TestWaitForNetworkCallbackHelper wait_for_network_callback_helper_;
   std::unique_ptr<BoundSessionRefreshCookieFetcherImpl> fetcher_;
   net::CookieList cookies_;
   base::HistogramTester histogram_tester_;
 };
 
 TEST_F(BoundSessionRefreshCookieFetcherImplTest, SuccessExpectedCookieSet) {
-  ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
   RefreshTestFuture future;
   fetcher_->Start(future.GetCallback());
 
@@ -229,7 +227,6 @@
 
 TEST_F(BoundSessionRefreshCookieFetcherImplTest,
        SuccessCookiesReportedDelayed) {
-  ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
   RefreshTestFuture future;
   fetcher_->Start(future.GetCallback());
 
@@ -254,7 +251,6 @@
 
 TEST_F(BoundSessionRefreshCookieFetcherImplTest,
        ResultNotReportedOnCookieRead) {
-  ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
   RefreshTestFuture future;
   fetcher_->Start(future.GetCallback());
 
@@ -278,7 +274,6 @@
 }
 
 TEST_F(BoundSessionRefreshCookieFetcherImplTest, CookiesNotReported) {
-  ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
   RefreshTestFuture future;
   fetcher_->Start(future.GetCallback());
 
@@ -301,7 +296,6 @@
 
 TEST_F(BoundSessionRefreshCookieFetcherImplTest,
        CookiesReportedExpectedCookieNotSet) {
-  ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
   RefreshTestFuture future;
   fetcher_->Start(future.GetCallback());
 
@@ -327,7 +321,6 @@
 
 TEST_F(BoundSessionRefreshCookieFetcherImplTest,
        CookiesReportedNotAllExpectedCookiesSet) {
-  ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
   RefreshTestFuture future;
   fetcher_->Start(future.GetCallback());
 
@@ -352,7 +345,6 @@
 }
 
 TEST_F(BoundSessionRefreshCookieFetcherImplTest, FailureNetError) {
-  ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
   RefreshTestFuture future;
   fetcher_->Start(future.GetCallback());
 
@@ -374,7 +366,6 @@
 }
 
 TEST_F(BoundSessionRefreshCookieFetcherImplTest, FailureHttpError) {
-  ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
   RefreshTestFuture future;
   fetcher_->Start(future.GetCallback());
 
@@ -394,7 +385,6 @@
 }
 
 TEST_F(BoundSessionRefreshCookieFetcherImplTest, ChallengeRequired) {
-  ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
   RefreshTestFuture future;
   fetcher_->Start(future.GetCallback());
 
@@ -426,7 +416,6 @@
 
 TEST_F(BoundSessionRefreshCookieFetcherImplTest,
        ChallengeRequiredNonUTF8Characters) {
-  ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
   RefreshTestFuture future;
   fetcher_->Start(future.GetCallback());
 
@@ -438,7 +427,6 @@
 
 TEST_F(BoundSessionRefreshCookieFetcherImplTest,
        BadChallengeHeaderFormatEmpty) {
-  ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
   RefreshTestFuture future;
   fetcher_->Start(future.GetCallback());
   SimulateChallengeRequired("");
@@ -449,7 +437,6 @@
 
 TEST_F(BoundSessionRefreshCookieFetcherImplTest,
        BadChallengeHeaderFormatChallengeMissing) {
-  ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
   RefreshTestFuture future;
   fetcher_->Start(future.GetCallback());
   SimulateChallengeRequired("session_id=12345;");
@@ -460,7 +447,6 @@
 
 TEST_F(BoundSessionRefreshCookieFetcherImplTest,
        BadChallengeHeaderFormatChallengeFieldEmpty) {
-  ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
   RefreshTestFuture future;
   fetcher_->Start(future.GetCallback());
   SimulateChallengeRequired(CreateChallengeHeaderValue(""));
@@ -471,7 +457,6 @@
 
 TEST_F(BoundSessionRefreshCookieFetcherImplTest,
        AssertionRequestsLimitExceeded) {
-  ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
   RefreshTestFuture future;
   fetcher_->Start(future.GetCallback());
 
@@ -497,10 +482,9 @@
   session_binding_helper_ = std::make_unique<SessionBindingHelper>(
       unexportable_key_service_, wrapped_key, kSessionId);
   fetcher_ = std::make_unique<BoundSessionRefreshCookieFetcherImpl>(
-      test_url_loader_factory_.GetSafeWeakWrapper(),
-      wait_for_network_callback_helper_, *session_binding_helper_, kGairaUrl,
+      test_url_loader_factory_.GetSafeWeakWrapper(), *session_binding_helper_,
+      kGairaUrl,
       base::flat_set<std::string>{k1PSIDTSCookieName, k3PSIDTSCookieName});
-  ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
   RefreshTestFuture future;
   fetcher_->Start(future.GetCallback());
 
@@ -540,24 +524,6 @@
             Result::kServerTransientError);
 }
 
-TEST_F(BoundSessionRefreshCookieFetcherImplTest, NetworkDelayed) {
-  wait_for_network_callback_helper_.SetNetworkCallsDelayed(true);
-  RefreshTestFuture future;
-  fetcher_->Start(future.GetCallback());
-  EXPECT_EQ(test_url_loader_factory_.total_requests(), 0u);
-
-  wait_for_network_callback_helper_.SetNetworkCallsDelayed(false);
-  EXPECT_EQ(test_url_loader_factory_.total_requests(), 1u);
-  network::TestURLLoaderFactory::PendingRequest* pending_request =
-      test_url_loader_factory_.GetPendingRequest(0);
-  EXPECT_EQ(pending_request->request.url,
-            "https://accounts.google.com/RotateBoundCookies");
-  test_url_loader_factory_.SimulateResponseForPendingRequest(
-      pending_request->request.url.spec(), "");
-
-  EXPECT_TRUE(future.Wait());
-}
-
 TEST_F(BoundSessionRefreshCookieFetcherImplTest, OnCookiesAccessedRead) {
   EXPECT_FALSE(reported_cookies_notified());
   SimulateOnCookiesAccessed(network::mojom::CookieAccessDetails::Type::kRead);
diff --git a/chrome/browser/sync/sync_service_factory.cc b/chrome/browser/sync/sync_service_factory.cc
index 665413d..5a60daf 100644
--- a/chrome/browser/sync/sync_service_factory.cc
+++ b/chrome/browser/sync/sync_service_factory.cc
@@ -97,6 +97,10 @@
 #include "chrome/browser/sync/android/jni_headers/SyncServiceFactory_jni.h"
 #endif  // BUILDFLAG(IS_ANDROID)
 
+#if BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/android/webapk/webapk_sync_service_factory.h"
+#endif  // BUILDFLAG(IS_ANDROID)
+
 namespace {
 
 std::unique_ptr<KeyedService> BuildSyncService(
@@ -278,6 +282,11 @@
   DependsOn(ThemeServiceFactory::GetInstance());
 #endif  // !BUILDFLAG(IS_ANDROID)
   DependsOn(TrustedVaultServiceFactory::GetInstance());
+#if BUILDFLAG(IS_ANDROID)
+  if (base::FeatureList::IsEnabled(syncer::kWebApkBackupAndRestoreBackend)) {
+    DependsOn(webapk::WebApkSyncServiceFactory::GetInstance());
+  }
+#endif  // BUILDFLAG(IS_ANDROID)
   DependsOn(WebDataServiceFactory::GetInstance());
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/Tab.java
index d6a9678..6e2727d 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/Tab.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -10,6 +10,7 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.Token;
 import org.chromium.base.UserDataHost;
@@ -49,7 +50,8 @@
         /** NavigationHandle for the loaded url. */
         public final @Nullable NavigationHandle navigationHandle;
 
-        LoadUrlResult(
+        @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+        public LoadUrlResult(
                 @TabLoadStatus int tabLoadStatus, @Nullable NavigationHandle navigationHandle) {
             this.tabLoadStatus = tabLoadStatus;
             this.navigationHandle = navigationHandle;
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 0038eefda..dd61e2c 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
@@ -626,8 +626,9 @@
         closeTab(tab);
     }
 
+    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
     @Override
-    protected void resetFilterState() {
+    public void resetFilterState() {
         mShouldRecordUma = false;
         mIsResetting = true;
         Map<Integer, Integer> rootIdToGroupLastShownTabId = new HashMap<>();
@@ -671,23 +672,33 @@
         super.markTabStateInitialized();
         boolean correctOrder = isOrderValid();
         RecordHistogram.recordBooleanHistogram("Tabs.Tasks.OrderValidOnStartup", correctOrder);
+
+        int fixedRootIdCount = fixRootIds();
+        RecordHistogram.recordCount1000Histogram(
+                "TabGroups.NumberOfRootIdsFixed", fixedRootIdCount);
     }
 
     /**
-     * Checks whether the order of the tabs in the {@link TabModel} respects the invariant of
-     * {@link TabGroupModelFilter} that tabs within a group must be contiguous.
+     * Checks whether the order of the tabs in the {@link TabModel} respects the invariant of {@link
+     * TabGroupModelFilter} that tabs within a group must be contiguous.
      *
-     * Valid order:
-     * Tab 1, Group A
-     * Tab 2, Group A
-     * Tab 3, Group B
+     * <p>Valid order:
      *
-     * Invalid order:
-     * Tab 1, Group A
-     * Tab 2, Group B
-     * Tab 3, Group A
+     * <ul>
+     *   <li>Tab 1, Group A
+     *   <li>Tab 2, Group A
+     *   <li>Tab 3, Group B
+     * </ul>
+     *
+     * <p>Invalid order:
+     *
+     * <ul>
+     *   <li>Tab 1, Group A
+     *   <li>Tab 2, Group B
+     *   <li>Tab 3, Group A
+     * </ul>
      */
-    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+    @VisibleForTesting
     public boolean isOrderValid() {
         HashSet<Integer> processedRootIds = new HashSet<>();
         int lastRootId = Tab.INVALID_TAB_ID;
@@ -706,6 +717,35 @@
         return true;
     }
 
+    /**
+     * Fixes root identifiers to guarantee a group's root identifier is the tab id of one of its
+     * tabs.
+     *
+     * @return the number of groups that had to be fixed.
+     */
+    @VisibleForTesting
+    public int fixRootIds() {
+        int fixedRootIdCount = 0;
+        TabModel model = getTabModel();
+        for (Map.Entry<Integer, TabGroup> entry : mRootIdToGroupMap.entrySet()) {
+            int rootId = entry.getKey();
+            TabGroup group = entry.getValue();
+            if (group.contains(rootId)) continue;
+
+            int fixedRootId = group.getTabIdOfFirstTab();
+            for (int tabId : group.getTabIdList()) {
+                Tab tab = TabModelUtils.getTabById(model, tabId);
+                setRootId(tab, fixedRootId);
+            }
+            fixedRootIdCount++;
+        }
+
+        if (fixedRootIdCount != 0) {
+            resetFilterState();
+        }
+        return fixedRootIdCount;
+    }
+
     @Override
     public int getValidPosition(Tab tab, int proposedPosition) {
         final int parentId = getParentId(tab);
diff --git a/chrome/browser/tab_group/javatests/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupsTest.java b/chrome/browser/tab_group/javatests/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupsTest.java
index 8ec3339..ade9d78b 100644
--- a/chrome/browser/tab_group/javatests/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupsTest.java
+++ b/chrome/browser/tab_group/javatests/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupsTest.java
@@ -128,6 +128,7 @@
         tabs.add(4, tab);
         assertEquals(tabs, getCurrentTabs());
         assertOrderValid(true);
+        assertFixedTabGroupRootIdCount(0);
     }
 
     @Test
@@ -143,6 +144,7 @@
         tabs.add(1, tab);
         assertEquals(tabs, getCurrentTabs());
         assertOrderValid(true);
+        assertFixedTabGroupRootIdCount(0);
     }
 
     @Test
@@ -158,6 +160,7 @@
         tabs.add(4, tab);
         assertEquals(tabs, getCurrentTabs());
         assertOrderValid(true);
+        assertFixedTabGroupRootIdCount(0);
     }
 
     @Test
@@ -173,6 +176,7 @@
         tabs.add(2, tab);
         assertEquals(tabs, getCurrentTabs());
         assertOrderValid(true);
+        assertFixedTabGroupRootIdCount(0);
     }
 
     @Test
@@ -191,6 +195,7 @@
         tabs.add(tab2);
         assertEquals(tabs, getCurrentTabs());
         assertOrderValid(false);
+        assertFixedTabGroupRootIdCount(0);
     }
 
     @Test
@@ -212,6 +217,7 @@
         tabs.add(3, tab5);
         assertEquals(tabs, getCurrentTabs());
         assertOrderValid(false);
+        assertFixedTabGroupRootIdCount(0);
     }
 
     @Test
@@ -233,6 +239,47 @@
         tabs.add(2, tab1);
         assertEquals(tabs, getCurrentTabs());
         assertOrderValid(true);
+        assertFixedTabGroupRootIdCount(0);
+    }
+
+    @Test
+    @SmallTest
+    public void testFixTabGroupRootIds() {
+        prepareTabs(Arrays.asList(new Integer[] {3, 2, 1}));
+        List<Tab> tabs = getCurrentTabs();
+
+        // Tab 0
+        // Tab 1, 2, 3
+        // Tab 4, 5
+        // Tab 6
+        Tab tab0 = tabs.get(0);
+        Tab tab1 = tabs.get(1);
+        Tab tab2 = tabs.get(2);
+        Tab tab3 = tabs.get(3);
+        Tab tab4 = tabs.get(4);
+        Tab tab5 = tabs.get(5);
+        Tab tab6 = tabs.get(6);
+
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    tab0.setRootId(tab6.getId());
+                    tab1.setRootId(tab0.getId());
+                    tab2.setRootId(tab0.getId());
+                    tab3.setRootId(tab0.getId());
+                    tab4.setRootId(tab5.getId());
+                    tab5.setRootId(tab5.getId());
+                    tab6.setRootId(tab1.getId());
+                    mTabGroupModelFilter.resetFilterState();
+                });
+
+        assertFixedTabGroupRootIdCount(3);
+
+        assertEquals(tab0.getId(), tab0.getRootId());
+        assertEquals(tab1.getId(), tab1.getRootId());
+        assertEquals(tab1.getId(), tab2.getRootId());
+        assertEquals(tab1.getId(), tab3.getRootId());
+        assertEquals(tab5.getId(), tab4.getRootId());
+        assertEquals(tab5.getId(), tab5.getRootId());
     }
 
     @Test
@@ -287,12 +334,16 @@
     private void assertOrderValid(boolean expectedState) {
         boolean isOrderValid =
                 TestThreadUtils.runOnUiThreadBlockingNoException(
-                        () -> {
-                            return mTabGroupModelFilter.isOrderValid();
-                        });
+                        mTabGroupModelFilter::isOrderValid);
         assertEquals(expectedState, isOrderValid);
     }
 
+    private void assertFixedTabGroupRootIdCount(int expectedCount) {
+        int fixedRootIdCount =
+                TestThreadUtils.runOnUiThreadBlockingNoException(mTabGroupModelFilter::fixRootIds);
+        assertEquals(expectedCount, fixedRootIdCount);
+    }
+
     private void moveTab(Tab tab, int index) {
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> {
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelUtils.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelUtils.java
index a176d26..e9db6a56 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelUtils.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelUtils.java
@@ -6,6 +6,7 @@
 
 import androidx.annotation.NonNull;
 
+import org.chromium.base.Callback;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabSelectionType;
 import org.chromium.content_public.browser.WebContents;
@@ -244,4 +245,31 @@
         }
         return mostRecentTab;
     }
+
+    /**
+     * Executes an {@link Callback} when {@link TabModelSelector#isTabStateInitialized()} becomes
+     * true. This will happen immediately and synchronously if the tab state is already initialized.
+     *
+     * @param tabModelSelector The {@link TabModelSelector} to act on.
+     * @param callback The callback to be run once tab state is initialized, receiving a
+     *     tabModelSelector.
+     */
+    public static void runOnTabStateInitialized(
+            @NonNull TabModelSelector tabModelSelector,
+            @NonNull Callback<TabModelSelector> callback) {
+        if (tabModelSelector.isTabStateInitialized()) {
+            callback.onResult(tabModelSelector);
+        } else {
+            TabModelSelectorObserver observer =
+                    new TabModelSelectorObserver() {
+                        @Override
+                        public void onTabStateInitialized() {
+                            tabModelSelector.removeObserver(this);
+                            callback.onResult(tabModelSelector);
+                        }
+                    };
+
+            tabModelSelector.addObserver(observer);
+        }
+    }
 }
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelUtilsUnitTest.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelUtilsUnitTest.java
index aaa7ce3d..d2db068 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelUtilsUnitTest.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelUtilsUnitTest.java
@@ -5,6 +5,9 @@
 package org.chromium.chrome.browser.tabmodel;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import androidx.test.filters.SmallTest;
@@ -17,6 +20,7 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
+import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
@@ -39,6 +43,7 @@
     @Mock private Profile mIncognitoProfile;
     @Mock private Tab mTab;
     @Mock private Tab mIncognitoTab;
+    @Mock private Callback<TabModelSelector> mTabModelSelectorCallback;
 
     private MockTabModelSelector mTabModelSelector;
     private MockTabModel mTabModel;
@@ -101,4 +106,21 @@
                 mTabModelSelector, UNUSED_TAB_ID, TabSelectionType.FROM_USER, false);
         assertEquals(TabList.INVALID_TAB_INDEX, mTabModel.index());
     }
+
+    @Test
+    @SmallTest
+    public void testRunOnTabStateInitializedCallback() {
+        mTabModelSelector.markTabStateInitialized();
+        TabModelUtils.runOnTabStateInitialized(mTabModelSelector, mTabModelSelectorCallback);
+        verify(mTabModelSelectorCallback, times(1)).onResult(mTabModelSelector);
+    }
+
+    @Test
+    @SmallTest
+    public void testRunOnTabStateInitializedObserver() {
+        TabModelUtils.runOnTabStateInitialized(mTabModelSelector, mTabModelSelectorCallback);
+        verify(mTabModelSelectorCallback, never()).onResult(mTabModelSelector);
+        mTabModelSelector.markTabStateInitialized();
+        verify(mTabModelSelectorCallback, times(1)).onResult(mTabModelSelector);
+    }
 }
diff --git a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_credit_card_controller_unittest.cc b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_credit_card_controller_unittest.cc
index 3cf420b..7ff5ebc 100644
--- a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_credit_card_controller_unittest.cc
+++ b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_credit_card_controller_unittest.cc
@@ -132,8 +132,8 @@
         autofill_manager().touch_to_fill_delegate());
   }
 
-  const std::vector<const CreditCard> credit_cards_ = {test::GetCreditCard(),
-                                                       test::GetCreditCard2()};
+  const std::vector<CreditCard> credit_cards_ = {test::GetCreditCard(),
+                                                 test::GetCreditCard2()};
   std::unique_ptr<MockTouchToFillCreditCardViewImpl> mock_view_;
 
   void OnBeforeAskForValuesToFill() {
diff --git a/chrome/browser/tpcd/metadata/updater_service.cc b/chrome/browser/tpcd/metadata/updater_service.cc
index 52529cb..7bf3cd1f 100644
--- a/chrome/browser/tpcd/metadata/updater_service.cc
+++ b/chrome/browser/tpcd/metadata/updater_service.cc
@@ -57,7 +57,8 @@
     base::Value value(ContentSetting::CONTENT_SETTING_ALLOW);
 
     tpcd_metadata_grants.emplace_back(primary_pattern, secondary_pattern,
-                                      std::move(value), std::string(), false);
+                                      std::move(value), metadata_entry.source(),
+                                      false);
   }
 
   cookie_settings_->SetContentSettingsFor3pcdMetadataGrants(
diff --git a/chrome/browser/tpcd/metadata/updater_service_browsertest.cc b/chrome/browser/tpcd/metadata/updater_service_browsertest.cc
index 3f1eb739..ea2fbfe 100644
--- a/chrome/browser/tpcd/metadata/updater_service_browsertest.cc
+++ b/chrome/browser/tpcd/metadata/updater_service_browsertest.cc
@@ -238,6 +238,8 @@
   MockComponentInstallation(metadata);
 
   ASSERT_EQ(GetCookieSettings()->GetTpcdMetadataGrants().size(), 1u);
+  ASSERT_EQ(GetCookieSettings()->GetTpcdMetadataGrants().front().source,
+            Parser::kSourceTest);
   EXPECT_TRUE(GetCookieSettings()->IsFullCookieAccessAllowed(
       kEmbedded, net::SiteForCookies(), kEmbedder, {}));
 }
diff --git a/chrome/browser/translate/chrome_translate_client.cc b/chrome/browser/translate/chrome_translate_client.cc
index b515aad..01a06d1 100644
--- a/chrome/browser/translate/chrome_translate_client.cc
+++ b/chrome/browser/translate/chrome_translate_client.cc
@@ -95,7 +95,7 @@
 bool IsMessageUISnackbarEnabled() {
   constexpr base::FeatureParam<bool> kIsSnackbarEnabled(
       &translate::kTranslateMessageUI,
-      translate::kTranslateMessageUISnackbarParam, false);
+      translate::kTranslateMessageUISnackbarParam, true);
   return kIsSnackbarEnabled.Get();
 }
 
diff --git a/chrome/browser/translate/translate_manager_render_view_host_android_unittest.cc b/chrome/browser/translate/translate_manager_render_view_host_android_unittest.cc
index 657716c..e2abfdc 100644
--- a/chrome/browser/translate/translate_manager_render_view_host_android_unittest.cc
+++ b/chrome/browser/translate/translate_manager_render_view_host_android_unittest.cc
@@ -20,6 +20,7 @@
 #include "components/infobars/content/content_infobar_manager.h"
 #include "components/infobars/core/infobar.h"
 #include "components/infobars/core/infobar_manager.h"
+#include "components/translate/content/android/translate_message.h"
 #include "components/translate/content/browser/content_translate_driver.h"
 #include "components/translate/content/common/translate.mojom.h"
 #include "components/translate/core/browser/translate_infobar_delegate.h"
@@ -135,6 +136,12 @@
   if (TranslateService::IsTranslateBubbleEnabled())
     return;
 
+  // TODO: https://crbug.com/40213244 - update this test to work with
+  // Translate Message UI when removing this feature flag.
+  if (base::FeatureList::IsEnabled(translate::kTranslateMessageUI)) {
+    return;
+  }
+
   GURL url("http://www.google.com");
   // We should not have a translate infobar.
   ASSERT_TRUE(GetTranslateInfoBar() == NULL);
@@ -155,6 +162,12 @@
   if (TranslateService::IsTranslateBubbleEnabled())
     return;
 
+  // TODO: https://crbug.com/40213244 - update this test to work with
+  // Translate Message UI when removing this feature flag.
+  if (base::FeatureList::IsEnabled(translate::kTranslateMessageUI)) {
+    return;
+  }
+
   GURL url("http://www.google.com");
   // We should not have a translate infobar.
   ASSERT_TRUE(GetTranslateInfoBar() == NULL);
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 8ef27f6..82003ffa 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3096,6 +3096,8 @@
       "webui/ash/edu_coexistence/edu_coexistence_state_tracker.h",
       "webui/ash/emoji/emoji_page_handler.cc",
       "webui/ash/emoji/emoji_page_handler.h",
+      "webui/ash/emoji/emoji_search_proxy.cc",
+      "webui/ash/emoji/emoji_search_proxy.h",
       "webui/ash/emoji/emoji_ui.cc",
       "webui/ash/emoji/emoji_ui.h",
       "webui/ash/emoji/gif_tenor_api_fetcher.cc",
@@ -3803,6 +3805,8 @@
       "//chromeos/ash/components/disks",
       "//chromeos/ash/components/drivefs",
       "//chromeos/ash/components/drivefs/mojom:mojom",
+      "//chromeos/ash/components/emoji",
+      "//chromeos/ash/components/emoji:mojo_bindings",
       "//chromeos/ash/components/emoji:resources",
       "//chromeos/ash/components/fwupd",
       "//chromeos/ash/components/heatmap",
diff --git a/chrome/browser/ui/android/appmenu/internal/java/res/values/dimens.xml b/chrome/browser/ui/android/appmenu/internal/java/res/values/dimens.xml
index 6c76124..d2ff16a3 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/res/values/dimens.xml
+++ b/chrome/browser/ui/android/appmenu/internal/java/res/values/dimens.xml
@@ -19,4 +19,8 @@
 
     <!-- Menu action bar bg elevation -->
     <dimen name="menu_action_bar_bg_elev">@dimen/default_elevation_1</dimen>
+
+    <!-- Menu item icon badge dimensions. -->
+    <dimen name="menu_item_icon_badge_size">8dp</dimen>
+    <dimen name="menu_item_icon_badge_border_size">2dp</dimen>
 </resources>
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinder.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinder.java
index 1e0c6a80..bd82c15 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinder.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinder.java
@@ -4,6 +4,14 @@
 
 package org.chromium.chrome.browser.ui.appmenu;
 
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
 import android.view.View;
@@ -58,18 +66,29 @@
         } else if (key == AppMenuItemProperties.ICON) {
             Drawable icon = model.get(AppMenuItemProperties.ICON);
             ChromeImageView imageView = (ChromeImageView) view.findViewById(R.id.menu_item_icon);
-            imageView.setImageDrawable(icon);
-            imageView.setVisibility(icon == null ? View.GONE : View.VISIBLE);
 
-            // tint the icon
             @ColorRes int colorResId = model.get(AppMenuItemProperties.ICON_COLOR_RES);
             if (colorResId == 0) {
                 // If there is no color assigned to the icon, use the default color.
                 colorResId = R.color.default_icon_color_secondary_tint_list;
             }
-            ImageViewCompat.setImageTintList(
-                    imageView,
-                    AppCompatResources.getColorStateList(imageView.getContext(), colorResId));
+            ColorStateList tintList =
+                    AppCompatResources.getColorStateList(imageView.getContext(), colorResId);
+
+            if (model.get(AppMenuItemProperties.ICON_SHOW_BADGE)) {
+                // Draw the icon with a red badge on top.
+                icon = drawIconWithBadge(imageView.getContext(), icon, colorResId);
+                // `colorResId` has already been applied by `drawIconWithBadge` and thus, passing
+                // `tintList` is not required.
+                // Note that tint is set to null to clear any tint previously set via XML.
+                tintList = null;
+            }
+
+            imageView.setImageDrawable(icon);
+            imageView.setVisibility(icon == null ? View.GONE : View.VISIBLE);
+
+            // tint the icon
+            ImageViewCompat.setImageTintList(imageView, tintList);
         } else if (key == AppMenuItemProperties.CLICK_HANDLER) {
             view.setOnClickListener(
                     v -> model.get(AppMenuItemProperties.CLICK_HANDLER).onItemClick(model));
@@ -253,4 +272,59 @@
         // Menu items may be hidden by command line flags before they get to this point.
         button.setVisibility(View.VISIBLE);
     }
+
+    /**
+     * Draws a badge (a red dot) on icon.
+     *
+     * @param context The activity context.
+     * @param icon The icon to draw the badge on.
+     * @param iconColorResId The resounce id of the color to color the icon with.
+     * @return A new drawable that portrays a badge on the passed icon.
+     */
+    // TODO(crbug.com/1503649): Consider moving the following to UiUtils or somewhere re-usable.
+    private static Drawable drawIconWithBadge(
+            Context context, Drawable icon, @ColorRes int iconColorResId) {
+        if (icon == null || icon.getIntrinsicWidth() <= 0 || icon.getIntrinsicHeight() <= 0) {
+            return icon;
+        }
+
+        int width = icon.getIntrinsicWidth();
+        int height = icon.getIntrinsicHeight();
+
+        // Create new drawable.
+        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+
+        Canvas canvas = new Canvas(bitmap);
+        icon.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+        icon.draw(canvas);
+
+        // Color the icon.
+        canvas.drawColor(context.getColor(iconColorResId), PorterDuff.Mode.SRC_IN);
+
+        int badgeRadius =
+                context.getResources().getDimensionPixelSize(R.dimen.menu_item_icon_badge_size) / 2;
+        int badgeCenterX = width - badgeRadius;
+        int badgeCenterY = height / 2 - badgeRadius;
+
+        // Cut a transparent hole through the background icon. This will serve as a border to
+        // the badge being overlaid.
+        Paint hole = new Paint();
+        hole.setAntiAlias(true);
+        hole.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+        canvas.drawCircle(
+                badgeCenterX,
+                badgeCenterY,
+                badgeRadius
+                        + context.getResources()
+                                .getDimensionPixelSize(R.dimen.menu_item_icon_badge_border_size),
+                hole);
+
+        // Draw the red badge.
+        Paint badge = new Paint();
+        hole.setAntiAlias(true);
+        badge.setColor(context.getColor(R.color.default_red));
+        canvas.drawCircle(badgeCenterX, badgeCenterY, badgeRadius, badge);
+
+        return new BitmapDrawable(context.getResources(), bitmap);
+    }
 }
diff --git a/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemProperties.java b/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemProperties.java
index 04e9a07..0cc1b1c6b 100644
--- a/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemProperties.java
+++ b/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemProperties.java
@@ -50,6 +50,10 @@
     /** The menu item icon's color resource value. */
     public static final WritableIntPropertyKey ICON_COLOR_RES = new WritableIntPropertyKey();
 
+    /** Whether to show a badge on the menu item icon. */
+    public static final WritableBooleanPropertyKey ICON_SHOW_BADGE =
+            new WritableBooleanPropertyKey();
+
     /** The the menu item's position in the menu. */
     static final WritableIntPropertyKey POSITION = new WritableIntPropertyKey();
 
@@ -90,6 +94,7 @@
                 CHECKED,
                 ICON,
                 ICON_COLOR_RES,
+                ICON_SHOW_BADGE,
                 POSITION,
                 SUPPORT_ENTER_ANIMATION,
                 CLICK_HANDLER,
diff --git a/chrome/browser/ui/android/omnibox/BUILD.gn b/chrome/browser/ui/android/omnibox/BUILD.gn
index f6f2462..f3bb916 100644
--- a/chrome/browser/ui/android/omnibox/BUILD.gn
+++ b/chrome/browser/ui/android/omnibox/BUILD.gn
@@ -74,6 +74,7 @@
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfo.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListManager.java",
+    "java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxLoadUrlParams.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdown.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownAdapter.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownEmbedder.java",
@@ -414,6 +415,7 @@
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/CachedZeroSuggestionsManagerUnitTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilderUnitTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListManagerUnitTest.java",
+    "java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxLoadUrlParamsUnitTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownUnitTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/PreWarmingRecycledViewPoolTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/RecyclerViewSelectionControllerUnitTest.java",
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
index 9eca9b4c..51af3f33b 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
@@ -37,7 +37,7 @@
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteControllerProvider;
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteCoordinator;
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteDelegate;
-import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteDelegate.AutocompleteLoadCallback;
+import org.chromium.chrome.browser.omnibox.suggestions.OmniboxLoadUrlParams;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionsDropdownScrollListener;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.BasicSuggestionProcessor.BookmarkState;
 import org.chromium.chrome.browser.omnibox.suggestions.history_clusters.HistoryClustersProcessor.OpenHistoryClustersDelegate;
@@ -510,43 +510,8 @@
     }
 
     @Override
-    public void loadUrl(String url, int transition, long inputStart, boolean openInNewTab) {
-        mLocationBarMediator.loadUrl(url, transition, inputStart, openInNewTab);
-    }
-
-    @Override
-    public void loadUrl(
-            String url,
-            int transition,
-            long inputStart,
-            boolean openInNewTab,
-            @Nullable AutocompleteLoadCallback callback) {
-        mLocationBarMediator.loadUrl(url, transition, inputStart, openInNewTab, callback);
-    }
-
-    @Override
-    public void loadUrlWithPostData(
-            String url, int transition, long inputStart, String postDataType, byte[] postData) {
-        mLocationBarMediator.loadUrlWithPostData(
-                url, transition, inputStart, postDataType, postData, /* openInNewTab= */ false);
-    }
-
-    @Override
-    public void loadUrlWithPostData(
-            String url,
-            int transition,
-            long inputStart,
-            String postDataType,
-            byte[] postData,
-            @Nullable AutocompleteLoadCallback callback) {
-        mLocationBarMediator.loadUrlWithPostData(
-                url,
-                transition,
-                inputStart,
-                postDataType,
-                postData,
-                /* openInNewTab= */ false,
-                callback);
+    public void loadUrl(OmniboxLoadUrlParams omniboxLoadUrlParams) {
+        mLocationBarMediator.loadUrl(omniboxLoadUrlParams);
     }
 
     @Override
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
index 31f2c13..a2079bf 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
@@ -51,7 +51,7 @@
 import org.chromium.chrome.browser.omnibox.status.StatusCoordinator;
 import org.chromium.chrome.browser.omnibox.styles.OmniboxResourceProvider;
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteCoordinator;
-import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteDelegate.AutocompleteLoadCallback;
+import org.chromium.chrome.browser.omnibox.suggestions.OmniboxLoadUrlParams;
 import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
 import org.chromium.chrome.browser.prefetch.settings.PreloadPagesSettingsBridge;
 import org.chromium.chrome.browser.prefetch.settings.PreloadPagesState;
@@ -515,38 +515,7 @@
                 mAutocompleteCoordinator.getSuggestionCount() != 0);
     }
 
-    /* package */ void loadUrl(String url, int transition, long inputStart, boolean openInNewTab) {
-        loadUrl(url, transition, inputStart, openInNewTab, null);
-    }
-
-    /* package */ void loadUrl(
-            String url,
-            int transition,
-            long inputStart,
-            boolean openInNewTab,
-            @Nullable AutocompleteLoadCallback callback) {
-        loadUrlWithPostData(url, transition, inputStart, null, null, openInNewTab, callback);
-    }
-
-    /* package */ void loadUrlWithPostData(
-            String url,
-            int transition,
-            long inputStart,
-            String postDataType,
-            byte[] postData,
-            boolean openInNewTab) {
-        loadUrlWithPostData(
-                url, transition, inputStart, postDataType, postData, openInNewTab, null);
-    }
-
-    /* package */ void loadUrlWithPostData(
-            String url,
-            int transition,
-            long inputStart,
-            String postDataType,
-            byte[] postData,
-            boolean openInNewTab,
-            @Nullable AutocompleteLoadCallback callback) {
+    /* package */ void loadUrl(OmniboxLoadUrlParams omniboxLoadUrlParams) {
         assert mLocationBarDataProvider != null;
         Tab currentTab = mLocationBarDataProvider.getTab();
 
@@ -556,33 +525,36 @@
 
         // TODO(crbug.com/1085812): Should be taking a full loaded LoadUrlParams.
         if (mOverrideUrlLoadingDelegate.willHandleLoadUrlWithPostData(
-                url,
-                transition,
-                inputStart,
-                postDataType,
-                postData,
+                omniboxLoadUrlParams.url,
+                omniboxLoadUrlParams.transitionType,
+                omniboxLoadUrlParams.inputStartTimestamp,
+                omniboxLoadUrlParams.postDataType,
+                omniboxLoadUrlParams.postData,
                 mLocationBarDataProvider.isIncognito())) {
             return;
         }
 
+        String url = omniboxLoadUrlParams.url;
         if (currentTab != null) {
             boolean isCurrentTabNtpUrl = UrlUtilities.isNtpUrl(currentTab.getUrl());
             if (currentTab.isNativePage() || isCurrentTabNtpUrl) {
                 mOmniboxUma.recordNavigationOnNtp(
-                        url, transition, !currentTab.isIncognito() && isCurrentTabNtpUrl);
+                        omniboxLoadUrlParams.url,
+                        omniboxLoadUrlParams.transitionType,
+                        !currentTab.isIncognito() && isCurrentTabNtpUrl);
                 // Passing in an empty string should not do anything unless the user is at the NTP.
                 // Since the NTP has no url, pressing enter while clicking on the URL bar should
                 // refresh the page as it does when you click and press enter on any other site.
                 if (url.isEmpty()) url = currentTab.getUrl().getSpec();
             }
 
-            if (callback != null) {
+            if (omniboxLoadUrlParams.callback != null) {
                 currentTab.addObserver(
                         new EmptyTabObserver() {
                             @Override
                             public void onLoadUrl(
                                     Tab tab, LoadUrlParams params, LoadUrlResult loadUrlResult) {
-                                callback.onLoadUrl(params, loadUrlResult);
+                                omniboxLoadUrlParams.callback.onLoadUrl(params, loadUrlResult);
                                 tab.removeObserver(this);
                             }
                         });
@@ -597,12 +569,13 @@
                     TimingMetric.shortUptime("Android.Omnibox.SetGeolocationHeadersTime")) {
                 loadUrlParams.setVerbatimHeaders(GeolocationHeader.getGeoHeader(url, currentTab));
             }
-            loadUrlParams.setTransitionType(transition | PageTransition.FROM_ADDRESS_BAR);
-            if (inputStart != 0) {
-                loadUrlParams.setInputStartTimestamp(inputStart);
+            loadUrlParams.setTransitionType(
+                    omniboxLoadUrlParams.transitionType | PageTransition.FROM_ADDRESS_BAR);
+            if (omniboxLoadUrlParams.inputStartTimestamp != 0) {
+                loadUrlParams.setInputStartTimestamp(omniboxLoadUrlParams.inputStartTimestamp);
             }
 
-            if (!TextUtils.isEmpty(postDataType)) {
+            if (!TextUtils.isEmpty(omniboxLoadUrlParams.postDataType)) {
                 StringBuilder headers = new StringBuilder();
                 String prevHeader = loadUrlParams.getVerbatimHeaders();
                 if (prevHeader != null && !prevHeader.isEmpty()) {
@@ -611,16 +584,18 @@
                 }
 
                 headers.append("Content-Type: ");
-                headers.append(postDataType);
+                headers.append(omniboxLoadUrlParams.postDataType);
 
                 loadUrlParams.setVerbatimHeaders(headers.toString());
             }
 
-            if (postData != null && postData.length != 0) {
-                loadUrlParams.setPostData(ResourceRequestBody.createFromBytes(postData));
+            if (omniboxLoadUrlParams.postData != null
+                    && omniboxLoadUrlParams.postData.length != 0) {
+                loadUrlParams.setPostData(
+                        ResourceRequestBody.createFromBytes(omniboxLoadUrlParams.postData));
             }
 
-            if (openInNewTab
+            if (omniboxLoadUrlParams.openInNewTab
                     && mTabModelSelectorSupplier != null
                     && mTabModelSelectorSupplier.get() != null) {
                 mTabModelSelectorSupplier
@@ -635,7 +610,8 @@
             }
             RecordUserAction.record("MobileOmniboxUse");
         }
-        mLocaleManager.recordLocaleBasedSearchMetrics(false, url, transition);
+        mLocaleManager.recordLocaleBasedSearchMetrics(
+                false, url, omniboxLoadUrlParams.transitionType);
 
         PostTask.postTask(TaskTraits.UI_USER_VISIBLE, () -> focusCurrentTab());
     }
@@ -1386,7 +1362,10 @@
                 mTemplateUrlServiceSupplier.get().getUrlForSearchQuery(query, searchParams);
 
         if (!TextUtils.isEmpty(queryUrl)) {
-            loadUrl(queryUrl, PageTransition.GENERATED, 0, /* openInNewTab= */ false);
+            loadUrl(
+                    new OmniboxLoadUrlParams.Builder(queryUrl, PageTransition.GENERATED)
+                            .setOpenInNewTab(false)
+                            .build());
         } else {
             setSearchQuery(query);
         }
@@ -1432,7 +1411,10 @@
 
     @Override
     public void loadUrlFromVoice(String url) {
-        loadUrl(url, PageTransition.TYPED, 0, /* openInNewTab= */ false);
+        loadUrl(
+                new OmniboxLoadUrlParams.Builder(url, PageTransition.TYPED)
+                        .setOpenInNewTab(false)
+                        .build());
     }
 
     @Override
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java
index 3353a09..8a7abdd 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java
@@ -72,6 +72,7 @@
 import org.chromium.chrome.browser.omnibox.status.StatusCoordinator;
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteCoordinator;
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteDelegate.AutocompleteLoadCallback;
+import org.chromium.chrome.browser.omnibox.suggestions.OmniboxLoadUrlParams;
 import org.chromium.chrome.browser.omnibox.test.R;
 import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
 import org.chromium.chrome.browser.prefetch.settings.PreloadPagesSettingsBridge;
@@ -375,7 +376,10 @@
         mMediator.onFinishNativeInitialization();
 
         doReturn(mTab).when(mLocationBarDataProvider).getTab();
-        mMediator.loadUrl(TEST_URL, PageTransition.TYPED, 0, false);
+        mMediator.loadUrl(
+                new OmniboxLoadUrlParams.Builder(TEST_URL, PageTransition.TYPED)
+                        .setOpenInNewTab(false)
+                        .build());
 
         verify(mTab).loadUrl(mLoadUrlParamsCaptor.capture());
         assertEquals(TEST_URL, mLoadUrlParamsCaptor.getValue().getUrl());
@@ -389,7 +393,11 @@
         mMediator.onFinishNativeInitialization();
 
         doReturn(mTab).when(mLocationBarDataProvider).getTab();
-        mMediator.loadUrl(TEST_URL, PageTransition.TYPED, 0, false, mAutocompleteLoadCallback);
+        mMediator.loadUrl(
+                new OmniboxLoadUrlParams.Builder(TEST_URL, PageTransition.TYPED)
+                        .setOpenInNewTab(false)
+                        .setAutocompleteLoadCallback(mAutocompleteLoadCallback)
+                        .build());
 
         verify(mTab).loadUrl(mLoadUrlParamsCaptor.capture());
         assertEquals(TEST_URL, mLoadUrlParamsCaptor.getValue().getUrl());
@@ -411,7 +419,10 @@
 
         doReturn(mTab).when(mLocationBarDataProvider).getTab();
         doReturn(data).when(mResourceRequestBodyJni).createResourceRequestBodyFromBytes(any());
-        mMediator.loadUrlWithPostData(TEST_URL, PageTransition.TYPED, 0, text, data, false);
+        mMediator.loadUrl(
+                new OmniboxLoadUrlParams.Builder(TEST_URL, PageTransition.TYPED)
+                        .setpostDataAndType(data, text)
+                        .build());
 
         verify(mTab).loadUrl(mLoadUrlParamsCaptor.capture());
         assertEquals(TEST_URL, mLoadUrlParamsCaptor.getValue().getUrl());
@@ -426,7 +437,10 @@
     public void testLoadUrl_NativeNotInitialized() {
         if (BuildConfig.ENABLE_ASSERTS) {
             try {
-                mMediator.loadUrl(TEST_URL, PageTransition.TYPED, 0, false);
+                mMediator.loadUrl(
+                        new OmniboxLoadUrlParams.Builder(TEST_URL, PageTransition.TYPED)
+                                .setOpenInNewTab(false)
+                                .build());
                 throw new Error("Expected an assert to be triggered.");
             } catch (AssertionError e) {
             }
@@ -442,7 +456,10 @@
                 .when(mOverrideUrlLoadingDelegate)
                 .willHandleLoadUrlWithPostData(
                         TEST_URL, PageTransition.TYPED, 0, null, null, false);
-        mMediator.loadUrl(TEST_URL, PageTransition.TYPED, 0, false);
+        mMediator.loadUrl(
+                new OmniboxLoadUrlParams.Builder(TEST_URL, PageTransition.TYPED)
+                        .setOpenInNewTab(false)
+                        .build());
 
         verify(mOverrideUrlLoadingDelegate)
                 .willHandleLoadUrlWithPostData(
@@ -456,7 +473,10 @@
 
         doReturn(mTab).when(mLocationBarDataProvider).getTab();
         doReturn(false).when(mTab).isIncognito();
-        mMediator.loadUrl(TEST_URL, PageTransition.TYPED, 0, true);
+        mMediator.loadUrl(
+                new OmniboxLoadUrlParams.Builder(TEST_URL, PageTransition.TYPED)
+                        .setOpenInNewTab(true)
+                        .build());
 
         verify(mTabModelSelector)
                 .openNewTab(
@@ -1219,10 +1239,16 @@
         assertTrue(UrlUtilities.isNtpUrl(mTab.getUrl()));
         doReturn(false).when(mTab).isIncognito();
         // Test navigating using omnibox.
-        mMediator.loadUrl(TEST_URL, PageTransition.TYPED, 0, false);
+        mMediator.loadUrl(
+                new OmniboxLoadUrlParams.Builder(TEST_URL, PageTransition.TYPED)
+                        .setOpenInNewTab(false)
+                        .build());
         verify(mOmniboxUma, times(1)).recordNavigationOnNtp(TEST_URL, PageTransition.TYPED, true);
         // Test searching using omnibox.
-        mMediator.loadUrl(TEST_URL, PageTransition.GENERATED, 0, false);
+        mMediator.loadUrl(
+                new OmniboxLoadUrlParams.Builder(TEST_URL, PageTransition.GENERATED)
+                        .setOpenInNewTab(false)
+                        .build());
         // The time to be checked for the calling of recordNavigationOnNtp is still 1 here
         // as we verify with the argument PageTransition.GENERATED instead.
         verify(mOmniboxUma, times(1))
@@ -1234,20 +1260,32 @@
         ShadowUrlUtilities.sIsNtp = false;
         assertFalse(UrlUtilities.isNtpUrl(mTab.getUrl()));
         // Test navigating using omnibox.
-        mMediator.loadUrl(TEST_URL, PageTransition.TYPED, 0, false);
+        mMediator.loadUrl(
+                new OmniboxLoadUrlParams.Builder(TEST_URL, PageTransition.TYPED)
+                        .setOpenInNewTab(false)
+                        .build());
         verify(mOmniboxUma, times(1)).recordNavigationOnNtp(TEST_URL, PageTransition.TYPED, true);
         // Test searching using omnibox.
-        mMediator.loadUrl(TEST_URL, PageTransition.GENERATED, 0, false);
+        mMediator.loadUrl(
+                new OmniboxLoadUrlParams.Builder(TEST_URL, PageTransition.GENERATED)
+                        .setOpenInNewTab(false)
+                        .build());
         verify(mOmniboxUma, times(1))
                 .recordNavigationOnNtp(TEST_URL, PageTransition.GENERATED, true);
 
         // Test clicking omnibox on html/rendered web page.
         doReturn(false).when(mTab).isNativePage();
         // Test navigating using omnibox.
-        mMediator.loadUrl(TEST_URL, PageTransition.TYPED, 0, false);
+        mMediator.loadUrl(
+                new OmniboxLoadUrlParams.Builder(TEST_URL, PageTransition.TYPED)
+                        .setOpenInNewTab(false)
+                        .build());
         verify(mOmniboxUma, times(1)).recordNavigationOnNtp(TEST_URL, PageTransition.TYPED, true);
         // Test searching using omnibox.
-        mMediator.loadUrl(TEST_URL, PageTransition.GENERATED, 0, false);
+        mMediator.loadUrl(
+                new OmniboxLoadUrlParams.Builder(TEST_URL, PageTransition.GENERATED)
+                        .setOpenInNewTab(false)
+                        .build());
         verify(mOmniboxUma, times(1))
                 .recordNavigationOnNtp(TEST_URL, PageTransition.GENERATED, true);
 
@@ -1261,10 +1299,16 @@
                 .willHandleLoadUrlWithPostData(
                         TEST_URL, PageTransition.GENERATED, 0, null, null, false);
         // Test navigating using omnibox.
-        mMediator.loadUrl(TEST_URL, PageTransition.TYPED, 0, false);
+        mMediator.loadUrl(
+                new OmniboxLoadUrlParams.Builder(TEST_URL, PageTransition.TYPED)
+                        .setOpenInNewTab(false)
+                        .build());
         verify(mOmniboxUma, times(1)).recordNavigationOnNtp(TEST_URL, PageTransition.TYPED, true);
         // Test searching using omnibox.
-        mMediator.loadUrl(TEST_URL, PageTransition.GENERATED, 0, false);
+        mMediator.loadUrl(
+                new OmniboxLoadUrlParams.Builder(TEST_URL, PageTransition.GENERATED)
+                        .setOpenInNewTab(false)
+                        .build());
         verify(mOmniboxUma, times(1))
                 .recordNavigationOnNtp(TEST_URL, PageTransition.GENERATED, true);
     }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
index a0e756f5..e654883 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
@@ -20,6 +20,7 @@
 import org.chromium.components.omnibox.AutocompleteMatch;
 import org.chromium.components.omnibox.AutocompleteResult;
 import org.chromium.components.omnibox.AutocompleteResult.VerificationPoint;
+import org.chromium.content_public.browser.NavigationHandle;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.url.GURL;
 
@@ -310,6 +311,23 @@
     }
 
     /**
+     * Create a native navigation observser on native side.
+     *
+     * @param navigationHandle The NavigationHandle for the current navigation.
+     * @param match AutocompleteMatch that was selected by the user
+     */
+    void createNavigationObserver(NavigationHandle navigationHandle, AutocompleteMatch match) {
+        if (mNativeController == 0) return;
+        if (!hasValidNativeObjectRef(match, VerificationPoint.SELECT_MATCH)) return;
+
+        AutocompleteControllerJni.get()
+                .createNavigationObserver(
+                        mNativeController,
+                        navigationHandle.nativeNavigationHandlePtr(),
+                        match.getNativeObjectRef());
+    }
+
+    /**
      * Called when the user touches down on a suggestion. Only called for search suggestions.
      *
      * @param match the match that received the touch
@@ -456,5 +474,11 @@
 
         // Create an instance of AutocompleteController associated with the supplied profile.
         long create(AutocompleteController controller, Profile profile, boolean isLowEndDevice);
+
+        // Create a navigation observser.
+        void createNavigationObserver(
+                long nativeAutocompleteControllerAndroid,
+                long mNativeNavigationHandle,
+                long nativeAutocompleteMatch);
     }
 }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteDelegate.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteDelegate.java
index 565961f..31ec9ce 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteDelegate.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteDelegate.java
@@ -4,11 +4,9 @@
 
 package org.chromium.chrome.browser.omnibox.suggestions;
 
-import androidx.annotation.Nullable;
 
 import org.chromium.chrome.browser.tab.Tab.LoadUrlResult;
 import org.chromium.content_public.browser.LoadUrlParams;
-import org.chromium.ui.base.PageTransition;
 
 /** Provides the additional functionality to trigger and interact with autocomplete suggestions. */
 public interface AutocompleteDelegate extends UrlBarDelegate {
@@ -47,68 +45,11 @@
     boolean isKeyboardActive();
 
     /**
-     * Requests that the given URL be loaded in the current tab or in a new tab.
+     * Requests that the given URL be loaded.
      *
-     * @param url The URL to be loaded.
-     * @param transition The transition type associated with the url load.
-     * @param inputStart The time the input started for the load request.
-     * @param openInNewTab Whether the URL will be loaded in a new tab. If {@code true}, the URL
-     *     will be loaded in a new tab. If {@code false}, The URL will be loaded in the current tab.
+     * @param omniboxLoadUrlParams parameters describing the url load.
      */
-    void loadUrl(String url, @PageTransition int transition, long inputStart, boolean openInNewTab);
-
-    /**
-     * Requests that the given URL be loaded in the current tab or in a new tab.
-     *
-     * @param url The URL to be loaded.
-     * @param transition The transition type associated with the url load.
-     * @param inputStart The time the input started for the load request.
-     * @param openInNewTab Whether the URL will be loaded in a new tab. If {@code true}, the URL
-     *     will be loaded in a new tab. If {@code false}, The URL will be loaded in the current tab.
-     * @param callback A callback that will be called when loadUrl is done on a {@link Tab}.
-     */
-    void loadUrl(
-            String url,
-            @PageTransition int transition,
-            long inputStart,
-            boolean openInNewTab,
-            @Nullable AutocompleteLoadCallback callback);
-
-    /**
-     * Requests that the given URL be loaded in the current tab.
-     *
-     * @param url The URL to be loaded.
-     * @param transition The transition type associated with the url load.
-     * @param inputStart The time the input started for the load request.
-     * @param postDataType postData type.
-     * @param postData Post-data to include in the tab URL's request body, ex. bitmap when image
-     *     search.
-     */
-    void loadUrlWithPostData(
-            String url,
-            @PageTransition int transition,
-            long inputStart,
-            String postDataType,
-            byte[] postData);
-
-    /**
-     * Requests that the given URL be loaded in the current tab.
-     *
-     * @param url The URL to be loaded.
-     * @param transition The transition type associated with the url load.
-     * @param inputStart The time the input started for the load request.
-     * @param postDataType postData type.
-     * @param postData Post-data to include in the tab URL's request body, ex. bitmap when image
-     *     search.
-     * @param callback A callback that will be called when loadUrl is done on a {@link Tab}.
-     */
-    void loadUrlWithPostData(
-            String url,
-            @PageTransition int transition,
-            long inputStart,
-            String postDataType,
-            byte[] postData,
-            @Nullable AutocompleteLoadCallback callback);
+    void loadUrl(OmniboxLoadUrlParams omniboxLoadUrlParams);
 
     /**
      * @return Whether the omnibox was focused via the NTP fakebox.
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
index e154df4..8077b98 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -34,6 +34,7 @@
 import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
 import org.chromium.chrome.browser.omnibox.styles.OmniboxResourceProvider;
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteController.OnSuggestionsReceivedListener;
+import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteDelegate.AutocompleteLoadCallback;
 import org.chromium.chrome.browser.omnibox.suggestions.action.OmniboxActionFactoryImpl;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.BasicSuggestionProcessor.BookmarkState;
 import org.chromium.chrome.browser.omnibox.suggestions.history_clusters.HistoryClustersProcessor.OpenHistoryClustersDelegate;
@@ -42,6 +43,7 @@
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.chrome.browser.share.ShareDelegate;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.Tab.LoadUrlResult;
 import org.chromium.chrome.browser.tab.TabSelectionType;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
@@ -54,6 +56,7 @@
 import org.chromium.components.omnibox.action.OmniboxAction;
 import org.chromium.components.omnibox.action.OmniboxActionDelegate;
 import org.chromium.components.search_engines.TemplateUrlService;
+import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.base.PageTransition;
@@ -935,15 +938,32 @@
                 finishInteraction();
             }
 
+            var autocompleteLoadCallback =
+                    new AutocompleteLoadCallback() {
+                        @Override
+                        public void onLoadUrl(LoadUrlParams params, LoadUrlResult loadUrlResult) {
+                            if (loadUrlResult.navigationHandle != null) {
+                                mAutocomplete.createNavigationObserver(
+                                        loadUrlResult.navigationHandle, suggestion);
+                            }
+                        }
+                    };
+
             if (suggestion.getType() == OmniboxSuggestionType.CLIPBOARD_IMAGE) {
-                mDelegate.loadUrlWithPostData(
-                        url.getSpec(),
-                        transition,
-                        inputStart,
-                        suggestion.getPostContentType(),
-                        suggestion.getPostData());
+                mDelegate.loadUrl(
+                        new OmniboxLoadUrlParams.Builder(url.getSpec(), transition)
+                                .setInputStartTimestamp(inputStart)
+                                .setpostDataAndType(
+                                        suggestion.getPostData(), suggestion.getPostContentType())
+                                .setAutocompleteLoadCallback(autocompleteLoadCallback)
+                                .build());
             } else {
-                mDelegate.loadUrl(url.getSpec(), transition, inputStart, openInNewTab);
+                mDelegate.loadUrl(
+                        new OmniboxLoadUrlParams.Builder(url.getSpec(), transition)
+                                .setInputStartTimestamp(inputStart)
+                                .setOpenInNewTab(openInNewTab)
+                                .setAutocompleteLoadCallback(autocompleteLoadCallback)
+                                .build());
             }
 
             if (mClearFocusAfterNavigationAsynchronously) {
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java
index 58dc0a8..ede877c 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java
@@ -5,11 +5,11 @@
 package org.chromium.chrome.browser.omnibox.suggestions;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
@@ -34,6 +34,8 @@
 import org.junit.Test;
 import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -63,6 +65,7 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.Tab.LoadUrlResult;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
 import org.chromium.chrome.browser.tabmodel.TabWindowManager;
@@ -77,6 +80,7 @@
 import org.chromium.components.omnibox.action.OmniboxActionFactoryJni;
 import org.chromium.components.omnibox.suggestions.OmniboxSuggestionUiType;
 import org.chromium.components.search_engines.TemplateUrlService;
+import org.chromium.content_public.browser.NavigationHandle;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
@@ -123,6 +127,9 @@
     private @Mock HistoryClustersProcessor.OpenHistoryClustersDelegate mOpenHistoryClustersDelegate;
     private @Mock OmniboxActionFactoryJni mActionFactoryJni;
     private @Mock TemplateUrlService mTemplateUrlService;
+    private @Mock NavigationHandle mNavigationHandle;
+
+    private @Captor ArgumentCaptor<OmniboxLoadUrlParams> mOmniboxLoadUrlParamsCaptor;
 
     private PropertyModel mListModel;
     private AutocompleteMediator mMediator;
@@ -587,8 +594,44 @@
 
         mMediator.onSuggestionClicked(mSuggestionsList.get(0), 0, url);
         // Verify that the URL is not loaded in a new tab.
-        verify(mAutocompleteDelegate)
-                .loadUrl(eq(url.getSpec()), anyInt(), anyLong(), /*openInNewTab*/ eq(false));
+        verify(mAutocompleteDelegate).loadUrl(mOmniboxLoadUrlParamsCaptor.capture());
+        assertEquals(mOmniboxLoadUrlParamsCaptor.getValue().url, url.getSpec());
+        assertFalse(mOmniboxLoadUrlParamsCaptor.getValue().openInNewTab);
+
+        // Verify the callback.
+        mOmniboxLoadUrlParamsCaptor
+                .getValue()
+                .callback
+                .onLoadUrl(
+                        null,
+                        new LoadUrlResult(Tab.TabLoadStatus.DEFAULT_PAGE_LOAD, mNavigationHandle));
+        verify(mAutocompleteController)
+                .createNavigationObserver(mNavigationHandle, mSuggestionsList.get(0));
+    }
+
+    @Test
+    public void onSuggestionClicked_ClipboardImageSuggestion() {
+        mMediator.setAutocompleteProfile(mProfile);
+        mMediator.onNativeInitialized();
+        mMediator.onOmniboxSessionStateChange(true);
+        var url = new GURL("http://test");
+        var match =
+                AutocompleteMatchBuilder.searchWithType(OmniboxSuggestionType.CLIPBOARD_IMAGE)
+                        .build();
+
+        // Verify that loadUrlWithPostData is called for the clipboard image suggestion.
+        mMediator.onSuggestionClicked(match, 0, url);
+        verify(mAutocompleteDelegate).loadUrl(mOmniboxLoadUrlParamsCaptor.capture());
+        assertEquals(mOmniboxLoadUrlParamsCaptor.getValue().url, url.getSpec());
+
+        // Verify the callback.
+        mOmniboxLoadUrlParamsCaptor
+                .getValue()
+                .callback
+                .onLoadUrl(
+                        null,
+                        new LoadUrlResult(Tab.TabLoadStatus.DEFAULT_PAGE_LOAD, mNavigationHandle));
+        verify(mAutocompleteController).createNavigationObserver(mNavigationHandle, match);
     }
 
     @Test
@@ -611,11 +654,21 @@
         // Simulate profile loaded.
         mMediator.setAutocompleteProfile(mProfile);
         ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
-        verify(mAutocompleteDelegate)
-                .loadUrl(eq(url.getSpec()), anyInt(), anyLong(), /*openInNewTab*/ eq(false));
+        verify(mAutocompleteDelegate).loadUrl(mOmniboxLoadUrlParamsCaptor.capture());
+        assertEquals(mOmniboxLoadUrlParamsCaptor.getValue().url, url.getSpec());
+        assertFalse(mOmniboxLoadUrlParamsCaptor.getValue().openInNewTab);
         verify(mAutocompleteDelegate).clearOmniboxFocus();
         verifyNoMoreInteractions(mAutocompleteDelegate);
 
+        // Verify the callback.
+        mOmniboxLoadUrlParamsCaptor
+                .getValue()
+                .callback
+                .onLoadUrl(
+                        null,
+                        new LoadUrlResult(Tab.TabLoadStatus.DEFAULT_PAGE_LOAD, mNavigationHandle));
+        verify(mAutocompleteController).createNavigationObserver(mNavigationHandle, match);
+
         // Verify no reload on profile change.
         Profile newProfile = mock(Profile.class);
         mMediator.setAutocompleteProfile(newProfile);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxLoadUrlParams.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxLoadUrlParams.java
new file mode 100644
index 0000000..d1b3ed7
--- /dev/null
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxLoadUrlParams.java
@@ -0,0 +1,111 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.omnibox.suggestions;
+
+import androidx.annotation.Nullable;
+
+import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteDelegate.AutocompleteLoadCallback;
+import org.chromium.ui.base.PageTransition;
+
+/** Holds parameters for AutocompleteDelegate.LoadUrl. */
+public class OmniboxLoadUrlParams {
+    public final String url;
+    public final @PageTransition int transitionType;
+    public final long inputStartTimestamp;
+    public final boolean openInNewTab;
+    public final @Nullable byte[] postData;
+    public final @Nullable String postDataType;
+    public final @Nullable AutocompleteLoadCallback callback;
+
+    private OmniboxLoadUrlParams(
+            String url,
+            @PageTransition int transitionType,
+            long inputStartTimestamp,
+            boolean openInNewTab,
+            @Nullable byte[] postData,
+            @Nullable String postDataType,
+            @Nullable AutocompleteLoadCallback callback) {
+        this.url = url;
+        this.transitionType = transitionType;
+        this.inputStartTimestamp = inputStartTimestamp;
+        this.openInNewTab = openInNewTab;
+        this.postData = postData;
+        this.postDataType = postDataType;
+        this.callback = callback;
+    }
+
+    /** The builder class for {@link OmniboxLoadUrlParams}. */
+    public static class Builder {
+        public String url;
+        public @PageTransition int transitionType;
+        public long inputStartTimestamp;
+        public boolean openInNewTab;
+        public @Nullable byte[] postData;
+        public @Nullable String postDataType;
+        public @Nullable AutocompleteLoadCallback callback;
+
+        /**
+         * @param url the URL will be loaded.
+         * @param transitionType One of PAGE_TRANSITION static constants in ContentView.
+         */
+        public Builder(String url, @PageTransition int transitionType) {
+            this.url = url;
+            this.transitionType = transitionType;
+        }
+
+        /**
+         * @param inputStartTimestamp the timestamp of the event in the location bar that triggered
+         *     this URL load.
+         */
+        public Builder setInputStartTimestamp(long inputStartTimestamp) {
+            this.inputStartTimestamp = inputStartTimestamp;
+            return this;
+        }
+
+        /**
+         * Set Whether the URL will be loaded in a new tab..
+         *
+         * @param openInNewTab Whether the URL will be loaded in a new tab..
+         */
+        public Builder setOpenInNewTab(boolean openInNewTab) {
+            this.openInNewTab = openInNewTab;
+            return this;
+        }
+
+        /**
+         * Set the post data of this load, and its type.
+         *
+         * @param postData Post data for this http post load.
+         * @param postDataType Post data type for this http post load.
+         */
+        public Builder setpostDataAndType(byte[] postData, String postDataType) {
+            this.postData = postData;
+            this.postDataType = postDataType;
+            return this;
+        }
+
+        /**
+         * Set the callback of this loa.
+         *
+         * @param callback The callback will be called once the url is loaded.
+         */
+        public Builder setAutocompleteLoadCallback(AutocompleteLoadCallback callback) {
+            this.callback = callback;
+            return this;
+        }
+
+        /** Builds the OmniboxLoadUrlParams. */
+        public OmniboxLoadUrlParams build() {
+            return new OmniboxLoadUrlParams(
+                    url,
+                    transitionType,
+                    inputStartTimestamp,
+                    openInNewTab,
+                    postData,
+                    postDataType,
+                    callback);
+        }
+    }
+}
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxLoadUrlParamsUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxLoadUrlParamsUnitTest.java
new file mode 100644
index 0000000..eeb0f83
--- /dev/null
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxLoadUrlParamsUnitTest.java
@@ -0,0 +1,69 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.omnibox.suggestions;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteDelegate.AutocompleteLoadCallback;
+import org.chromium.chrome.browser.tab.Tab.LoadUrlResult;
+import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.ui.base.PageTransition;
+
+/** Tests for {@link OmniboxLoadUrlParams}. */
+@RunWith(BaseRobolectricTestRunner.class)
+public class OmniboxLoadUrlParamsUnitTest {
+    private static final String TEST_URL = "http://www.example.org";
+
+    @Test
+    @SmallTest
+    public void defaultValue() {
+        OmniboxLoadUrlParams params =
+                new OmniboxLoadUrlParams.Builder(TEST_URL, PageTransition.TYPED).build();
+
+        assertEquals(params.url, TEST_URL);
+        assertEquals(params.transitionType, PageTransition.TYPED);
+        assertEquals(params.inputStartTimestamp, 0L);
+        assertFalse(params.openInNewTab);
+        assertNull(params.postDataType);
+        assertNull(params.postData);
+        assertNull(params.callback);
+    }
+
+    @Test
+    @SmallTest
+    public void setAllValue() {
+        String text = "text";
+        byte[] data = new byte[] {0, 1, 2, 3, 4};
+        AutocompleteLoadCallback callback =
+                new AutocompleteLoadCallback() {
+                    @Override
+                    public void onLoadUrl(LoadUrlParams params, LoadUrlResult loadUrlResult) {}
+                };
+        OmniboxLoadUrlParams params =
+                new OmniboxLoadUrlParams.Builder(TEST_URL, PageTransition.TYPED)
+                        .setInputStartTimestamp(100L)
+                        .setOpenInNewTab(true)
+                        .setpostDataAndType(data, text)
+                        .setAutocompleteLoadCallback(callback)
+                        .build();
+
+        assertEquals(params.url, TEST_URL);
+        assertEquals(params.transitionType, PageTransition.TYPED);
+        assertEquals(params.inputStartTimestamp, 100L);
+        assertTrue(params.openInNewTab);
+        assertEquals(params.postDataType, text);
+        assertEquals(params.postData, data);
+        assertEquals(params.callback, callback);
+    }
+}
diff --git a/chrome/browser/ui/ash/clipboard_history_browsertest.cc b/chrome/browser/ui/ash/clipboard_history_browsertest.cc
index b4c6657..c3ade53 100644
--- a/chrome/browser/ui/ash/clipboard_history_browsertest.cc
+++ b/chrome/browser/ui/ash/clipboard_history_browsertest.cc
@@ -689,16 +689,7 @@
 }
 
 class ClipboardHistoryPasteTypeBrowserTest
-    : public ClipboardHistoryBrowserTest,
-      public ::testing::WithParamInterface<
-          /*web_contents_paste_enabled=*/bool> {
- public:
-  ClipboardHistoryPasteTypeBrowserTest() {
-    scoped_feature_list_.InitWithFeatureState(
-        ash::features::kClipboardHistoryWebContentsPaste,
-        /*web_contents_paste_enabled=*/GetParam());
-  }
-
+    : public ClipboardHistoryBrowserTest {
  protected:
   // ClipboardHistoryBrowserTest:
   void SetUpOnMainThread() override {
@@ -787,16 +778,11 @@
     return std::move(paste_list_value).TakeList();
   }
 
-  base::test::ScopedFeatureList scoped_feature_list_;
   raw_ptr<content::WebContents, DanglingUntriaged> web_contents_ = nullptr;
   int paste_num_ = 1;
 };
 
-INSTANTIATE_TEST_SUITE_P(All,
-                         ClipboardHistoryPasteTypeBrowserTest,
-                         /*web_contents_paste_enabled=*/::testing::Bool());
-
-IN_PROC_BROWSER_TEST_P(ClipboardHistoryPasteTypeBrowserTest,
+IN_PROC_BROWSER_TEST_F(ClipboardHistoryPasteTypeBrowserTest,
                        PlainAndRichTextPastes) {
   using ClipboardHistoryPasteType =
       ash::ClipboardHistoryControllerImpl::ClipboardHistoryPasteType;
@@ -1050,7 +1036,7 @@
 
 // Regression test for crbug.com/1363828 --- verifies that
 // `WebContents::Paste()` works, since that's necessary for the html preview.
-IN_PROC_BROWSER_TEST_P(ClipboardHistoryPasteTypeBrowserTest, PasteCommand) {
+IN_PROC_BROWSER_TEST_F(ClipboardHistoryPasteTypeBrowserTest, PasteCommand) {
   SetClipboardTextAndHtml("A", "<span>A</span>");
   web_contents()->Paste();
   WaitForWebContentsPaste("A", /*paste_plain_text=*/false);
@@ -1131,7 +1117,7 @@
 // The browser test which creates a widget with a textfield during setting-up
 // to help verify the multipaste menu item's response to the gesture tap and
 // the mouse click.
-class ClipboardHistoryTextfieldBrowserTestBase
+class ClipboardHistoryTextfieldBrowserTest
     : public ClipboardHistoryBrowserTest {
  protected:
   // ClipboardHistoryBrowserTest:
@@ -1168,28 +1154,9 @@
   raw_ptr<views::Textfield> textfield_ = nullptr;
 };
 
-class ClipboardHistoryTextfieldBrowserTest
-    : public ClipboardHistoryTextfieldBrowserTestBase,
-      public ::testing::WithParamInterface<
-          /*web_contents_paste_enabled=*/bool> {
- public:
-  ClipboardHistoryTextfieldBrowserTest() {
-    scoped_feature_list_.InitWithFeatureState(
-        ash::features::kClipboardHistoryWebContentsPaste,
-        /*web_contents_paste_enabled=*/GetParam());
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-INSTANTIATE_TEST_SUITE_P(All,
-                         ClipboardHistoryTextfieldBrowserTest,
-                         /*web_contents_paste_enabled=*/::testing::Bool());
-
 // Verifies that the clipboard history menu responses to the gesture tap
 // correctly (https://crbug.com/1142088).
-IN_PROC_BROWSER_TEST_P(ClipboardHistoryTextfieldBrowserTest,
+IN_PROC_BROWSER_TEST_F(ClipboardHistoryTextfieldBrowserTest,
                        VerifyResponseToGestures) {
   base::HistogramTester histogram_tester;
 
@@ -1217,7 +1184,7 @@
 
 // Verifies that the metric to record the count of the consecutive pastes from
 // the clipboard history menu works as expected.
-IN_PROC_BROWSER_TEST_P(ClipboardHistoryTextfieldBrowserTest,
+IN_PROC_BROWSER_TEST_F(ClipboardHistoryTextfieldBrowserTest,
                        VerifyConsecutivePasteMetric) {
   base::HistogramTester histogram_tester;
 
@@ -1233,7 +1200,7 @@
                                       /*expected_bucket_count=*/1);
 }
 
-IN_PROC_BROWSER_TEST_P(ClipboardHistoryTextfieldBrowserTest,
+IN_PROC_BROWSER_TEST_F(ClipboardHistoryTextfieldBrowserTest,
                        ShouldPasteHistoryViaKeyboard) {
   base::HistogramTester histogram_tester;
   // Write some things to the clipboard.
@@ -1303,7 +1270,7 @@
       "Ash.ClipboardHistory.ContextMenu.DisplayFormatPasted", 4);
 }
 
-IN_PROC_BROWSER_TEST_P(ClipboardHistoryTextfieldBrowserTest,
+IN_PROC_BROWSER_TEST_F(ClipboardHistoryTextfieldBrowserTest,
                        ShouldPasteHistoryWhileHoldingDownCommandKey) {
   // Write some things to the clipboard.
   SetClipboardText("A");
@@ -1336,7 +1303,7 @@
   Release(ui::KeyboardCode::VKEY_COMMAND);
 }
 
-IN_PROC_BROWSER_TEST_P(ClipboardHistoryTextfieldBrowserTest,
+IN_PROC_BROWSER_TEST_F(ClipboardHistoryTextfieldBrowserTest,
                        PasteWithLockedScreen) {
   // Write an item to the clipboard.
   SetClipboardText("A");
@@ -1407,7 +1374,7 @@
 
 // The browser test equipped with the custom policy controller.
 class ClipboardHistoryWithMockDLPBrowserTest
-    : public ClipboardHistoryTextfieldBrowserTestBase {
+    : public ClipboardHistoryTextfieldBrowserTest {
  public:
   ClipboardHistoryWithMockDLPBrowserTest()
       : data_transfer_policy_controller_(
@@ -1927,7 +1894,7 @@
 // Base class used to test features that only exist when the Ctrl+V longpress
 // feature is enabled.
 class ClipboardHistoryLongpressEnabledBrowserTest
-    : public ClipboardHistoryTextfieldBrowserTestBase {
+    : public ClipboardHistoryTextfieldBrowserTest {
  public:
   ClipboardHistoryLongpressEnabledBrowserTest() {
     scoped_feature_list_.InitAndEnableFeature(
@@ -1997,7 +1964,7 @@
 // Base class used to test features that only exist when the UI refresh is
 // enabled.
 class ClipboardHistoryRefreshEnabledBrowserTest
-    : public ClipboardHistoryTextfieldBrowserTestBase {
+    : public ClipboardHistoryTextfieldBrowserTest {
  public:
   ClipboardHistoryRefreshEnabledBrowserTest() {
     scoped_feature_list_.InitWithFeatures(
diff --git a/chrome/browser/ui/ash/clipboard_history_controller_delegate_impl.cc b/chrome/browser/ui/ash/clipboard_history_controller_delegate_impl.cc
index 3c1ae24..127a020 100644
--- a/chrome/browser/ui/ash/clipboard_history_controller_delegate_impl.cc
+++ b/chrome/browser/ui/ash/clipboard_history_controller_delegate_impl.cc
@@ -76,9 +76,6 @@
 }
 
 bool ClipboardHistoryControllerDelegateImpl::Paste() const {
-  if (!ash::features::IsClipboardHistoryWebContentsPasteEnabled()) {
-    return false;
-  }
   for (auto* const web_contents : GetAllWebContents()) {
     auto* const focused_web_contents = GetFocusedWebContents(web_contents);
     if (!focused_web_contents) {
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
index 4045cf7..94e4ac3 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
@@ -2694,7 +2694,7 @@
     // Disable contextual nudges to prevent in-app to home nudge from being
     // announced in the ChromeVox test.
     scoped_feature_list_.InitAndDisableFeature(
-        ash::features::kContextualNudges);
+        ash::features::kHideShelfControlsInTabletMode);
     ShelfAppBrowserTest::SetUp();
   }
 
diff --git a/chrome/browser/ui/ash/test_wallpaper_controller.cc b/chrome/browser/ui/ash/test_wallpaper_controller.cc
index 12e3d33..bc5a8100 100644
--- a/chrome/browser/ui/ash/test_wallpaper_controller.cc
+++ b/chrome/browser/ui/ash/test_wallpaper_controller.cc
@@ -218,12 +218,12 @@
 void TestWallpaperController::SetSeaPenWallpaper(
     const AccountId& account_id,
     const ash::SeaPenImage& sea_pen_image,
-    const std::string& query_info,
+    const ash::personalization_app::mojom::SeaPenQueryPtr& query,
     SetWallpaperCallback callback) {
   ++sea_pen_wallpaper_count_;
   wallpaper_info_ = ash::WallpaperInfo();
   wallpaper_info_->type = ash::WallpaperType::kSeaPen;
-  sea_pen_metadata_ = query_info;
+  sea_pen_query_ = query.Clone();
   std::move(callback).Run(/*success=*/true);
 }
 
diff --git a/chrome/browser/ui/ash/test_wallpaper_controller.h b/chrome/browser/ui/ash/test_wallpaper_controller.h
index 0ba20e7..fe6f4e86 100644
--- a/chrome/browser/ui/ash/test_wallpaper_controller.h
+++ b/chrome/browser/ui/ash/test_wallpaper_controller.h
@@ -80,7 +80,9 @@
   const std::optional<ash::WallpaperInfo>& wallpaper_info() const {
     return wallpaper_info_;
   }
-  const std::string& sea_pen_metadata() const { return sea_pen_metadata_; }
+  const ash::personalization_app::mojom::SeaPenQueryPtr& sea_pen_query() const {
+    return sea_pen_query_;
+  }
   int update_current_wallpaper_layout_count() const {
     return update_current_wallpaper_layout_count_;
   }
@@ -154,10 +156,11 @@
                               const std::string& file_name,
                               ash::WallpaperLayout layout,
                               const gfx::ImageSkia& image) override;
-  void SetSeaPenWallpaper(const AccountId& account_id,
-                          const ash::SeaPenImage& sea_pen_image,
-                          const std::string& query_info,
-                          SetWallpaperCallback callback) override;
+  void SetSeaPenWallpaper(
+      const AccountId& account_id,
+      const ash::SeaPenImage& sea_pen_image,
+      const ash::personalization_app::mojom::SeaPenQueryPtr& query,
+      SetWallpaperCallback callback) override;
   void SetSeaPenWallpaperFromFile(const AccountId& account_id,
                                   const base::FilePath& sea_pen_file_path,
                                   SetWallpaperCallback callback) override;
@@ -221,7 +224,7 @@
   int one_shot_wallpaper_count_ = 0;
   int sea_pen_wallpaper_count_ = 0;
   std::optional<ash::WallpaperInfo> wallpaper_info_;
-  std::string sea_pen_metadata_;
+  ash::personalization_app::mojom::SeaPenQueryPtr sea_pen_query_;
   int update_current_wallpaper_layout_count_ = 0;
   std::optional<ash::WallpaperLayout> update_current_wallpaper_layout_layout_;
   DailyGooglePhotosIdCache id_cache_;
diff --git a/chrome/browser/ui/download/download_bubble_row_view_info_unittest.cc b/chrome/browser/ui/download/download_bubble_row_view_info_unittest.cc
index 39c27313..17f551b 100644
--- a/chrome/browser/ui/download/download_bubble_row_view_info_unittest.cc
+++ b/chrome/browser/ui/download/download_bubble_row_view_info_unittest.cc
@@ -192,7 +192,7 @@
             DownloadCommands::Command::KEEP);
 }
 
-TEST_F(DownloadBubbleRowViewInfoTest, InProgressOrCompletedBubbleUIInfo) {
+TEST_F(DownloadBubbleRowViewInfoTest, InProgressOrCompletedInfo) {
   ON_CALL(item(), GetState())
       .WillByDefault(Return(download::DownloadItem::COMPLETE));
   item().NotifyObserversDownloadUpdated();
@@ -231,7 +231,7 @@
   EXPECT_FALSE(info().primary_button_command().has_value());
 }
 
-TEST_F(DownloadBubbleRowViewInfoTest, DangerousWarningBubbleUIInfo) {
+TEST_F(DownloadBubbleRowViewInfoTest, DangerousWarningInfo) {
   ON_CALL(item(), GetState())
       .WillByDefault(Return(download::DownloadItem::COMPLETE));
   const struct DangerTypeTestCase {
@@ -264,7 +264,7 @@
   }
 }
 
-TEST_F(DownloadBubbleRowViewInfoTest, InterruptedBubbleUIInfo) {
+TEST_F(DownloadBubbleRowViewInfoTest, InterruptedInfo) {
   std::vector<download::DownloadInterruptReason> no_retry_interrupt_reasons = {
       download::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE,
       download::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED,
@@ -346,8 +346,7 @@
   }
 }
 
-TEST_F(DownloadBubbleRowViewInfoTest,
-       GetBubbleUIInfoForTailoredWarning_CookieTheft) {
+TEST_F(DownloadBubbleRowViewInfoTest, GetInfoForTailoredWarning_CookieTheft) {
   SetupTailoredWarningForItem(
       download::DOWNLOAD_DANGER_TYPE_DANGEROUS_ACCOUNT_COMPROMISE,
       TailoredVerdict::COOKIE_THEFT, /*adjustments=*/{});
@@ -359,7 +358,7 @@
 }
 
 TEST_F(DownloadBubbleRowViewInfoTest,
-       GetBubbleUIInfoForTailoredWarning_SuspiciousArchive) {
+       GetInfoForTailoredWarning_SuspiciousArchive) {
   SetupTailoredWarningForItem(download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT,
                               TailoredVerdict::SUSPICIOUS_ARCHIVE,
                               /*adjustments=*/{});
@@ -371,7 +370,7 @@
 }
 
 TEST_F(DownloadBubbleRowViewInfoTest,
-       GetBubbleUIInfoForTailoredWarning_AccountInfoStringWithAccount) {
+       GetInfoForTailoredWarning_AccountInfoStringWithAccount) {
   SetupTailoredWarningForItem(
       download::DOWNLOAD_DANGER_TYPE_DANGEROUS_ACCOUNT_COMPROMISE,
       TailoredVerdict::COOKIE_THEFT, {TailoredVerdict::ACCOUNT_INFO_STRING});
@@ -387,7 +386,7 @@
 }
 
 TEST_F(DownloadBubbleRowViewInfoTest,
-       GetBubbleUIInfoForTailoredWarning_AccountInfoStringWithoutAccount) {
+       GetInfoForTailoredWarning_AccountInfoStringWithoutAccount) {
   SetupTailoredWarningForItem(
       download::DOWNLOAD_DANGER_TYPE_DANGEROUS_ACCOUNT_COMPROMISE,
       TailoredVerdict::COOKIE_THEFT, {TailoredVerdict::ACCOUNT_INFO_STRING});
@@ -398,4 +397,15 @@
   EXPECT_TRUE(info().has_subpage());
 }
 
+TEST_F(DownloadBubbleRowViewInfoTest, InsecurePrimaryButtonCommand) {
+  for (const auto& insecure_download_status :
+       {download::DownloadItem::InsecureDownloadStatus::BLOCK,
+        download::DownloadItem::InsecureDownloadStatus::WARN}) {
+    ON_CALL(item(), GetInsecureDownloadStatus())
+        .WillByDefault(Return(insecure_download_status));
+    item().NotifyObserversDownloadUpdated();
+    EXPECT_EQ(info().primary_button_command(), DownloadCommands::Command::KEEP);
+  }
+}
+
 }  // namespace
diff --git a/chrome/browser/ui/performance_controls/tab_resource_usage_collector.cc b/chrome/browser/ui/performance_controls/tab_resource_usage_collector.cc
index c16c5f9..1df38aa 100644
--- a/chrome/browser/ui/performance_controls/tab_resource_usage_collector.cc
+++ b/chrome/browser/ui/performance_controls/tab_resource_usage_collector.cc
@@ -14,11 +14,11 @@
 namespace {
 constexpr base::TimeDelta kTabResourceUsageRefreshInterval = base::Minutes(2);
 
-using performance_manager::resource_attribution::MemorySummaryResult;
-using performance_manager::resource_attribution::PageContext;
-using performance_manager::resource_attribution::QueryBuilder;
-using performance_manager::resource_attribution::QueryResultMap;
-using performance_manager::resource_attribution::ResourceType;
+using resource_attribution::MemorySummaryResult;
+using resource_attribution::PageContext;
+using resource_attribution::QueryBuilder;
+using resource_attribution::QueryResultMap;
+using resource_attribution::ResourceType;
 }  // namespace
 
 TabResourceUsageCollector::TabResourceUsageCollector()
@@ -76,8 +76,7 @@
         result.memory_summary_result;
     if (memory_result.has_value()) {
       content::WebContents* const web_contents =
-          performance_manager::resource_attribution::AsContext<PageContext>(
-              page_context)
+          resource_attribution::AsContext<PageContext>(page_context)
               .GetWebContents();
       if (web_contents) {
         auto* const tab_resource_usage_tab_helper =
diff --git a/chrome/browser/ui/performance_controls/tab_resource_usage_collector.h b/chrome/browser/ui/performance_controls/tab_resource_usage_collector.h
index fc715366..eed8a09 100644
--- a/chrome/browser/ui/performance_controls/tab_resource_usage_collector.h
+++ b/chrome/browser/ui/performance_controls/tab_resource_usage_collector.h
@@ -19,7 +19,7 @@
 }
 
 class TabResourceUsageCollector
-    : public performance_manager::resource_attribution::QueryResultObserver {
+    : public resource_attribution::QueryResultObserver {
  public:
   class Observer : public base::CheckedObserver {
    public:
@@ -38,20 +38,17 @@
   void ImmediatelyRefreshMetrics(content::WebContents* web_contents);
   void ImmediatelyRefreshMetricsForAllTabs();
 
-  // performance_manager::resource_attribution::QueryResultObserver:
+  // resource_attribution::QueryResultObserver:
   void OnResourceUsageUpdated(
-      const performance_manager::resource_attribution::QueryResultMap& results)
-      override;
+      const resource_attribution::QueryResultMap& results) override;
 
  private:
   friend base::NoDestructor<TabResourceUsageCollector>;
 
   TabResourceUsageCollector();
 
-  performance_manager::resource_attribution::ScopedResourceUsageQuery
-      scoped_query_;
-  performance_manager::resource_attribution::ScopedQueryObservation
-      query_observer_{this};
+  resource_attribution::ScopedResourceUsageQuery scoped_query_;
+  resource_attribution::ScopedQueryObservation query_observer_{this};
   base::ObserverList<Observer> observers_;
 };
 
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_mac.mm b/chrome/browser/ui/views/frame/immersive_mode_controller_mac.mm
index 279e471..2df26c2 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_mac.mm
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_mac.mm
@@ -153,6 +153,11 @@
     // controlled fullscreen NSWindow.
     browser_view_->overlay_widget()->Show();
 
+    // Set revealed to be true when entering immersive fullscreen so the toolbar
+    // and bookmarks bar heights are accounted for during the fullscreen
+    // transition.
+    OnImmersiveModeToolbarRevealChanged(true);
+
     // Move top chrome to the overlay view.
     browser_view_->OnImmersiveRevealStarted();
     browser_view_->InvalidateLayout();
diff --git a/chrome/browser/ui/views/media_preview/camera_preview/video_stream_coordinator.cc b/chrome/browser/ui/views/media_preview/camera_preview/video_stream_coordinator.cc
index b52ef39..7a2ae674 100644
--- a/chrome/browser/ui/views/media_preview/camera_preview/video_stream_coordinator.cc
+++ b/chrome/browser/ui/views/media_preview/camera_preview/video_stream_coordinator.cc
@@ -12,15 +12,10 @@
 #include "chrome/browser/ui/views/media_preview/camera_preview/video_stream_view.h"
 #include "content/public/browser/context_factory.h"
 #include "media/capture/video_capture_types.h"
-#include "ui/compositor/compositor.h"
 
 VideoStreamCoordinator::VideoStreamCoordinator(views::View& parent_view) {
   auto* video_stream_view =
       parent_view.AddChildView(std::make_unique<VideoStreamView>());
-
-  video_stream_view->SetRasterContextProvider(
-      content::GetContextFactory()->SharedMainThreadRasterContextProvider());
-
   video_stream_view_tracker_.SetView(video_stream_view);
 }
 
diff --git a/chrome/browser/ui/views/media_preview/camera_preview/video_stream_view.cc b/chrome/browser/ui/views/media_preview/camera_preview/video_stream_view.cc
index d98b11a0..0eb008d 100644
--- a/chrome/browser/ui/views/media_preview/camera_preview/video_stream_view.cc
+++ b/chrome/browser/ui/views/media_preview/camera_preview/video_stream_view.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/media_preview/camera_preview/video_format_comparison.h"
 #include "chrome/grit/generated_resources.h"
+#include "content/public/browser/context_factory.h"
 #include "media/base/video_transformation.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
@@ -25,10 +26,31 @@
       IDS_MEDIA_PREVIEW_VIDEO_STREAM_ACCESSIBLE_NAME));
   SetAccessibleRole(ax::mojom::Role::kImage);
   SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
+
+  raster_context_provider_ =
+      content::GetContextFactory()->SharedMainThreadRasterContextProvider();
+  if (raster_context_provider_) {
+    raster_context_provider_->AddObserver(this);
+  }
 }
 
 VideoStreamView::~VideoStreamView() {
   ClearFrame();
+  if (raster_context_provider_) {
+    raster_context_provider_->RemoveObserver(this);
+  }
+}
+
+void VideoStreamView::OnContextLost() {
+  if (raster_context_provider_) {
+    raster_context_provider_->RemoveObserver(this);
+  }
+
+  raster_context_provider_ =
+      content::GetContextFactory()->SharedMainThreadRasterContextProvider();
+  if (raster_context_provider_) {
+    raster_context_provider_->AddObserver(this);
+  }
 }
 
 void VideoStreamView::ScheduleFramePaint(
diff --git a/chrome/browser/ui/views/media_preview/camera_preview/video_stream_view.h b/chrome/browser/ui/views/media_preview/camera_preview/video_stream_view.h
index fd3e3b0..62afbee 100644
--- a/chrome/browser/ui/views/media_preview/camera_preview/video_stream_view.h
+++ b/chrome/browser/ui/views/media_preview/camera_preview/video_stream_view.h
@@ -5,9 +5,8 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_MEDIA_PREVIEW_CAMERA_PREVIEW_VIDEO_STREAM_VIEW_H_
 #define CHROME_BROWSER_UI_VIEWS_MEDIA_PREVIEW_CAMERA_PREVIEW_VIDEO_STREAM_VIEW_H_
 
-#include <utility>
-
 #include "base/memory/scoped_refptr.h"
+#include "components/viz/common/gpu/context_lost_observer.h"
 #include "components/viz/common/gpu/raster_context_provider.h"
 #include "media/base/video_frame.h"
 #include "media/renderers/paint_canvas_video_renderer.h"
@@ -15,7 +14,7 @@
 #include "ui/views/view.h"
 
 // The camera live video feed view.
-class VideoStreamView : public views::View {
+class VideoStreamView : public views::View, public viz::ContextLostObserver {
   METADATA_HEADER(VideoStreamView, views::View)
 
  public:
@@ -24,10 +23,8 @@
   VideoStreamView& operator=(const VideoStreamView&) = delete;
   ~VideoStreamView() override;
 
-  void SetRasterContextProvider(
-      scoped_refptr<viz::RasterContextProvider> raster_context_provider) {
-    raster_context_provider_ = std::move(raster_context_provider);
-  }
+  // viz::ContextLostObserver.
+  void OnContextLost() override;
 
   void ScheduleFramePaint(scoped_refptr<media::VideoFrame> frame);
   void ClearFrame();
diff --git a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
index 75e322c..723ec753 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
@@ -223,7 +223,9 @@
             std::min(description_width, kMinimumDescriptionWidth) &&
         !OmniboxFieldTrial::IsActionsUISimplificationEnabled()) {
       *description_max_width = 0;
-      // Since we're not going to display the description, the contents can have
+    }
+    if (*description_max_width == 0) {
+      // If we're not going to display the description, the contents can have
       // the space we reserved for the separator.
       available_width += separator_width;
       *contents_max_width = std::min(contents_width, available_width);
diff --git a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view_unittest.cc b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view_unittest.cc
index d089293..a9614b3 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view_unittest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view_unittest.cc
@@ -32,17 +32,6 @@
   EXPECT_EQ(contents_width, contents_max_width);
   EXPECT_EQ(0, description_max_width);
 
-  // Description should be hidden if it's at least 75 pixels wide but doesn't
-  // get 75 pixels of space.
-  contents_width = 300;
-  description_width = 100;
-  available_width = 384;
-  OmniboxMatchCellView::ComputeMatchMaxWidths(
-      contents_width, separator_width, description_width, available_width,
-      false, true, &contents_max_width, &description_max_width);
-  EXPECT_EQ(contents_width, contents_max_width);
-  EXPECT_EQ(0, description_max_width);
-
   // If contents and description are on separate lines, each can take the full
   // available width.
   contents_width = 300;
diff --git a/chrome/browser/ui/views/page_action/pwa_install_view.cc b/chrome/browser/ui/views/page_action/pwa_install_view.cc
index b6008376..5562ba8d 100644
--- a/chrome/browser/ui/views/page_action/pwa_install_view.cc
+++ b/chrome/browser/ui/views/page_action/pwa_install_view.cc
@@ -42,6 +42,7 @@
 #include "chrome/browser/metrics/structured/event_logging_features.h"
 // TODO(crbug.com/1125897): Enable gn check once it handles conditional includes
 #include "components/metrics/structured/structured_events.h"  // nogncheck
+#include "components/metrics/structured/structured_metrics_client.h"  // nogncheck
 #endif
 
 namespace {
@@ -187,9 +188,9 @@
 
 #if BUILDFLAG(IS_CHROMEOS)
   if (base::FeatureList::IsEnabled(metrics::structured::kAppDiscoveryLogging)) {
-    cros_events::AppDiscovery_Browser_OmniboxInstallIconClicked()
-        .SetIPHShown(install_icon_clicked_after_iph_shown_)
-        .Record();
+    metrics::structured::StructuredMetricsClient::Record(
+        std::move(cros_events::AppDiscovery_Browser_OmniboxInstallIconClicked()
+                      .SetIPHShown(install_icon_clicked_after_iph_shown_)));
   }
 #endif
 
diff --git a/chrome/browser/ui/views/permissions/chip/multi_image_container.cc b/chrome/browser/ui/views/permissions/chip/multi_image_container.cc
index 2f24a15..0727058 100644
--- a/chrome/browser/ui/views/permissions/chip/multi_image_container.cc
+++ b/chrome/browser/ui/views/permissions/chip/multi_image_container.cc
@@ -23,12 +23,12 @@
   image->SetCanProcessEventsWithinSubtree(false);
   images_.push_back(image);
 
-  view_ = view_container.get();
+  view_tracker_.SetView(view_container.get());
   return view_container;
 }
 
 void MultiImageContainer::SetImages(
-    const std::vector<const ui::ImageModel>& image_models) {
+    const std::vector<ui::ImageModel>& image_models) {
   if (images_.size() < image_models.size()) {
     AddExtraImages(image_models.size());
   } else if (images_.size() > image_models.size()) {
@@ -41,46 +41,43 @@
 }
 
 void MultiImageContainer::AddExtraImages(const size_t number_of_images) {
-  if (!view_) {
-    return;
-  }
-
-  for (size_t i = images_.size(); i < number_of_images; i++) {
-    auto* image = view_->AddChildView(std::make_unique<views::ImageView>());
-    image->SetCanProcessEventsWithinSubtree(false);
-    images_.push_back(image);
+  if (auto* view = view_tracker_.view(); view) {
+    for (size_t i = images_.size(); i < number_of_images; i++) {
+      auto* image = view->AddChildView(std::make_unique<views::ImageView>());
+      image->SetCanProcessEventsWithinSubtree(false);
+      images_.push_back(image);
+    }
   }
 }
 
 void MultiImageContainer::RemoveExtraImages(const size_t number_of_images) {
-  if (!view_) {
-    return;
+  if (auto* view = view_tracker_.view(); view) {
+    for (size_t i = images_.size() - 1; i >= number_of_images; i--) {
+      view->RemoveChildViewT(std::exchange(images_[i], nullptr));
+    }
+    images_.resize(number_of_images);
   }
-
-  for (size_t i = images_.size() - 1; i >= number_of_images; i--) {
-    view_->RemoveChildViewT(std::exchange(images_[i], nullptr));
-  }
-  images_.resize(number_of_images);
 }
 
 void MultiImageContainer::SetImage(size_t index,
                                    const ui::ImageModel& image_model) {
-  SetImage(index, image_model.Rasterize(view_->GetColorProvider()));
+  if (auto* view = view_tracker_.view(); view) {
+    SetImage(index, image_model.Rasterize(view->GetColorProvider()));
+  }
 }
 
-void MultiImageContainer::SetImage(size_t index,
-                                   const gfx::ImageSkia& image_skia) {
+void MultiImageContainer::SetImage(size_t index, const gfx::ImageSkia& image) {
   if (index < images_.size()) {
-    images_[index]->SetImage(ui::ImageModel::FromImageSkia(image_skia));
+    images_[index]->SetImage(ui::ImageModel::FromImageSkia(image));
   }
 }
 
 views::View* MultiImageContainer::GetView() {
-  return view_;
+  return view_tracker_.view();
 }
 
 const views::View* MultiImageContainer::GetView() const {
-  return view_;
+  return view_tracker_.view();
 }
 
 void MultiImageContainer::UpdateImage(const views::LabelButton* button) {
diff --git a/chrome/browser/ui/views/permissions/chip/multi_image_container.h b/chrome/browser/ui/views/permissions/chip/multi_image_container.h
index 260d38b..650a4d9 100644
--- a/chrome/browser/ui/views/permissions/chip/multi_image_container.h
+++ b/chrome/browser/ui/views/permissions/chip/multi_image_container.h
@@ -31,9 +31,9 @@
   MultiImageContainer& operator=(const MultiImageContainer&) = delete;
   ~MultiImageContainer() override;
 
-  void SetImages(const std::vector<const ui::ImageModel>& image_model);
+  void SetImages(const std::vector<ui::ImageModel>& image_model);
   void SetImage(size_t index, const ui::ImageModel& image_model);
-  void SetImage(size_t index, const gfx::ImageSkia& image_skia);
+  void SetImage(size_t index, const gfx::ImageSkia& image);
 
   // LabelButtonImageContainer
   std::unique_ptr<views::View> CreateView() override;
@@ -46,7 +46,7 @@
   void RemoveExtraImages(const size_t number_of_images);
 
   std::vector<raw_ptr<views::ImageView>> images_;
-  raw_ptr<views::View> view_;
+  views::ViewTracker view_tracker_;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_PERMISSIONS_CHIP_MULTI_IMAGE_CONTAINER_H_
diff --git a/chrome/browser/ui/views/permissions/chip/multi_image_container_unittest.cc b/chrome/browser/ui/views/permissions/chip/multi_image_container_unittest.cc
index d4041e47..637def3 100644
--- a/chrome/browser/ui/views/permissions/chip/multi_image_container_unittest.cc
+++ b/chrome/browser/ui/views/permissions/chip/multi_image_container_unittest.cc
@@ -14,7 +14,7 @@
   EXPECT_EQ(images->children().size(), 1u);
 
   size_t number_of_images = 5;  // some arbitrary number.
-  std::vector<const ui::ImageModel> models(number_of_images);
+  std::vector<ui::ImageModel> models(number_of_images);
   container.SetImages(models);
   EXPECT_EQ(images->children().size(), number_of_images);
 
diff --git a/chrome/browser/ui/views/tab_search_bubble_host.cc b/chrome/browser/ui/views/tab_search_bubble_host.cc
index 886f680..72d6e7f3 100644
--- a/chrome/browser/ui/views/tab_search_bubble_host.cc
+++ b/chrome/browser/ui/views/tab_search_bubble_host.cc
@@ -11,6 +11,8 @@
 #include "base/trace_event/trace_event.h"
 #include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/feature_engagement/tracker_factory.h"
+#include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
+#include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -60,7 +62,10 @@
 
 TabSearchBubbleHost::TabSearchBubbleHost(views::Button* button,
                                          Profile* profile)
-    : button_(button),
+    : optimization_guide::SettingsEnabledObserver(
+          optimization_guide::proto::ModelExecutionFeature::
+              MODEL_EXECUTION_FEATURE_TAB_ORGANIZATION),
+      button_(button),
       profile_(profile),
       webui_bubble_manager_(button,
                             profile,
@@ -77,6 +82,12 @@
       tab_organization_service->AddObserver(this);
     }
   }
+  OptimizationGuideKeyedService* optimization_guide_keyed_service =
+      OptimizationGuideKeyedServiceFactory::GetForProfile(profile);
+  if (optimization_guide_keyed_service) {
+    optimization_guide_keyed_service->AddModelExecutionSettingsEnabledObserver(
+        this);
+  }
   auto menu_button_controller = std::make_unique<views::MenuButtonController>(
       button,
       base::BindRepeating(&TabSearchBubbleHost::ButtonPressed,
@@ -94,6 +105,12 @@
       tab_organization_service->RemoveObserver(this);
     }
   }
+  OptimizationGuideKeyedService* optimization_guide_keyed_service =
+      OptimizationGuideKeyedServiceFactory::GetForProfile(profile_);
+  if (optimization_guide_keyed_service) {
+    optimization_guide_keyed_service
+        ->RemoveModelExecutionSettingsEnabledObserver(this);
+  }
 }
 
 void TabSearchBubbleHost::OnWidgetVisibilityChanged(views::Widget* widget,
@@ -156,6 +173,24 @@
   }
 }
 
+void TabSearchBubbleHost::OnChangeInFeatureCurrentlyEnabledState(
+    bool is_now_enabled) {
+  // This logic is slightly more strict than is_now_enabled, may make a
+  // difference in some edge cases.
+  bool enabled = TabOrganizationUtils::GetInstance()->IsEnabled(profile_);
+  if (!enabled) {
+    return;
+  }
+
+  auto* const tab_organization_service =
+      TabOrganizationServiceFactory::GetForProfile(profile_);
+  if (!tab_organization_service) {
+    return;
+  }
+
+  tab_organization_service->AddObserver(this);
+}
+
 bool TabSearchBubbleHost::ShowTabSearchBubble(
     bool triggered_by_keyboard_shortcut,
     int tab_index) {
diff --git a/chrome/browser/ui/views/tab_search_bubble_host.h b/chrome/browser/ui/views/tab_search_bubble_host.h
index faf4820..cafdc07 100644
--- a/chrome/browser/ui/views/tab_search_bubble_host.h
+++ b/chrome/browser/ui/views/tab_search_bubble_host.h
@@ -24,7 +24,8 @@
 // TabSearchBubbleHost assumes responsibility for configuring its button,
 // showing / hiding the tab search bubble and handling metrics collection.
 class TabSearchBubbleHost : public views::WidgetObserver,
-                            public TabOrganizationObserver {
+                            public TabOrganizationObserver,
+                            public optimization_guide::SettingsEnabledObserver {
  public:
   TabSearchBubbleHost(views::Button* button, Profile* profile);
   TabSearchBubbleHost(const TabSearchBubbleHost&) = delete;
@@ -39,6 +40,9 @@
   void OnOrganizationAccepted(const Browser* browser) override;
   void OnUserInvokedFeature(const Browser* browser) override;
 
+  // SettingsEnabledObserver
+  void OnChangeInFeatureCurrentlyEnabledState(bool is_now_enabled) override;
+
   // When this is called the bubble may already be showing or be loading in.
   // This returns true if the method call results in the creation of a new Tab
   // Search bubble. Optionally use tab_index to force the bubble to open to the
diff --git a/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.cc b/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.cc
index 8ad78dc7..d1e3f35 100644
--- a/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.cc
+++ b/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.cc
@@ -52,6 +52,7 @@
 // TODO(crbug/1125897): Enable gn check once it learns about conditional
 // includes.
 #include "components/metrics/structured/structured_events.h"  // nogncheck
+#include "components/metrics/structured/structured_metrics_client.h"  // nogncheck
 #endif
 
 namespace {
@@ -241,11 +242,11 @@
 #if BUILDFLAG(IS_CHROMEOS)
     if (base::FeatureList::IsEnabled(
             metrics::structured::kAppDiscoveryLogging)) {
-      cros_events::AppDiscovery_Browser_AppInstallDialogResult()
-          .SetWebAppInstallStatus(
-              ToLong(web_app::WebAppInstallStatus::kCancelled))
-          .SetAppId(app_id)
-          .Record();
+      metrics::structured::StructuredMetricsClient::Record(
+          std::move(cros_events::AppDiscovery_Browser_AppInstallDialogResult()
+                        .SetWebAppInstallStatus(
+                            ToLong(web_app::WebAppInstallStatus::kCancelled))
+                        .SetAppId(app_id)));
     }
 #endif  //  BUILDFLAG(IS_CHROMEOS)
 
@@ -275,10 +276,11 @@
 
 #if BUILDFLAG(IS_CHROMEOS)
   if (base::FeatureList::IsEnabled(metrics::structured::kAppDiscoveryLogging)) {
-    cros_events::AppDiscovery_Browser_AppInstallDialogResult()
-        .SetWebAppInstallStatus(ToLong(web_app::WebAppInstallStatus::kAccepted))
-        .SetAppId(app_id)
-        .Record();
+    metrics::structured::StructuredMetricsClient::Record(
+        std::move(cros_events::AppDiscovery_Browser_AppInstallDialogResult()
+                      .SetWebAppInstallStatus(
+                          ToLong(web_app::WebAppInstallStatus::kAccepted))
+                      .SetAppId(app_id)));
   }
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
diff --git a/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.cc b/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.cc
index b82dd4d..31053f6 100644
--- a/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.cc
@@ -75,6 +75,7 @@
 // TODO(crbug/1125897): Enable gn check once it learns about conditional
 // includes.
 #include "components/metrics/structured/structured_events.h"  // nogncheck
+#include "components/metrics/structured/structured_metrics_client.h"  // nogncheck
 #endif
 
 namespace {
@@ -418,9 +419,9 @@
 #if BUILDFLAG(IS_CHROMEOS)
   if (base::FeatureList::IsEnabled(metrics::structured::kAppDiscoveryLogging)) {
     webapps::AppId app_id = web_app::GenerateAppIdFromManifestId(manifest_id);
-    cros_events::AppDiscovery_Browser_AppInstallDialogShown()
-        .SetAppId(app_id)
-        .Record();
+    metrics::structured::StructuredMetricsClient::Record(std::move(
+        cros_events::AppDiscovery_Browser_AppInstallDialogShown().SetAppId(
+            app_id)));
   }
 #endif  // BUILDFLAG(IS_CHROMEOS)
 }
diff --git a/chrome/browser/ui/views/webid/account_selection_modal_view.cc b/chrome/browser/ui/views/webid/account_selection_modal_view.cc
index 66213bf..27141b3 100644
--- a/chrome/browser/ui/views/webid/account_selection_modal_view.cc
+++ b/chrome/browser/ui/views/webid/account_selection_modal_view.cc
@@ -44,6 +44,8 @@
 constexpr int kDialogMargin = 24;
 
 AccountSelectionModalView::AccountSelectionModalView(
+    const std::u16string& top_frame_for_display,
+    const std::optional<std::u16string>& idp_title,
     blink::mojom::RpContext rp_context,
     Browser* browser,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
@@ -52,8 +54,7 @@
     : AccountSelectionViewBase(browser,
                                observer,
                                widget_observer,
-                               std::move(url_loader_factory)),
-      rp_context_(rp_context) {
+                               std::move(url_loader_factory)) {
   SetModalType(ui::MODAL_TYPE_WINDOW);
   SetOwnedByWidget(true);
   set_margins(gfx::Insets::VH(kDialogMargin, kDialogMargin));
@@ -66,6 +67,10 @@
   SetButtons(ui::DIALOG_BUTTON_CANCEL);
   SetButtonLabel(ui::DIALOG_BUTTON_CANCEL,
                  l10n_util::GetStringUTF16(IDS_ACCOUNT_SELECTION_CANCEL));
+
+  title_ = GetTitle(top_frame_for_display, /*iframe_for_display=*/absl::nullopt,
+                    idp_title, rp_context);
+  SetAccessibleTitle(title_);
 }
 
 AccountSelectionModalView::~AccountSelectionModalView() {}
@@ -86,9 +91,62 @@
   dialog_widget_ = widget->GetWeakPtr();
 }
 
+std::unique_ptr<views::View>
+AccountSelectionModalView::CreateAccountChooserHeader() {
+  // TODO(crbug.com/1518356): Add IDP icon.
+  std::unique_ptr<views::View> header = std::make_unique<views::View>();
+  header->SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kVertical));
+
+  // Add the title.
+  title_label_ = header->AddChildView(std::make_unique<views::Label>(
+      title_, views::style::CONTEXT_DIALOG_TITLE, views::style::STYLE_PRIMARY));
+  SetLabelProperties(title_label_);
+
+  // Add the body.
+  views::Label* body_label =
+      header->AddChildView(std::make_unique<views::Label>(
+          l10n_util::GetStringUTF16(IDS_ACCOUNT_SELECTION_CHOOSE_AN_ACCOUNT),
+          views::style::CONTEXT_DIALOG_BODY_TEXT, views::style::STYLE_HINT));
+  SetLabelProperties(body_label);
+  return header;
+}
+
+std::unique_ptr<views::View>
+AccountSelectionModalView::CreateMultipleAccountChooser(
+    const std::vector<IdentityProviderDisplayData>& idp_display_data_list) {
+  auto scroll_view = std::make_unique<views::ScrollView>();
+  scroll_view->SetHorizontalScrollBarMode(
+      views::ScrollView::ScrollBarMode::kDisabled);
+  views::View* const content =
+      scroll_view->SetContents(std::make_unique<views::View>());
+  content->SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kVertical));
+  size_t num_rows = 0;
+  for (const auto& idp_display_data : idp_display_data_list) {
+    for (const auto& account : idp_display_data.accounts) {
+      content->AddChildView(
+          CreateAccountRow(account, idp_display_data, /*should_hover=*/true));
+    }
+    num_rows += idp_display_data.accounts.size();
+  }
+
+  const int per_account_size = content->GetPreferredSize().height() / num_rows;
+  scroll_view->ClipHeightTo(0, static_cast<int>(per_account_size * 2.5f));
+  return scroll_view;
+}
+
 void AccountSelectionModalView::ShowMultiAccountPicker(
     const std::vector<IdentityProviderDisplayData>& idp_display_data_list) {
-  // TODO(crbug.com/1518356): Implement modal multi account picker.
+  AddChildView(CreateAccountChooserHeader());
+  AddChildView(CreateMultipleAccountChooser(idp_display_data_list));
+
+  InitDialogWidget();
+
+  // TODO(crbug.com/1518356): Connect with multi IDP API.
+  // TODO(crbug.com/1518356): Connect with add account API.
+  // TODO(crbug.com/1518356): Add permissions UI. This should include the
+  // disclosure text.
 }
 
 void AccountSelectionModalView::ShowVerifyingSheet(
@@ -121,32 +179,12 @@
     const content::IdentityRequestAccount& account,
     const IdentityProviderDisplayData& idp_display_data,
     bool show_back_button) {
-  // TODO(crbug.com/1518356): Add IDP icon.
-  std::unique_ptr<views::View> header = std::make_unique<views::View>();
-  header->SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::Orientation::kVertical));
-
-  // Add the title.
-  std::u16string title_text =
-      GetTitle(top_frame_for_display, iframe_for_display,
-               idp_display_data.idp_etld_plus_one, rp_context_);
-  title_label_ = header->AddChildView(std::make_unique<views::Label>(
-      title_text, views::style::CONTEXT_DIALOG_TITLE,
-      views::style::STYLE_PRIMARY));
-  SetLabelProperties(title_label_);
-
-  // Add the body.
-  views::Label* body_label =
-      header->AddChildView(std::make_unique<views::Label>(
-          l10n_util::GetStringUTF16(IDS_ACCOUNT_SELECTION_CHOOSE_AN_ACCOUNT),
-          views::style::CONTEXT_DIALOG_BODY_TEXT, views::style::STYLE_HINT));
-  SetLabelProperties(body_label);
-
-  AddChildView(std::move(header));
+  AddChildView(CreateAccountChooserHeader());
   AddChildView(CreateSingleAccountChooser(idp_display_data, account));
 
   InitDialogWidget();
 
+  // TODO(crbug.com/1518356): Connect with multi IDP API.
   // TODO(crbug.com/1518356): Connect with add account API.
   // TODO(crbug.com/1518356): Add permissions UI. This should include the
   // disclosure text.
diff --git a/chrome/browser/ui/views/webid/account_selection_modal_view.h b/chrome/browser/ui/views/webid/account_selection_modal_view.h
index 0ca2998..e1dc7988 100644
--- a/chrome/browser/ui/views/webid/account_selection_modal_view.h
+++ b/chrome/browser/ui/views/webid/account_selection_modal_view.h
@@ -23,6 +23,8 @@
 
  public:
   AccountSelectionModalView(
+      const std::u16string& top_frame_for_display,
+      const std::optional<std::u16string>& idp_title,
       blink::mojom::RpContext rp_context,
       Browser* browser,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
@@ -70,6 +72,10 @@
   std::optional<std::string> GetDialogSubtitle() const override;
 
  private:
+  // Returns a View for header of an account chooser. It contains text to prompt
+  // the user to sign in to an RP with an account from an IDP.
+  std::unique_ptr<views::View> CreateAccountChooserHeader();
+
   // Returns a View for single account chooser. It contains clickable account
   // information, and a button for the user to close the modal dialog. The size
   // of the `idp_display_data.accounts` vector must be 1.
@@ -77,11 +83,16 @@
       const IdentityProviderDisplayData& idp_display_data,
       const content::IdentityRequestAccount& account);
 
+  // Returns a View for multiple account chooser. It contains the info for each
+  // account in a button, so the user can pick an account.
+  std::unique_ptr<views::View> CreateMultipleAccountChooser(
+      const std::vector<IdentityProviderDisplayData>& idp_display_data_list);
+
   // View containing the modal dialog title.
   raw_ptr<views::Label> title_label_ = nullptr;
 
-  // The relying party context to show in the title.
-  blink::mojom::RpContext rp_context_;
+  // The title for the modal dialog.
+  std::u16string title_;
 
   // Used to ensure that callbacks are not run if the AccountSelectionModalView
   // is destroyed.
diff --git a/chrome/browser/ui/views/webid/account_selection_modal_view_unittest.cc b/chrome/browser/ui/views/webid/account_selection_modal_view_unittest.cc
index 060377a3..f2d9f674 100644
--- a/chrome/browser/ui/views/webid/account_selection_modal_view_unittest.cc
+++ b/chrome/browser/ui/views/webid/account_selection_modal_view_unittest.cc
@@ -45,7 +45,6 @@
 namespace {
 
 const std::u16string kTopFrameETLDPlusOne = u"top-frame-example.com";
-const std::u16string kIframeETLDPlusOne = u"iframe-example.com";
 const std::u16string kIdpETLDPlusOne = u"idp-example.com";
 const std::u16string kTitleSignIn =
     u"Sign in to top-frame-example.com with idp-example.com";
@@ -71,6 +70,17 @@
       /*domain_hints=*/std::vector<std::string>(), login_state);
 }
 
+std::vector<content::IdentityRequestAccount> CreateTestIdentityRequestAccounts(
+    const std::vector<std::string>& account_suffixes,
+    content::IdentityRequestAccount::LoginState login_state) {
+  std::vector<content::IdentityRequestAccount> accounts;
+  for (const std::string& account_suffix : account_suffixes) {
+    accounts.push_back(
+        CreateTestIdentityRequestAccount(account_suffix, login_state));
+  }
+  return accounts;
+}
+
 content::ClientMetadata CreateTestClientMetadata(
     const std::string& terms_of_service_url) {
   return content::ClientMetadata((GURL(terms_of_service_url)),
@@ -81,7 +91,6 @@
   std::vector<std::string> child_class_names;
   for (views::View* child_view : parent->children()) {
     child_class_names.push_back(child_view->GetClassName());
-    std::cout << child_view->GetClassName() << std::endl;
   }
   return child_class_names;
 }
@@ -97,11 +106,11 @@
     anchor_widget_ = CreateTestWidget();
     anchor_widget_->Show();
 
-    dialog_ = new AccountSelectionModalView(blink::mojom::RpContext::kSignIn,
-                                            /*browser=*/nullptr,
-                                            shared_url_loader_factory(),
-                                            /*observer=*/nullptr,
-                                            /*widget_observer=*/nullptr);
+    dialog_ = new AccountSelectionModalView(
+        kTopFrameETLDPlusOne, kIdpETLDPlusOne, blink::mojom::RpContext::kSignIn,
+        /*browser=*/nullptr, shared_url_loader_factory(),
+        /*observer=*/nullptr,
+        /*widget_observer=*/nullptr);
   }
 
   void CreateSingleAccountPicker(
@@ -117,10 +126,29 @@
         CreateTestClientMetadata(terms_of_service_url), {account},
         /*request_permission=*/true, /*has_login_status_mismatch=*/false);
     dialog_->ShowSingleAccountConfirmDialog(
-        kTopFrameETLDPlusOne,
-        exclude_iframe ? std::nullopt
-                       : std::make_optional<std::u16string>(kIframeETLDPlusOne),
-        account, idp_data, show_back_button);
+        kTopFrameETLDPlusOne, /*iframe_for_display=*/absl::nullopt, account,
+        idp_data, show_back_button);
+    constrained_window::CreateBrowserModalDialogViews(
+        dialog_->AsDialogDelegate(), anchor_widget_->GetNativeWindow());
+  }
+
+  void CreateMultiAccountPicker(
+      const std::vector<std::string>& account_suffixes,
+      bool supports_add_account = false) {
+    std::vector<content::IdentityRequestAccount> account_list =
+        CreateTestIdentityRequestAccounts(
+            account_suffixes,
+            content::IdentityRequestAccount::LoginState::kSignUp);
+
+    CreateAccountSelectionModal();
+    std::vector<IdentityProviderDisplayData> idp_data;
+    content::IdentityProviderMetadata metadata;
+    metadata.supports_add_account = supports_add_account;
+    idp_data.emplace_back(
+        kIdpETLDPlusOne, metadata,
+        CreateTestClientMetadata(/*terms_of_service_url=*/""), account_list,
+        /*request_permission=*/true, /*has_login_status_mismatch=*/false);
+    dialog_->ShowMultiAccountPicker(idp_data);
     constrained_window::CreateBrowserModalDialogViews(
         dialog_->AsDialogDelegate(), anchor_widget_->GetNativeWindow());
   }
@@ -143,6 +171,15 @@
               gfx::Size(kDesiredAvatarSize, kDesiredAvatarSize));
   }
 
+  void CheckAccountRows(
+      const std::vector<raw_ptr<views::View, VectorExperimental>>& accounts,
+      const std::vector<std::string>& account_suffixes) {
+    EXPECT_EQ(accounts.size(), account_suffixes.size());
+    for (size_t i = 0; i < std::size(account_suffixes); ++i) {
+      CheckAccountRow(accounts[i], account_suffixes[i]);
+    }
+  }
+
   void PerformHeaderChecks(views::View* header,
                            const std::u16string& expected_title) {
     // Perform some basic dialog checks.
@@ -203,6 +240,32 @@
     CheckAccountRow(single_account_chooser->children()[0], kAccountSuffix);
   }
 
+  void TestMultipleAccounts(const std::u16string& expected_title) {
+    const std::vector<std::string> kAccountSuffixes = {"0", "1", "2"};
+    CreateMultiAccountPicker(kAccountSuffixes);
+
+    std::vector<raw_ptr<views::View, VectorExperimental>> children =
+        dialog()->children();
+    ASSERT_EQ(children.size(), 2u);
+    PerformHeaderChecks(children[0], expected_title);
+
+    views::ScrollView* scroller = static_cast<views::ScrollView*>(children[1]);
+    ASSERT_FALSE(scroller->children().empty());
+    views::View* wrapper = scroller->children()[0];
+    ASSERT_FALSE(wrapper->children().empty());
+    views::View* contents = wrapper->children()[0];
+
+    views::BoxLayout* layout_manager =
+        static_cast<views::BoxLayout*>(contents->GetLayoutManager());
+    EXPECT_TRUE(layout_manager);
+    EXPECT_EQ(layout_manager->GetOrientation(),
+              views::BoxLayout::Orientation::kVertical);
+    std::vector<raw_ptr<views::View, VectorExperimental>> accounts =
+        contents->children();
+
+    CheckAccountRows(accounts, kAccountSuffixes);
+  }
+
   void SetUp() override {
     feature_list_.InitAndEnableFeature(features::kFedCm);
     test_web_contents_ =
@@ -246,3 +309,7 @@
 TEST_F(AccountSelectionModalViewTest, SingleAccount) {
   TestSingleAccount(kTitleSignIn);
 }
+
+TEST_F(AccountSelectionModalViewTest, MultipleAccounts) {
+  TestMultipleAccounts(kTitleSignIn);
+}
diff --git a/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.cc b/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.cc
index 80585adf..97ed5a80 100644
--- a/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.cc
+++ b/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.cc
@@ -91,6 +91,17 @@
       base::BindOnce(&FedCmAccountSelectionView::OnAccountsDisplayed,
                      weak_ptr_factory_.GetWeakPtr());
 
+  // TODO(crbug.com/1518356): Support modal dialogs for all types of FedCM
+  // dialogs. This boolean is used to fall back to the bubble dialog where
+  // modal is not yet implemented.
+  bool has_modal_support =
+      sign_in_mode != Account::SignInMode::kAuto &&
+      !identity_provider_data_list[0].idp_metadata.supports_add_account;
+
+  // TODO(crbug.com/1518356): Implement modal request permissions dialog.
+  bool should_request_permission =
+      rp_mode == blink::mojom::RpMode::kWidget || !has_modal_support;
+
   idp_display_data_list_.clear();
 
   size_t accounts_size = 0u;
@@ -99,7 +110,9 @@
     idp_display_data_list_.emplace_back(
         base::UTF8ToUTF16(identity_provider.idp_for_display),
         identity_provider.idp_metadata, identity_provider.client_metadata,
-        identity_provider.accounts, identity_provider.request_permission,
+        identity_provider.accounts,
+        should_request_permission ? identity_provider.request_permission
+                                  : false,
         identity_provider.has_login_status_mismatch);
     // TODO(crbug.com/1406014): Decide what we should display if the IdPs use
     // different contexts here.
@@ -118,13 +131,6 @@
                                   base::UTF8ToUTF16(*iframe_etld_plus_one))
                             : std::nullopt;
 
-  // TODO(crbug.com/1518356): Support modal dialogs for all types of FedCM
-  // dialogs. This boolean is used to fall back to the bubble dialog where
-  // modal is not yet implemented.
-  bool has_modal_support =
-      idp_display_data_list_.size() == 1u && accounts_size == 1u &&
-      !idp_display_data_list_[0].idp_metadata.supports_add_account;
-
   // If a modal dialog was created previously but there is no modal support for
   // this type of dialog, reset account_selection_view_ to create a bubble
   // dialog instead.
@@ -449,7 +455,7 @@
 
   if (rp_mode == blink::mojom::RpMode::kButton && has_modal_support) {
     return new AccountSelectionModalView(
-        rp_context, browser,
+        top_frame_etld_plus_one, idp_title, rp_context, browser,
         SystemNetworkContextManager::GetInstance()->GetSharedURLLoaderFactory(),
         this, this);
   }
diff --git a/chrome/browser/ui/web_applications/web_app_dialog_utils.cc b/chrome/browser/ui/web_applications/web_app_dialog_utils.cc
index 6ca9834..4a8d5f3 100644
--- a/chrome/browser/ui/web_applications/web_app_dialog_utils.cc
+++ b/chrome/browser/ui/web_applications/web_app_dialog_utils.cc
@@ -39,6 +39,7 @@
 #include "chrome/browser/metrics/structured/event_logging_features.h"
 // TODO(crbug.com/1125897): Enable gn check once it handles conditional includes
 #include "components/metrics/structured/structured_events.h"  // nogncheck
+#include "components/metrics/structured/structured_metrics_client.h"  // nogncheck
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -139,9 +140,9 @@
           install_source == webapps::WebappInstallSource::MENU_BROWSER_TAB) {
         webapps::AppId app_id =
             web_app::GenerateAppIdFromManifestId(web_app_info->manifest_id);
-        cros_events::AppDiscovery_Browser_ClickInstallAppFromMenu()
-            .SetAppId(app_id)
-            .Record();
+        metrics::structured::StructuredMetricsClient::Record(std::move(
+            cros_events::AppDiscovery_Browser_ClickInstallAppFromMenu()
+                .SetAppId(app_id)));
       }
 #endif
       if (webapps::AppBannerManager::FromWebContents(initiator_web_contents)
@@ -166,9 +167,9 @@
               metrics::structured::kAppDiscoveryLogging)) {
         webapps::AppId app_id =
             web_app::GenerateAppIdFromManifestId(web_app_info->manifest_id);
-        cros_events::AppDiscovery_Browser_CreateShortcut()
-            .SetAppId(app_id)
-            .Record();
+        metrics::structured::StructuredMetricsClient::Record(std::move(
+            cros_events::AppDiscovery_Browser_CreateShortcut().SetAppId(
+                app_id)));
       }
 #endif
 
diff --git a/chrome/browser/ui/webui/ash/app_install/app_install_page_handler.cc b/chrome/browser/ui/webui/ash/app_install/app_install_page_handler.cc
index 8131586..3ed5962b 100644
--- a/chrome/browser/ui/webui/ash/app_install/app_install_page_handler.cc
+++ b/chrome/browser/ui/webui/ash/app_install/app_install_page_handler.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/webui/ash/app_install/app_install_page_handler.h"
 
+#include <utility>
+
 #include "base/metrics/user_metrics.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
@@ -11,6 +13,7 @@
 #include "chrome/browser/ui/webui/ash/app_install/app_install.mojom.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
 #include "components/metrics/structured/structured_events.h"
+#include "components/metrics/structured/structured_metrics_client.h"
 #include "components/services/app_service/public/cpp/app_launch_util.h"
 
 namespace ash::app_install {
@@ -56,11 +59,11 @@
 
     if (base::FeatureList::IsEnabled(
             metrics::structured::kAppDiscoveryLogging)) {
-      cros_events::AppDiscovery_Browser_AppInstallDialogResult()
-          .SetWebAppInstallStatus(
-              ToLong(web_app::WebAppInstallStatus::kCancelled))
-          .SetAppId(expected_app_id_)
-          .Record();
+      metrics::structured::StructuredMetricsClient::Record(
+          std::move(cros_events::AppDiscovery_Browser_AppInstallDialogResult()
+                        .SetWebAppInstallStatus(
+                            ToLong(web_app::WebAppInstallStatus::kCancelled))
+                        .SetAppId(expected_app_id_)));
     }
     std::move(dialog_accepted_callback_).Run(false);
   }
@@ -76,10 +79,11 @@
   base::RecordAction(
       base::UserMetricsAction("ChromeOS.AppInstallDialog.Installed"));
   if (base::FeatureList::IsEnabled(metrics::structured::kAppDiscoveryLogging)) {
-    cros_events::AppDiscovery_Browser_AppInstallDialogResult()
-        .SetWebAppInstallStatus(ToLong(web_app::WebAppInstallStatus::kAccepted))
-        .SetAppId(expected_app_id_)
-        .Record();
+    metrics::structured::StructuredMetricsClient::Record(
+        std::move(cros_events::AppDiscovery_Browser_AppInstallDialogResult()
+                      .SetWebAppInstallStatus(
+                          ToLong(web_app::WebAppInstallStatus::kAccepted))
+                      .SetAppId(expected_app_id_)));
   }
 
   install_app_callback_ = std::move(callback);
diff --git a/chrome/browser/ui/webui/ash/cellular_setup/cellular_setup_localized_strings_provider.cc b/chrome/browser/ui/webui/ash/cellular_setup/cellular_setup_localized_strings_provider.cc
index e15c3075..e9bc3d49 100644
--- a/chrome/browser/ui/webui/ash/cellular_setup/cellular_setup_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/ash/cellular_setup/cellular_setup_localized_strings_provider.cc
@@ -124,12 +124,11 @@
   int value;
 };
 
-const std::vector<const NamedBoolean>& GetBooleanValues() {
-  static const base::NoDestructor<std::vector<const NamedBoolean>> named_bools(
+const std::vector<NamedBoolean>& GetBooleanValues() {
+  static const base::NoDestructor<std::vector<NamedBoolean>> named_bools(
       {{"useSecondEuicc",
         base::FeatureList::IsEnabled(features::kCellularUseSecondEuicc)},
-       {"isSmdsSupportEnabled",
-        ash::features::IsSmdsSupportEnabled()}});
+       {"isSmdsSupportEnabled", ash::features::IsSmdsSupportEnabled()}});
   return *named_bools;
 }
 
diff --git a/chrome/browser/ui/webui/ash/emoji/BUILD.gn b/chrome/browser/ui/webui/ash/emoji/BUILD.gn
index 0d7fd51..fbd17d6f 100644
--- a/chrome/browser/ui/webui/ash/emoji/BUILD.gn
+++ b/chrome/browser/ui/webui/ash/emoji/BUILD.gn
@@ -13,6 +13,7 @@
     "new_window_proxy.mojom",
   ]
   public_deps = [
+    "//chromeos/ash/components/emoji:mojo_bindings",
     "//mojo/public/mojom/base",
     "//ui/gfx/geometry/mojom",
     "//url/mojom:url_mojom_gurl",
diff --git a/chrome/browser/ui/webui/ash/emoji/emoji_page_handler.cc b/chrome/browser/ui/webui/ash/emoji/emoji_page_handler.cc
index c6fd2fb6..8539aa9 100644
--- a/chrome/browser/ui/webui/ash/emoji/emoji_page_handler.cc
+++ b/chrome/browser/ui/webui/ash/emoji/emoji_page_handler.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/ui/webui/ash/emoji/emoji_ui.h"
 #include "chrome/browser/ui/webui/ash/emoji/seal_utils.h"
 #include "chrome/grit/generated_resources.h"
+#include "chromeos/ash/components/emoji/emoji_search.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/storage_partition.h"
 #include "ui/base/clipboard/scoped_clipboard_writer.h"
@@ -294,6 +295,10 @@
         emoji_picker::mojom::Feature::EMOJI_PICKER_GIF_SUPPORT);
   }
 
+  if (base::FeatureList::IsEnabled(features::kImeSystemEmojiPickerMojoSearch)) {
+    enabled_features.push_back(
+        emoji_picker::mojom::Feature::EMOJI_PICKER_MOJO_SEARCH);
+  }
   if (SealUtils::ShouldEnable()) {
     enabled_features.push_back(
         emoji_picker::mojom::Feature::EMOJI_PICKER_SEAL_SUPPORT);
diff --git a/chrome/browser/ui/webui/ash/emoji/emoji_picker.mojom b/chrome/browser/ui/webui/ash/emoji/emoji_picker.mojom
index 23bd2e07..821000e 100644
--- a/chrome/browser/ui/webui/ash/emoji/emoji_picker.mojom
+++ b/chrome/browser/ui/webui/ash/emoji/emoji_picker.mojom
@@ -24,6 +24,8 @@
   // Extends the emoji picker with global variant grouping by syncing skin tone
   // and gender preferences between emojis.
   EMOJI_PICKER_VARIANT_GROUPING_SUPPORT = 5,
+  // Use Mojo for search
+  EMOJI_PICKER_MOJO_SEARCH = 6,
 };
 
 // The below structs are used to represent the Response format from the Tenor
diff --git a/chrome/browser/ui/webui/ash/emoji/emoji_search_proxy.cc b/chrome/browser/ui/webui/ash/emoji/emoji_search_proxy.cc
new file mode 100644
index 0000000..94bc200
--- /dev/null
+++ b/chrome/browser/ui/webui/ash/emoji/emoji_search_proxy.cc
@@ -0,0 +1,29 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/ash/emoji/emoji_search_proxy.h"
+
+#include <memory>
+
+#include "ash/constants/ash_features.h"
+#include "chromeos/ash/components/emoji/emoji_search.h"
+
+namespace ash {
+
+EmojiSearchProxy::EmojiSearchProxy(
+    mojo::PendingReceiver<emoji_search::mojom::EmojiSearch> receiver)
+    : receiver_(this, std::move(receiver)) {
+  // Before enabling flag - move search creation to background thread.
+  search_ = std::make_unique<emoji::EmojiSearch>();
+}
+
+EmojiSearchProxy::~EmojiSearchProxy() {}
+
+void EmojiSearchProxy::SearchEmoji(const std::string& query,
+                                   SearchEmojiCallback callback) {
+  CHECK(search_);
+  search_->SearchEmoji(query, std::move(callback));
+}
+
+}  // namespace ash
diff --git a/chrome/browser/ui/webui/ash/emoji/emoji_search_proxy.h b/chrome/browser/ui/webui/ash/emoji/emoji_search_proxy.h
new file mode 100644
index 0000000..0d4c4e3
--- /dev/null
+++ b/chrome/browser/ui/webui/ash/emoji/emoji_search_proxy.h
@@ -0,0 +1,33 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_EMOJI_EMOJI_SEARCH_PROXY_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_EMOJI_EMOJI_SEARCH_PROXY_H_
+
+#include <memory>
+
+#include "chromeos/ash/components/emoji/emoji_search.h"
+#include "chromeos/ash/components/emoji/emoji_search.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+
+namespace ash {
+
+class EmojiSearchProxy : public emoji_search::mojom::EmojiSearch {
+ public:
+  explicit EmojiSearchProxy(
+      mojo::PendingReceiver<emoji_search::mojom::EmojiSearch> receiver);
+  ~EmojiSearchProxy() override;
+
+  void SearchEmoji(const std::string& query,
+                   SearchEmojiCallback callback) override;
+
+ private:
+  mojo::Receiver<emoji_search::mojom::EmojiSearch> receiver_;
+  std::unique_ptr<emoji::EmojiSearch> search_;
+};
+
+}  // namespace ash
+
+#endif  // CHROME_BROWSER_UI_WEBUI_ASH_EMOJI_EMOJI_SEARCH_PROXY_H_
diff --git a/chrome/browser/ui/webui/ash/emoji/emoji_ui.cc b/chrome/browser/ui/webui/ash/emoji/emoji_ui.cc
index b7a1f9e..0b9363f 100644
--- a/chrome/browser/ui/webui/ash/emoji/emoji_ui.cc
+++ b/chrome/browser/ui/webui/ash/emoji/emoji_ui.cc
@@ -180,6 +180,11 @@
 }
 
 void EmojiUI::BindInterface(
+    mojo::PendingReceiver<emoji_search::mojom::EmojiSearch> receiver) {
+  emoji_search_ = std::make_unique<EmojiSearchProxy>(std::move(receiver));
+}
+
+void EmojiUI::BindInterface(
     mojo::PendingReceiver<emoji_picker::mojom::PageHandlerFactory> receiver) {
   page_factory_receiver_.reset();
   page_factory_receiver_.Bind(std::move(receiver));
diff --git a/chrome/browser/ui/webui/ash/emoji/emoji_ui.h b/chrome/browser/ui/webui/ash/emoji/emoji_ui.h
index 13753a3..1b6581b 100644
--- a/chrome/browser/ui/webui/ash/emoji/emoji_ui.h
+++ b/chrome/browser/ui/webui/ash/emoji/emoji_ui.h
@@ -10,12 +10,14 @@
 #include "chrome/browser/ui/views/bubble/webui_bubble_manager.h"
 #include "chrome/browser/ui/webui/ash/emoji/emoji_page_handler.h"
 #include "chrome/browser/ui/webui/ash/emoji/emoji_picker.mojom.h"
+#include "chrome/browser/ui/webui/ash/emoji/emoji_search_proxy.h"
 #include "chrome/browser/ui/webui/ash/emoji/new_window_proxy.h"
 #include "chrome/browser/ui/webui/ash/emoji/new_window_proxy.mojom.h"
 #include "chrome/browser/ui/webui/ash/emoji/seal.h"
 #include "chrome/browser/ui/webui/ash/emoji/seal.mojom.h"
 #include "chrome/browser/ui/webui/webui_load_timer.h"
 #include "chrome/common/webui_url_constants.h"
+#include "chromeos/ash/components/emoji/emoji_search.h"
 #include "content/public/browser/webui_config.h"
 #include "content/public/common/url_constants.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -58,6 +60,9 @@
       mojo::PendingReceiver<color_change_listener::mojom::PageHandler>
           receiver);
 
+  void BindInterface(
+      mojo::PendingReceiver<emoji_search::mojom::EmojiSearch> receiver);
+
   // Instantiates the implementor of the mojom::PageHandlerFactory mojo
   // interface passing the pending receiver that will be internally bound.
   void BindInterface(
@@ -83,6 +88,7 @@
   std::unique_ptr<EmojiPageHandler> page_handler_;
   std::unique_ptr<ash::NewWindowProxy> new_window_proxy_;
   std::unique_ptr<ash::SealService> seal_service_;
+  std::unique_ptr<EmojiSearchProxy> emoji_search_;
 
   mojo::Receiver<emoji_picker::mojom::PageHandlerFactory>
       page_factory_receiver_{this};
diff --git a/chrome/browser/ui/webui/ash/firmware_update_ui/OWNERS b/chrome/browser/ui/webui/ash/firmware_update_ui/OWNERS
new file mode 100644
index 0000000..a4ca675
--- /dev/null
+++ b/chrome/browser/ui/webui/ash/firmware_update_ui/OWNERS
@@ -0,0 +1,2 @@
+jimmyxgong@chromium.org
+cambickel@google.com
\ No newline at end of file
diff --git a/chrome/browser/ui/webui/ash/firmware_update_ui/integration_tests/firmware_update_interactive_uitest.cc b/chrome/browser/ui/webui/ash/firmware_update_ui/integration_tests/firmware_update_interactive_uitest.cc
index d35e4e2d..d03d7f4 100644
--- a/chrome/browser/ui/webui/ash/firmware_update_ui/integration_tests/firmware_update_interactive_uitest.cc
+++ b/chrome/browser/ui/webui/ash/firmware_update_ui/integration_tests/firmware_update_interactive_uitest.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 "base/files/file_util.h"
+#include "base/json/string_escape.h"
 #include "chrome/test/base/chromeos/crosier/interactive_ash_test.h"
 #include "chrome/test/interaction/interactive_browser_test.h"
 
@@ -10,8 +12,14 @@
 namespace {
 
 constexpr char kFirmwareUpdatesUrl[] = "chrome://accessory-update";
+
 class FirmwareUpdateInteractiveUiTest : public InteractiveAshTest {
  public:
+  FirmwareUpdateInteractiveUiTest() {
+    DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kFirmwareUpdatesAppWebContentsId);
+    webcontents_id_ = kFirmwareUpdatesAppWebContentsId;
+  }
+
   auto LaunchFirmwareUpdatesApp() {
     return Do([&]() { CreateBrowserWindow(GURL(kFirmwareUpdatesUrl)); });
   }
@@ -26,26 +34,55 @@
     // Ensure the OS Settings system web app (SWA) is installed.
     InstallSystemApps();
   }
+
+ protected:
+  ui::ElementIdentifier webcontents_id_;
 };
 
 IN_PROC_BROWSER_TEST_F(FirmwareUpdateInteractiveUiTest,
                        TestUpdateCardPresence) {
-  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kFirmwareUpdatesAppWebContentsId);
-
   const DeepQuery kUpdateCardQuery{
       "firmware-update-app",
       "peripheral-updates-list",
       "update-card",
   };
 
+  const DeepQuery kUpdateButtonQuery{
+      "firmware-update-app",
+      "peripheral-updates-list",
+      "update-card",
+      "#updateButton",
+  };
+
+  const DeepQuery kConfirmationDialogNextButtonQuery{
+      "firmware-update-app",
+      "firmware-confirmation-dialog",
+      "#nextButton",
+  };
+
+  const DeepQuery kUpdateDialogDoneButtonQuery{
+      "firmware-update-app",
+      "firmware-update-dialog",
+      "#updateDoneButton",
+  };
+
   RunTestSequence(
-      InstrumentNextTab(kFirmwareUpdatesAppWebContentsId, AnyBrowser()),
+      InstrumentNextTab(webcontents_id_, AnyBrowser()),
       LaunchFirmwareUpdatesApp(),
-      WaitForWebContentsReady(kFirmwareUpdatesAppWebContentsId,
+      WaitForWebContentsReady(webcontents_id_,
                               GURL("chrome://accessory-update")),
       InAnyContext(Steps(
           Log("Verifying that the update-card element is present."),
-          EnsurePresent(kFirmwareUpdatesAppWebContentsId, kUpdateCardQuery))));
+          EnsurePresent(webcontents_id_, kUpdateCardQuery),
+          Log("Clicking on the update button."),
+          ClickElement(webcontents_id_, kUpdateButtonQuery),
+          WaitForElementExists(webcontents_id_,
+                               kConfirmationDialogNextButtonQuery),
+          Log("Clicking on the start update button."),
+          ClickElement(webcontents_id_, kConfirmationDialogNextButtonQuery),
+          Log("Verifying existence of Update Done button."),
+          WaitForElementExists(webcontents_id_,
+                               kUpdateDialogDoneButtonQuery))));
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/webui/ash/settings/DEPS b/chrome/browser/ui/webui/ash/settings/DEPS
index 415bba3..501d303 100644
--- a/chrome/browser/ui/webui/ash/settings/DEPS
+++ b/chrome/browser/ui/webui/ash/settings/DEPS
@@ -7,10 +7,6 @@
     '+ash/color_enhancement/color_enhancement_controller.h',
     '+content/public/browser/tts_controller.h',
   ],
-  'add_new_keyboard_interactive_uitest\.cc': [
-    '+ash/shell.h',
-    '+device/udev_linux/fake_udev_loader.h',
-  ],
   'device_keyboard_handler\.cc': [
     '+ash/shell.h',
   ],
@@ -20,6 +16,10 @@
   'device_section\.cc': [
     '+ash/shell.h',
   ],
+  'device_settings_base_test\.(cc|h)': [
+    '+ash/shell.h',
+    '+device/udev_linux/fake_udev_loader.h',
+  ],
   'display_settings_provider\.cc': [
     '+ash/shell.h',
     "+ash/display/display_prefs.h",
@@ -27,10 +27,6 @@
   'fast_pair_.*': [
     "+ash/quick_pair",
   ],
-  'keyboard_six_pack_keys_interactive_uitest\.cc': [
-    '+ash/shell.h',
-    '+device/udev_linux/fake_udev_loader.h',
-  ],
   'power_section\.(cc|h)' : [
     '+ash/shell.h',
     '+ash/system/power',
diff --git a/chrome/browser/ui/webui/ash/settings/integration_tests/add_new_keyboard_interactive_uitest.cc b/chrome/browser/ui/webui/ash/settings/integration_tests/add_new_keyboard_interactive_uitest.cc
index 260c0db..fe766af 100644
--- a/chrome/browser/ui/webui/ash/settings/integration_tests/add_new_keyboard_interactive_uitest.cc
+++ b/chrome/browser/ui/webui/ash/settings/integration_tests/add_new_keyboard_interactive_uitest.cc
@@ -2,136 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <linux/input-event-codes.h>
-#include <memory>
-
-#include "ash/constants/ash_features.h"
-#include "ash/shell.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/settings_window_manager_chromeos.h"
-#include "chrome/test/base/chromeos/crosier/interactive_ash_test.h"
-#include "device/udev_linux/fake_udev_loader.h"
-#include "ui/base/interaction/element_identifier.h"
-#include "ui/events/ash/keyboard_capability.h"
-#include "ui/events/devices/device_data_manager_test_api.h"
-#include "ui/events/devices/input_device.h"
-#include "ui/events/devices/keyboard_device.h"
+#include "ash/webui/settings/public/constants/routes.mojom-forward.h"
+#include "chrome/browser/ui/webui/ash/settings/integration_tests/device_settings_base_test.h"
 
 namespace ash {
 
 namespace {
 
-constexpr char kKbdTopRowPropertyName[] = "CROS_KEYBOARD_TOP_ROW_LAYOUT";
-constexpr char kKbdTopRowLayout1Tag[] = "1";
-constexpr int kDeviceId1 = 5;
-constexpr char kPerDeviceKeyboardSubpagePath[] = "per-device-keyboard";
-
-class FakeDeviceManager {
- public:
-  FakeDeviceManager() = default;
-  FakeDeviceManager(const FakeDeviceManager&) = delete;
-  FakeDeviceManager& operator=(const FakeDeviceManager&) = delete;
-  ~FakeDeviceManager() = default;
-
-  // Add a fake keyboard to DeviceDataManagerTestApi and provide layout info to
-  // fake udev.
-  void AddFakeInternalKeyboard() {
-    ui::KeyboardDevice fake_keyboard(
-        /*id=*/kDeviceId1, /*type=*/ui::InputDeviceType::INPUT_DEVICE_INTERNAL,
-        /*name=*/"Keyboard1");
-    fake_keyboard.sys_path = base::FilePath("path1");
-
-    fake_keyboard_devices_.push_back(fake_keyboard);
-    ui::KeyboardCapability::KeyboardInfo keyboard_info;
-    keyboard_info.device_type =
-        ui::KeyboardCapability::DeviceType::kDeviceInternalKeyboard;
-    keyboard_info.top_row_layout =
-        ui::KeyboardCapability::KeyboardTopRowLayout::kKbdTopRowLayoutDefault;
-
-    Shell::Get()->keyboard_capability()->SetKeyboardInfoForTesting(
-        fake_keyboard, std::move(keyboard_info));
-
-    ui::DeviceDataManagerTestApi().SetKeyboardDevices({});
-    ui::DeviceDataManagerTestApi().SetKeyboardDevices(fake_keyboard_devices_);
-    ui::DeviceDataManagerTestApi().OnDeviceListsComplete();
-
-    std::map<std::string, std::string> sysfs_properties;
-    std::map<std::string, std::string> sysfs_attributes;
-    sysfs_properties[kKbdTopRowPropertyName] = kKbdTopRowLayout1Tag;
-    fake_udev_.AddFakeDevice(fake_keyboard.name, fake_keyboard.sys_path.value(),
-                             /*subsystem=*/"input", /*devnode=*/std::nullopt,
-                             /*devtype=*/std::nullopt,
-                             std::move(sysfs_attributes),
-                             std::move(sysfs_properties));
-  }
-
- private:
-  testing::FakeUdevLoader fake_udev_;
-  std::vector<ui::KeyboardDevice> fake_keyboard_devices_;
-};
-
-class DeviceSettingsInteractiveUiTest : public InteractiveAshTest {
- public:
-  DeviceSettingsInteractiveUiTest() {
-    feature_list_.InitAndEnableFeature(features::kInputDeviceSettingsSplit);
-  }
-
-  auto AddFakeInternalKeyboard() {
-    return Do([&]() { fake_keyboard_manager_->AddFakeInternalKeyboard(); });
-  }
-
-  // Query to pierce through Shadow DOM to find the keyboard.
-  static DeepQuery kKeyboardNameQuery() {
-    return {
-        "os-settings-ui",
-        "os-settings-main",
-        "main-page-container",
-        "settings-device-page",
-        "settings-per-device-keyboard",
-        "settings-per-device-keyboard-subsection",
-        "h2#keyboardName",
-    };
-  }
-
-  // InteractiveAshTest:
-  void SetUpOnMainThread() override {
-    InteractiveAshTest::SetUpOnMainThread();
-
-    // Set up context for element tracking for InteractiveBrowserTest.
-    SetupContextWidget();
-
-    // Ensure the OS Settings system web app (SWA) is installed.
-    InstallSystemApps();
-
-    fake_keyboard_manager_ = std::make_unique<FakeDeviceManager>();
-  }
-
- protected:
-  std::unique_ptr<FakeDeviceManager> fake_keyboard_manager_;
-  base::test::ScopedFeatureList feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_F(DeviceSettingsInteractiveUiTest, AddNewKeyboard) {
-  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOsSettingsWebContentsId);
-
-  RunTestSequence(
-      Log("Adding a fake internal keyboard"), AddFakeInternalKeyboard(),
-      Log("Opening per device keyboard settings subpage"),
-      InstrumentNextTab(kOsSettingsWebContentsId, AnyBrowser()), Do([&]() {
-        chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
-            GetActiveUserProfile(), kPerDeviceKeyboardSubpagePath);
-      }),
-      WaitForShow(kOsSettingsWebContentsId),
-      Log("Waiting for per device keyboard settings page to load"),
-      WaitForWebContentsReady(
-          kOsSettingsWebContentsId,
-          chrome::GetOSSettingsUrl(kPerDeviceKeyboardSubpagePath)),
-      Log("Waiting for keyboard to exist"),
-      WaitForElementExists(kOsSettingsWebContentsId, kKeyboardNameQuery()),
-      CheckJsResultAt(kOsSettingsWebContentsId, kKeyboardNameQuery(),
-                      "el => el.innerText", "Built-in Keyboard"),
-      Log("Test complete"));
+IN_PROC_BROWSER_TEST_F(DeviceSettingsBaseTest, AddNewKeyboard) {
+  RunTestSequence(SetupInternalKeyboard(),
+                  LaunchSettingsApp(
+                      chromeos::settings::mojom::kPerDeviceKeyboardSubpagePath),
+                  Log("Waiting for keyboard to exist"),
+                  WaitForElementExists(webcontents_id_, kKeyboardNameQuery),
+                  CheckJsResultAt(webcontents_id_, kKeyboardNameQuery,
+                                  "el => el.innerText", "Built-in Keyboard"),
+                  Log("Test complete"));
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/webui/ash/settings/integration_tests/device_settings_base_test.cc b/chrome/browser/ui/webui/ash/settings/integration_tests/device_settings_base_test.cc
new file mode 100644
index 0000000..2a0f995b
--- /dev/null
+++ b/chrome/browser/ui/webui/ash/settings/integration_tests/device_settings_base_test.cc
@@ -0,0 +1,118 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/ash/settings/integration_tests/device_settings_base_test.h"
+#include "base/run_loop.h"
+
+namespace ash {
+
+namespace {
+
+constexpr char kKbdTopRowPropertyName[] = "CROS_KEYBOARD_TOP_ROW_LAYOUT";
+constexpr char kKbdTopRowLayout1Tag[] = "1";
+
+}  // namespace
+
+FakeDeviceManager::FakeDeviceManager() = default;
+FakeDeviceManager::~FakeDeviceManager() = default;
+
+void FakeDeviceManager::AddFakeInternalKeyboard() {
+  ui::KeyboardDevice fake_keyboard(
+      /*id=*/kDeviceId1, /*type=*/ui::InputDeviceType::INPUT_DEVICE_INTERNAL,
+      /*name=*/"Keyboard1");
+  fake_keyboard.sys_path = base::FilePath("path1");
+
+  fake_keyboard_devices_.push_back(fake_keyboard);
+  ui::KeyboardCapability::KeyboardInfo keyboard_info;
+  keyboard_info.device_type =
+      ui::KeyboardCapability::DeviceType::kDeviceInternalKeyboard;
+  keyboard_info.top_row_layout =
+      ui::KeyboardCapability::KeyboardTopRowLayout::kKbdTopRowLayoutDefault;
+
+  Shell::Get()->keyboard_capability()->SetKeyboardInfoForTesting(
+      fake_keyboard, std::move(keyboard_info));
+  // Calling RunUntilIdle() here is necessary before setting the keyboard
+  // devices to prevent the callback from evdev thread to overwrite whatever
+  // we sethere below. See
+  // `InputDeviceFactoryEvdevProxy::OnStartupScanComplete()`.
+  base::RunLoop().RunUntilIdle();
+  ui::DeviceDataManagerTestApi().SetKeyboardDevices(fake_keyboard_devices_);
+  ui::DeviceDataManagerTestApi().OnDeviceListsComplete();
+
+  std::map<std::string, std::string> sysfs_properties;
+  std::map<std::string, std::string> sysfs_attributes;
+  sysfs_properties[kKbdTopRowPropertyName] = kKbdTopRowLayout1Tag;
+  fake_udev_.AddFakeDevice(fake_keyboard.name, fake_keyboard.sys_path.value(),
+                           /*subsystem=*/"input", /*devnode=*/std::nullopt,
+                           /*devtype=*/std::nullopt,
+                           std::move(sysfs_attributes),
+                           std::move(sysfs_properties));
+}
+
+DeviceSettingsBaseTest::DeviceSettingsBaseTest() {
+  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOsSettingsWebContentsId);
+  webcontents_id_ = kOsSettingsWebContentsId;
+
+  feature_list_.InitWithFeatures({features::kInputDeviceSettingsSplit,
+                                  features::kAltClickAndSixPackCustomization},
+                                 {});
+}
+
+DeviceSettingsBaseTest::~DeviceSettingsBaseTest() = default;
+
+ui::test::InteractiveTestApi::MultiStep
+DeviceSettingsBaseTest::LaunchSettingsApp(const std::string& subpage) {
+  return Steps(
+      Log(std::format("Open OS Settings to {0}", subpage)),
+      InstrumentNextTab(webcontents_id_, AnyBrowser()), Do([&]() {
+        chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
+            GetActiveUserProfile(), subpage);
+      }),
+      WaitForShow(webcontents_id_),
+      Log(std::format("Waiting for OS Settings {0} page to load", subpage)),
+
+      Log("Waiting for OS settings audio settings page to load"),
+      WaitForWebContentsReady(webcontents_id_,
+                              chrome::GetOSSettingsUrl(subpage)));
+}
+
+void DeviceSettingsBaseTest::SetUpOnMainThread() {
+  InteractiveAshTest::SetUpOnMainThread();
+
+  // Set up context for element tracking for InteractiveBrowserTest.
+  SetupContextWidget();
+
+  // Ensure the OS Settings system web app (SWA) is installed.
+  InstallSystemApps();
+
+  fake_keyboard_manager_ = std::make_unique<FakeDeviceManager>();
+}
+
+ui::test::InteractiveTestApi::StepBuilder
+DeviceSettingsBaseTest::SetupInternalKeyboard() {
+  return Do([&]() { fake_keyboard_manager_->AddFakeInternalKeyboard(); });
+}
+
+void DeviceSettingsBaseTest::SetMouseDevices(
+    const std::vector<ui::InputDevice>& mice) {
+  base::RunLoop().RunUntilIdle();
+  ui::DeviceDataManagerTestApi().SetMouseDevices(mice);
+  ui::DeviceDataManagerTestApi().OnDeviceListsComplete();
+}
+
+void DeviceSettingsBaseTest::SetTouchpadDevices(
+    const std::vector<ui::TouchpadDevice>& touchpads) {
+  base::RunLoop().RunUntilIdle();
+  ui::DeviceDataManagerTestApi().SetTouchpadDevices(touchpads);
+  ui::DeviceDataManagerTestApi().OnDeviceListsComplete();
+}
+
+void DeviceSettingsBaseTest::SetPointingStickDevices(
+    const std::vector<ui::InputDevice>& pointing_sticks) {
+  base::RunLoop().RunUntilIdle();
+  ui::DeviceDataManagerTestApi().SetPointingStickDevices(pointing_sticks);
+  ui::DeviceDataManagerTestApi().OnDeviceListsComplete();
+}
+
+}  // namespace ash
diff --git a/chrome/browser/ui/webui/ash/settings/integration_tests/device_settings_base_test.h b/chrome/browser/ui/webui/ash/settings/integration_tests/device_settings_base_test.h
new file mode 100644
index 0000000..8ef756d
--- /dev/null
+++ b/chrome/browser/ui/webui/ash/settings/integration_tests/device_settings_base_test.h
@@ -0,0 +1,83 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_INTEGRATION_TESTS_DEVICE_SETTINGS_BASE_TEST_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_INTEGRATION_TESTS_DEVICE_SETTINGS_BASE_TEST_H_
+
+#include <linux/input-event-codes.h>
+#include <memory>
+
+#include "ash/constants/ash_features.h"
+#include "ash/shell.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/ui/chrome_pages.h"
+#include "chrome/browser/ui/settings_window_manager_chromeos.h"
+#include "chrome/test/base/chromeos/crosier/interactive_ash_test.h"
+#include "device/udev_linux/fake_udev_loader.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/events/ash/keyboard_capability.h"
+#include "ui/events/devices/device_data_manager_test_api.h"
+#include "ui/events/devices/input_device.h"
+#include "ui/events/devices/keyboard_device.h"
+
+namespace ash {
+
+inline constexpr int kDeviceId1 = 5;
+inline constexpr char kPerDeviceKeyboardSubpagePath[] = "per-device-keyboard";
+
+class FakeDeviceManager {
+ public:
+  FakeDeviceManager();
+  FakeDeviceManager(const FakeDeviceManager&) = delete;
+  FakeDeviceManager& operator=(const FakeDeviceManager&) = delete;
+  ~FakeDeviceManager();
+
+  // Add a fake keyboard to DeviceDataManagerTestApi and provide layout info to
+  // fake udev.
+  void AddFakeInternalKeyboard();
+
+ private:
+  testing::FakeUdevLoader fake_udev_;
+  std::vector<ui::KeyboardDevice> fake_keyboard_devices_;
+};
+
+class DeviceSettingsBaseTest : public InteractiveAshTest {
+ public:
+  DeviceSettingsBaseTest();
+  DeviceSettingsBaseTest(const DeviceSettingsBaseTest&) = delete;
+  DeviceSettingsBaseTest& operator=(const DeviceSettingsBaseTest&) = delete;
+  ~DeviceSettingsBaseTest() override;
+
+  ui::test::InteractiveTestApi::MultiStep LaunchSettingsApp(
+      const std::string& sub_page);
+  ui::test::InteractiveTestApi::StepBuilder SetupInternalKeyboard();
+
+  void SetMouseDevices(const std::vector<ui::InputDevice>& mice);
+  void SetTouchpadDevices(const std::vector<ui::TouchpadDevice>& touchpads);
+  void SetPointingStickDevices(
+      const std::vector<ui::InputDevice>& pointing_sticks);
+
+  // Query to pierce through Shadow DOM to find the keyboard.
+  const DeepQuery kKeyboardNameQuery{
+      "os-settings-ui",
+      "os-settings-main",
+      "main-page-container",
+      "settings-device-page",
+      "settings-per-device-keyboard",
+      "settings-per-device-keyboard-subsection",
+      "h2#keyboardName",
+  };
+
+  // MixinBasedInProcessBrowserTest:
+  void SetUpOnMainThread() override;
+
+ protected:
+  std::unique_ptr<FakeDeviceManager> fake_keyboard_manager_;
+  base::test::ScopedFeatureList feature_list_;
+  ui::ElementIdentifier webcontents_id_;
+};
+
+}  // namespace ash
+
+#endif  // CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_INTEGRATION_TESTS_DEVICE_SETTINGS_BASE_TEST_H_
diff --git a/chrome/browser/ui/webui/ash/settings/integration_tests/keyboard_six_pack_keys_interactive_uitest.cc b/chrome/browser/ui/webui/ash/settings/integration_tests/keyboard_six_pack_keys_interactive_uitest.cc
index 8081ff8b..f71327a6 100644
--- a/chrome/browser/ui/webui/ash/settings/integration_tests/keyboard_six_pack_keys_interactive_uitest.cc
+++ b/chrome/browser/ui/webui/ash/settings/integration_tests/keyboard_six_pack_keys_interactive_uitest.cc
@@ -4,87 +4,16 @@
 
 #include <memory>
 
-#include "ash/constants/ash_features.h"
-#include "ash/shell.h"
 #include "ash/webui/settings/public/constants/routes.mojom-forward.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/settings_window_manager_chromeos.h"
-#include "chrome/test/base/chromeos/crosier/interactive_ash_test.h"
-#include "device/udev_linux/fake_udev_loader.h"
-#include "ui/base/interaction/element_identifier.h"
-#include "ui/events/ash/keyboard_capability.h"
-#include "ui/events/devices/device_data_manager_test_api.h"
-#include "ui/events/devices/input_device.h"
-#include "ui/events/devices/keyboard_device.h"
+#include "chrome/browser/ui/webui/ash/settings/integration_tests/device_settings_base_test.h"
 #include "ui/events/test/event_generator.h"
 
 namespace ash {
 
 namespace {
 
-constexpr int kDeviceId1 = 5;
-
-class FakeDeviceManager {
+class DeviceSettingsSixPackKeysTest : public DeviceSettingsBaseTest {
  public:
-  FakeDeviceManager() = default;
-  FakeDeviceManager(const FakeDeviceManager&) = delete;
-  FakeDeviceManager& operator=(const FakeDeviceManager&) = delete;
-  ~FakeDeviceManager() = default;
-
-  // Add a fake keyboard to DeviceDataManagerTestApi and provide layout info to
-  // fake udev.
-  void AddFakeInternalKeyboard() {
-    ui::KeyboardDevice fake_keyboard(
-        /*id=*/kDeviceId1, /*type=*/ui::InputDeviceType::INPUT_DEVICE_INTERNAL,
-        /*name=*/"Keyboard1");
-    fake_keyboard.sys_path = base::FilePath("path1");
-
-    fake_keyboard_devices_.push_back(fake_keyboard);
-    ui::KeyboardCapability::KeyboardInfo keyboard_info;
-    keyboard_info.device_type =
-        ui::KeyboardCapability::DeviceType::kDeviceInternalKeyboard;
-    keyboard_info.top_row_layout =
-        ui::KeyboardCapability::KeyboardTopRowLayout::kKbdTopRowLayoutDefault;
-
-    Shell::Get()->keyboard_capability()->SetKeyboardInfoForTesting(
-        fake_keyboard, std::move(keyboard_info));
-
-    // Calling RunUntilIdle() here is necessary before setting the keyboard
-    // devices to prevent the callback from evdev thread to overwrite whatever
-    // we set here below. See
-    // `InputDeviceFactoryEvdevProxy::OnStartupScanComplete()`.
-    base::RunLoop().RunUntilIdle();
-    ui::DeviceDataManagerTestApi().SetKeyboardDevices(fake_keyboard_devices_);
-    ui::DeviceDataManagerTestApi().OnDeviceListsComplete();
-
-    std::map<std::string, std::string> sysfs_properties;
-    std::map<std::string, std::string> sysfs_attributes;
-    sysfs_properties["CROS_KEYBOARD_TOP_ROW_LAYOUT"] = "1";
-    fake_udev_.AddFakeDevice(fake_keyboard.name, fake_keyboard.sys_path.value(),
-                             /*subsystem=*/"input", /*devnode=*/std::nullopt,
-                             /*devtype=*/std::nullopt,
-                             std::move(sysfs_attributes),
-                             std::move(sysfs_properties));
-  }
-
- private:
-  testing::FakeUdevLoader fake_udev_;
-  std::vector<ui::KeyboardDevice> fake_keyboard_devices_;
-};
-
-class DeviceSettingsSixPackKeysTest : public InteractiveAshTest {
- public:
-  DeviceSettingsSixPackKeysTest() {
-    feature_list_.InitWithFeatures({features::kInputDeviceSettingsSplit,
-                                    features::kAltClickAndSixPackCustomization},
-                                   {});
-  }
-
-  auto AddFakeInternalKeyboard() {
-    return Do([&]() { fake_keyboard_manager_->AddFakeInternalKeyboard(); });
-  }
-
   auto SendKeyPressEvent(ui::KeyboardCode key) {
     return Do([key]() {
       ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
@@ -109,17 +38,6 @@
       "settings-device-page", "#perDeviceKeyboardRow",
   };
 
-  // Query to pierce through Shadow DOM to find the Keyboard header.
-  const DeepQuery kKeyboardNameQuery{
-      "os-settings-ui",
-      "os-settings-main",
-      "main-page-container",
-      "settings-device-page",
-      "settings-per-device-keyboard",
-      "settings-per-device-keyboard-subsection",
-      "h2#keyboardName",
-  };
-
   // Query to pierce through Shadow DOM to find the Settings search box.
   const DeepQuery kSearchboxQuery{
       "os-settings-ui", "os-toolbar", "#searchBox", "#search", "#searchInput",
@@ -153,53 +71,22 @@
     change.test_function = value_check_function;
     return WaitForStateChange(webcontents_id_, change);
   }
-
-  // InteractiveAshTest:
-  void SetUpOnMainThread() override {
-    InteractiveAshTest::SetUpOnMainThread();
-
-    // Set up context for element tracking for InteractiveBrowserTest.
-    SetupContextWidget();
-
-    // Ensure the OS Settings system web app (SWA) is installed.
-    InstallSystemApps();
-
-    fake_keyboard_manager_ = std::make_unique<FakeDeviceManager>();
-  }
-
- protected:
-  std::unique_ptr<FakeDeviceManager> fake_keyboard_manager_;
-  base::test::ScopedFeatureList feature_list_;
-  ui::ElementIdentifier webcontents_id_;
 };
 
 IN_PROC_BROWSER_TEST_F(DeviceSettingsSixPackKeysTest, SixPackKeys) {
-  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOsSettingsWebContentsId);
-  webcontents_id_ = kOsSettingsWebContentsId;
   RunTestSequence(
-      Log("Adding a fake internal keyboard"), AddFakeInternalKeyboard(),
-      InstrumentNextTab(kOsSettingsWebContentsId, AnyBrowser()), Do([&]() {
-        chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
-            GetActiveUserProfile(),
-            chromeos::settings::mojom::kDeviceSectionPath);
-      }),
-      WaitForShow(kOsSettingsWebContentsId),
-      Log("Waiting for per device section to load"),
-      WaitForWebContentsReady(
-          kOsSettingsWebContentsId,
-          chrome::GetOSSettingsUrl(
-              chromeos::settings::mojom::kDeviceSectionPath)),
-      WaitForElementExists(kOsSettingsWebContentsId, kKeyboardRowQuery),
-      ClickElement(kOsSettingsWebContentsId, kKeyboardRowQuery),
-      WaitForElementTextContains(kOsSettingsWebContentsId, kKeyboardNameQuery,
+      Log("Adding a fake internal keyboard"), SetupInternalKeyboard(),
+      LaunchSettingsApp(chromeos::settings::mojom::kDeviceSectionPath),
+      WaitForElementExists(webcontents_id_, kKeyboardRowQuery),
+      ClickElement(webcontents_id_, kKeyboardRowQuery),
+      WaitForElementTextContains(webcontents_id_, kKeyboardNameQuery,
                                  "Built-in Keyboard"),
-      ClickElement(kOsSettingsWebContentsId,
-                   kCustomizeKeyboardKeysInternalQuery),
+      ClickElement(webcontents_id_, kCustomizeKeyboardKeysInternalQuery),
       Log("Remapping the 'Ctrl' key to 'Backspace'"),
-      ExecuteJsAt(kOsSettingsWebContentsId, kCtrlDropdownQuery,
+      ExecuteJsAt(webcontents_id_, kCtrlDropdownQuery,
                   "(el) => {el.selectedIndex = 5; el.dispatchEvent(new "
                   "Event('change'));}"),
-      ExecuteJsAt(kOsSettingsWebContentsId, kSearchboxQuery,
+      ExecuteJsAt(webcontents_id_, kSearchboxQuery,
                   "(el) => { el.focus(); el.select(); }"),
       Log("Entering 'redo' into the Settings search box"),
       EnterLowerCaseText("redo"), WaitForSearchboxContainsText("redo"),
diff --git a/chrome/browser/ui/webui/ash/settings/integration_tests/mouse_scroll_acceleration_interactive_uitest.cc b/chrome/browser/ui/webui/ash/settings/integration_tests/mouse_scroll_acceleration_interactive_uitest.cc
index 60e854c..c09e05c 100644
--- a/chrome/browser/ui/webui/ash/settings/integration_tests/mouse_scroll_acceleration_interactive_uitest.cc
+++ b/chrome/browser/ui/webui/ash/settings/integration_tests/mouse_scroll_acceleration_interactive_uitest.cc
@@ -2,26 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/constants/ash_features.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/settings_window_manager_chromeos.h"
-#include "chrome/test/base/chromeos/crosier/interactive_ash_test.h"
-#include "ui/base/interaction/element_identifier.h"
-#include "ui/events/devices/device_data_manager_test_api.h"
+#include "ash/webui/settings/public/constants/routes.mojom-forward.h"
+#include "chrome/browser/ui/webui/ash/settings/integration_tests/device_settings_base_test.h"
 #include "ui/events/devices/input_device.h"
 
 namespace ash {
 
 namespace {
 
-constexpr char kDeviceSectionPath[] = "device";
+const ui::InputDevice kMouse(3, ui::InputDeviceType::INPUT_DEVICE_USB, "mouse");
 
-class DeviceSettingsMouseInteractiveUiTest : public InteractiveAshTest {
+class DeviceSettingsMouseInteractiveUiTest : public DeviceSettingsBaseTest {
  public:
-  DeviceSettingsMouseInteractiveUiTest() {
-    feature_list_.InitAndEnableFeature(features::kInputDeviceSettingsSplit);
-  }
 
   // Query to pierce through Shadow DOM to find the mouse row.
   const DeepQuery kMouseRowQuery{
@@ -48,31 +40,13 @@
       "settings-per-device-mouse-subsection",
       "#mouseControlledScrolling",
   };
-
-  void SetUpOnMainThread() override {
-    InteractiveAshTest::SetUpOnMainThread();
-
-    // Set up context for element tracking for InteractiveBrowserTest.
-    SetupContextWidget();
-
-    // Ensure the OS Settings system web app (SWA) is installed.
-    InstallSystemApps();
-
-    // Initialize mouse.
-    ui::DeviceDataManagerTestApi().SetMouseDevices(
-        {ui::InputDevice(3, ui::InputDeviceType::INPUT_DEVICE_USB, "mouse")});
-    ui::DeviceDataManagerTestApi().OnDeviceListsComplete();
-  }
-
- protected:
-  base::test::ScopedFeatureList feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_F(DeviceSettingsMouseInteractiveUiTest,
                        MouseScrollAcceleration) {
-  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOsSettingsWebContentsId);
   DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kScrollingSpeedSliderDisabledEvent);
   DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kScrollingSpeedSliderEnabledEvent);
+  SetMouseDevices({kMouse});
 
   StateChange scrolling_speed_slider_disabled;
   scrolling_speed_slider_disabled.type =
@@ -89,21 +63,12 @@
   scrolling_speed_slider_enabled.test_function = "el => !el.disabled";
 
   RunTestSequence(
-      InstrumentNextTab(kOsSettingsWebContentsId, AnyBrowser()), Do([&]() {
-        chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
-            GetActiveUserProfile(), kDeviceSectionPath);
-      }),
-      WaitForShow(kOsSettingsWebContentsId),
-      Log("Waiting for per device section to load"),
-      WaitForWebContentsReady(kOsSettingsWebContentsId,
-                              chrome::GetOSSettingsUrl(kDeviceSectionPath)),
-      WaitForElementExists(kOsSettingsWebContentsId, kMouseRowQuery),
-      ClickElement(kOsSettingsWebContentsId, kMouseRowQuery),
-      WaitForStateChange(kOsSettingsWebContentsId,
-                         scrolling_speed_slider_disabled),
-      ClickElement(kOsSettingsWebContentsId, kControlledScrollingButtonQuery),
-      WaitForStateChange(kOsSettingsWebContentsId,
-                         scrolling_speed_slider_enabled));
+      LaunchSettingsApp(chromeos::settings::mojom::kDeviceSectionPath),
+      WaitForElementExists(webcontents_id_, kMouseRowQuery),
+      ClickElement(webcontents_id_, kMouseRowQuery),
+      WaitForStateChange(webcontents_id_, scrolling_speed_slider_disabled),
+      ClickElement(webcontents_id_, kControlledScrollingButtonQuery),
+      WaitForStateChange(webcontents_id_, scrolling_speed_slider_enabled));
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/webui/ash/settings/integration_tests/open_keyboard_subpage_interactive_uitest.cc b/chrome/browser/ui/webui/ash/settings/integration_tests/open_keyboard_subpage_interactive_uitest.cc
index 4f78d51..462977332 100644
--- a/chrome/browser/ui/webui/ash/settings/integration_tests/open_keyboard_subpage_interactive_uitest.cc
+++ b/chrome/browser/ui/webui/ash/settings/integration_tests/open_keyboard_subpage_interactive_uitest.cc
@@ -2,85 +2,30 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <vector>
-
-#include "ash/constants/ash_features.h"
-#include "base/files/file_path.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/settings_window_manager_chromeos.h"
-#include "chrome/test/base/chromeos/crosier/interactive_ash_test.h"
-#include "ui/base/interaction/element_identifier.h"
-#include "ui/events/devices/device_data_manager_test_api.h"
-#include "ui/events/devices/input_device.h"
-#include "ui/events/devices/keyboard_device.h"
+#include "ash/webui/settings/public/constants/routes.mojom-forward.h"
+#include "chrome/browser/ui/webui/ash/settings/integration_tests/device_settings_base_test.h"
 
 namespace ash {
 
 namespace {
 
-constexpr char kDeviceSectionPath[] = "device";
-
-class DeviceSettingsKeyboardInteractiveUiTest : public InteractiveAshTest {
+class DeviceSettingsKeyboardInteractiveUiTest : public DeviceSettingsBaseTest {
  public:
-  DeviceSettingsKeyboardInteractiveUiTest() {
-    feature_list_.InitAndEnableFeature(features::kInputDeviceSettingsSplit);
-  }
-
   // Query to pierce through Shadow DOM to find the touchpad row.
   const DeepQuery kKeyboardRowQuery{
       "os-settings-ui",       "os-settings-main",      "main-page-container",
       "settings-device-page", "#perDeviceKeyboardRow",
   };
-
-  // Query to pierce through Shadow DOM to find the Keyboard header.
-  const DeepQuery kKeyboardNameQuery{
-      "os-settings-ui",
-      "os-settings-main",
-      "main-page-container",
-      "settings-device-page",
-      "settings-per-device-keyboard",
-      "settings-per-device-keyboard-subsection",
-      "h2#keyboardName",
-  };
-
-  // InteractiveAshTest:
-  void SetUpOnMainThread() override {
-    InteractiveAshTest::SetUpOnMainThread();
-
-    // Set up context for element tracking for InteractiveBrowserTest.
-    SetupContextWidget();
-
-    // Ensure the OS Settings system web app (SWA) is installed.
-    InstallSystemApps();
-
-    // Initialize keyboard.
-    std::vector<ui::KeyboardDevice> keyboard_devices;
-    keyboard_devices.emplace_back(1, ui::InputDeviceType::INPUT_DEVICE_INTERNAL,
-                                  "keyboard");
-    ui::DeviceDataManagerTestApi().SetKeyboardDevices(keyboard_devices);
-    ui::DeviceDataManagerTestApi().OnDeviceListsComplete();
-  }
-
- protected:
-  base::test::ScopedFeatureList feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_F(DeviceSettingsKeyboardInteractiveUiTest,
                        OpenKeyboardSubpage) {
-  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOsSettingsWebContentsId);
   RunTestSequence(
-      InstrumentNextTab(kOsSettingsWebContentsId, AnyBrowser()), Do([&]() {
-        chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
-            GetActiveUserProfile(), kDeviceSectionPath);
-      }),
-      WaitForShow(kOsSettingsWebContentsId),
-      Log("Waiting for per device section to load"),
-      WaitForWebContentsReady(kOsSettingsWebContentsId,
-                              chrome::GetOSSettingsUrl(kDeviceSectionPath)),
-      WaitForElementExists(kOsSettingsWebContentsId, kKeyboardRowQuery),
-      ClickElement(kOsSettingsWebContentsId, kKeyboardRowQuery),
-      WaitForElementTextContains(kOsSettingsWebContentsId, kKeyboardNameQuery,
+      SetupInternalKeyboard(),
+      LaunchSettingsApp(chromeos::settings::mojom::kDeviceSectionPath),
+      WaitForElementExists(webcontents_id_, kKeyboardRowQuery),
+      ClickElement(webcontents_id_, kKeyboardRowQuery),
+      WaitForElementTextContains(webcontents_id_, kKeyboardNameQuery,
                                  "Built-in Keyboard"));
 }
 
diff --git a/chrome/browser/ui/webui/ash/settings/integration_tests/open_touchpad_subpage_interactive_uitest.cc b/chrome/browser/ui/webui/ash/settings/integration_tests/open_touchpad_subpage_interactive_uitest.cc
index a89e110f..a974c85 100644
--- a/chrome/browser/ui/webui/ash/settings/integration_tests/open_touchpad_subpage_interactive_uitest.cc
+++ b/chrome/browser/ui/webui/ash/settings/integration_tests/open_touchpad_subpage_interactive_uitest.cc
@@ -2,22 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/constants/ash_features.h"
+#include "ash/webui/settings/public/constants/routes.mojom-forward.h"
 #include "base/files/file_path.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/settings_window_manager_chromeos.h"
-#include "chrome/test/base/chromeos/crosier/interactive_ash_test.h"
-#include "ui/base/interaction/element_identifier.h"
-#include "ui/events/devices/device_data_manager_test_api.h"
-#include "ui/events/devices/input_device.h"
+#include "chrome/browser/ui/webui/ash/settings/integration_tests/device_settings_base_test.h"
 #include "ui/events/devices/touchpad_device.h"
 
 namespace ash {
 
 namespace {
 
-constexpr char kDeviceSectionPath[] = "device";
 const ui::TouchpadDevice kSampleTouchpadInternal(1,
                                                  ui::INPUT_DEVICE_INTERNAL,
                                                  "kSampleTouchpadInternal",
@@ -27,11 +20,8 @@
                                                  0x4444,
                                                  0);
 
-class DeviceSettingsTouchpadInteractiveUiTest : public InteractiveAshTest {
+class DeviceSettingsTouchpadInteractiveUiTest : public DeviceSettingsBaseTest {
  public:
-  DeviceSettingsTouchpadInteractiveUiTest() {
-    feature_list_.InitAndEnableFeature(features::kInputDeviceSettingsSplit);
-  }
 
   // Query to pierce through Shadow DOM to find the touchpad row.
   const DeepQuery kTouchpadRowQuery{
@@ -49,42 +39,16 @@
       "settings-per-device-touchpad-subsection",
       "h2#touchpadName",
   };
-
-  // InteractiveAshTest:
-  void SetUpOnMainThread() override {
-    InteractiveAshTest::SetUpOnMainThread();
-
-    // Set up context for element tracking for InteractiveBrowserTest.
-    SetupContextWidget();
-
-    // Ensure the OS Settings system web app (SWA) is installed.
-    InstallSystemApps();
-
-    // Initialize touchpad.
-    ui::DeviceDataManagerTestApi().SetTouchpadDevices(
-        {kSampleTouchpadInternal});
-    ui::DeviceDataManagerTestApi().OnDeviceListsComplete();
-  }
-
- protected:
-  base::test::ScopedFeatureList feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_F(DeviceSettingsTouchpadInteractiveUiTest,
                        OpenTouchpadSubpage) {
-  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOsSettingsWebContentsId);
+  SetTouchpadDevices({kSampleTouchpadInternal});
   RunTestSequence(
-      InstrumentNextTab(kOsSettingsWebContentsId, AnyBrowser()), Do([&]() {
-        chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
-            GetActiveUserProfile(), kDeviceSectionPath);
-      }),
-      WaitForShow(kOsSettingsWebContentsId),
-      Log("Waiting for per device section to load"),
-      WaitForWebContentsReady(kOsSettingsWebContentsId,
-                              chrome::GetOSSettingsUrl(kDeviceSectionPath)),
-      WaitForElementExists(kOsSettingsWebContentsId, kTouchpadRowQuery),
-      ClickElement(kOsSettingsWebContentsId, kTouchpadRowQuery),
-      WaitForElementTextContains(kOsSettingsWebContentsId, kTouchpadNameQuery,
+      LaunchSettingsApp(chromeos::settings::mojom::kDeviceSectionPath),
+      WaitForElementExists(webcontents_id_, kTouchpadRowQuery),
+      ClickElement(webcontents_id_, kTouchpadRowQuery),
+      WaitForElementTextContains(webcontents_id_, kTouchpadNameQuery,
                                  "Built-in Touchpad"));
 }
 
diff --git a/chrome/browser/ui/webui/ash/settings/integration_tests/swap_primary_mouse_button_interactive_uitest.cc b/chrome/browser/ui/webui/ash/settings/integration_tests/swap_primary_mouse_button_interactive_uitest.cc
index 0567eac4..b381fd7 100644
--- a/chrome/browser/ui/webui/ash/settings/integration_tests/swap_primary_mouse_button_interactive_uitest.cc
+++ b/chrome/browser/ui/webui/ash/settings/integration_tests/swap_primary_mouse_button_interactive_uitest.cc
@@ -2,29 +2,24 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/constants/ash_features.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/settings_window_manager_chromeos.h"
-#include "chrome/test/base/chromeos/crosier/interactive_ash_test.h"
-#include "ui/base/interaction/element_identifier.h"
-#include "ui/events/devices/device_data_manager_test_api.h"
+#include "ash/webui/settings/public/constants/routes.mojom-forward.h"
+#include "chrome/browser/ui/webui/ash/settings/integration_tests/device_settings_base_test.h"
 #include "ui/events/devices/input_device.h"
 
 namespace ash {
 
 namespace {
 
-constexpr char kDeviceSectionPath[] = "device";
+const ui::InputDevice kMouse(3, ui::InputDeviceType::INPUT_DEVICE_USB, "mouse");
 
 class DeviceSettingsSwapPrimaryMouseButtonInteractiveUiTest
-    : public InteractiveAshTest {
+    : public DeviceSettingsBaseTest {
  public:
   DeviceSettingsSwapPrimaryMouseButtonInteractiveUiTest() {
+    feature_list_.Reset();
     feature_list_.InitWithFeatures({features::kInputDeviceSettingsSplit},
                                    {features::kPeripheralCustomization});
   }
-
   // Query to pierce through Shadow DOM to find the mouse row.
   const DeepQuery kMouseRowQuery{
       "os-settings-ui",       "os-settings-main",   "main-page-container",
@@ -52,30 +47,12 @@
       "#mouseAcceleration",
       "#control",
   };
-
-  void SetUpOnMainThread() override {
-    InteractiveAshTest::SetUpOnMainThread();
-
-    // Set up context for element tracking for InteractiveBrowserTest.
-    SetupContextWidget();
-
-    // Ensure the OS Settings system web app (SWA) is installed.
-    InstallSystemApps();
-
-    // Initialize mouse.
-    ui::DeviceDataManagerTestApi().SetMouseDevices(
-        {ui::InputDevice(3, ui::InputDeviceType::INPUT_DEVICE_USB, "mouse")});
-    ui::DeviceDataManagerTestApi().OnDeviceListsComplete();
-  }
-
- protected:
-  base::test::ScopedFeatureList feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_F(DeviceSettingsSwapPrimaryMouseButtonInteractiveUiTest,
                        SwapPrimaryMouseButton) {
-  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOsSettingsWebContentsId);
   DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kCursorAccelerationToggleEnabledEvent);
+  SetMouseDevices({kMouse});
   StateChange cursor_acceleration_toggle_enabled;
   cursor_acceleration_toggle_enabled.type =
       StateChange::Type::kExistsAndConditionTrue;
@@ -85,28 +62,20 @@
   cursor_acceleration_toggle_enabled.test_function = "el => !el.disabled";
 
   RunTestSequence(
-      InstrumentNextTab(kOsSettingsWebContentsId, AnyBrowser()), Do([&]() {
-        chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
-            GetActiveUserProfile(), kDeviceSectionPath);
-      }),
-      WaitForShow(kOsSettingsWebContentsId),
-      WaitForWebContentsReady(kOsSettingsWebContentsId,
-                              chrome::GetOSSettingsUrl(kDeviceSectionPath)),
+      LaunchSettingsApp(chromeos::settings::mojom::kDeviceSectionPath),
       Log("Waiting for per device mouse row to be visible"),
-      WaitForElementExists(kOsSettingsWebContentsId, kMouseRowQuery),
-      ClickElement(kOsSettingsWebContentsId, kMouseRowQuery),
+      WaitForElementExists(webcontents_id_, kMouseRowQuery),
+      ClickElement(webcontents_id_, kMouseRowQuery),
       Log("Waiting for swap primary mouse toggle to be visible"),
-      WaitForElementExists(kOsSettingsWebContentsId,
-                           kMouseSwapButtonDropdownQuery),
+      WaitForElementExists(webcontents_id_, kMouseSwapButtonDropdownQuery),
       Log("Selecting 'Right button' from the dropdown menu"),
-      ExecuteJsAt(kOsSettingsWebContentsId, kMouseSwapButtonDropdownQuery,
+      ExecuteJsAt(webcontents_id_, kMouseSwapButtonDropdownQuery,
                   "(el) => {el.selectedIndex = 1; el.dispatchEvent(new "
                   "Event('change'));}"),
       Log("Verifying that right clicking behavior has changed"),
-      MoveMouseTo(kOsSettingsWebContentsId, kCursorAcceleartorToggleQuery),
+      MoveMouseTo(webcontents_id_, kCursorAcceleartorToggleQuery),
       ClickMouse(ui_controls::RIGHT),
-      WaitForStateChange(kOsSettingsWebContentsId,
-                         cursor_acceleration_toggle_enabled));
+      WaitForStateChange(webcontents_id_, cursor_acceleration_toggle_enabled));
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/webui/ash/settings/integration_tests/trackpoint_enabled_interactive_uitest.cc b/chrome/browser/ui/webui/ash/settings/integration_tests/trackpoint_enabled_interactive_uitest.cc
index edd849c..c46b76f 100644
--- a/chrome/browser/ui/webui/ash/settings/integration_tests/trackpoint_enabled_interactive_uitest.cc
+++ b/chrome/browser/ui/webui/ash/settings/integration_tests/trackpoint_enabled_interactive_uitest.cc
@@ -2,33 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/constants/ash_features.h"
-#include "base/files/file_path.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/settings_window_manager_chromeos.h"
-#include "chrome/test/base/chromeos/crosier/interactive_ash_test.h"
-#include "ui/base/interaction/element_identifier.h"
-#include "ui/events/devices/device_data_manager_test_api.h"
-#include "ui/events/devices/input_device.h"
-#include "ui/events/devices/touchpad_device.h"
+#include "ash/webui/settings/public/constants/routes.mojom-forward.h"
+#include "chrome/browser/ui/webui/ash/settings/integration_tests/device_settings_base_test.h"
 
 namespace ash {
 
 namespace {
 
-constexpr char kDeviceSectionPath[] = "device";
 const ui::InputDevice kSamplePointingStickInternal(
     2,
     ui::INPUT_DEVICE_INTERNAL,
     "kSamplePointingStickInternal");
 
-class DeviceSettingsTrackpointInteractiveUiTest : public InteractiveAshTest {
+class DeviceSettingsTrackpointInteractiveUiTest
+    : public DeviceSettingsBaseTest {
  public:
-  DeviceSettingsTrackpointInteractiveUiTest() {
-    feature_list_.InitAndEnableFeature(features::kInputDeviceSettingsSplit);
-  }
-
   // Query to pierce through Shadow DOM to find the pointing stick row.
   const DeepQuery kPointingStickRowQuery{
       "os-settings-ui",
@@ -48,42 +36,16 @@
       "settings-per-device-pointing-stick-subsection",
       "h2#pointingStickName",
   };
-
-  // InteractiveAshTest:
-  void SetUpOnMainThread() override {
-    InteractiveAshTest::SetUpOnMainThread();
-
-    // Set up context for element tracking for InteractiveBrowserTest.
-    SetupContextWidget();
-
-    // Ensure the OS Settings system web app (SWA) is installed.
-    InstallSystemApps();
-
-    // Initialize pointing stick.
-    ui::DeviceDataManagerTestApi().SetPointingStickDevices(
-        {kSamplePointingStickInternal});
-    ui::DeviceDataManagerTestApi().OnDeviceListsComplete();
-  }
-
- protected:
-  base::test::ScopedFeatureList feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_F(DeviceSettingsTrackpointInteractiveUiTest,
                        TrackpointEnabled) {
-  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOsSettingsWebContentsId);
+  SetPointingStickDevices({kSamplePointingStickInternal});
   RunTestSequence(
-      InstrumentNextTab(kOsSettingsWebContentsId, AnyBrowser()), Do([&]() {
-        chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
-            GetActiveUserProfile(), kDeviceSectionPath);
-      }),
-      WaitForShow(kOsSettingsWebContentsId),
-      WaitForWebContentsReady(kOsSettingsWebContentsId,
-                              chrome::GetOSSettingsUrl(kDeviceSectionPath)),
-      WaitForElementExists(kOsSettingsWebContentsId, kPointingStickRowQuery),
-      ClickElement(kOsSettingsWebContentsId, kPointingStickRowQuery),
-      WaitForElementTextContains(kOsSettingsWebContentsId,
-                                 kPointingStickNameQuery,
+      LaunchSettingsApp(chromeos::settings::mojom::kDeviceSectionPath),
+      WaitForElementExists(webcontents_id_, kPointingStickRowQuery),
+      ClickElement(webcontents_id_, kPointingStickRowQuery),
+      WaitForElementTextContains(webcontents_id_, kPointingStickNameQuery,
                                  "Built-in TrackPoint"));
 }
 
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index c18ef42..3444445 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -2007,6 +2007,8 @@
       {"topicsPageAllowTopic", IDS_SETTINGS_TOPICS_PAGE_ALLOW_TOPIC},
       {"topicsPageAllowTopicA11yLabel",
        IDS_SETTINGS_TOPICS_PAGE_ALLOW_TOPIC_A11Y_LABEL},
+      {"topicsPageUnblockTopicA11yLabel",
+       IDS_SETTINGS_TOPICS_PAGE_UNBLOCK_TOPIC_A11Y_LABEL},
       {"topicsPageCurrentTopicsDescriptionLearnMoreA11yLabel",
        IDS_SETTINGS_TOPICS_PAGE_CURRENT_TOPICS_DESCRIPTION_LEARN_MORE_A11Y_LABEL},
       {"fledgePageTitle", IDS_SETTINGS_FLEDGE_PAGE_TITLE},
diff --git a/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc b/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc
index ef5e666..753c060 100644
--- a/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc
+++ b/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc
@@ -179,7 +179,10 @@
     content::WebUI* web_ui,
     ui::MojoBubbleWebUIController* webui_controller,
     MetricsReporter* metrics_reporter)
-    : receiver_(this, std::move(receiver)),
+    : optimization_guide::SettingsEnabledObserver(
+          optimization_guide::proto::ModelExecutionFeature::
+              MODEL_EXECUTION_FEATURE_TAB_ORGANIZATION),
+      receiver_(this, std::move(receiver)),
       page_(std::move(page)),
       web_ui_(web_ui),
       webui_controller_(webui_controller),
@@ -196,13 +199,6 @@
       tab_search_prefs::kTabSearchTabIndex,
       base::BindRepeating(&TabSearchPageHandler::NotifyTabIndexPrefChanged,
                           base::Unretained(this), profile));
-  pref_change_registrar_.Add(
-      optimization_guide::prefs::GetSettingEnabledPrefName(
-          optimization_guide::proto::ModelExecutionFeature::
-              MODEL_EXECUTION_FEATURE_TAB_ORGANIZATION),
-      base::BindRepeating(
-          &TabSearchPageHandler::NotifySettingEnabledPrefChanged,
-          base::Unretained(this), profile));
   if (TabOrganizationUtils::GetInstance()->IsEnabled(profile)) {
     organization_service_ =
         TabOrganizationServiceFactory::GetForProfile(profile);
@@ -210,6 +206,12 @@
       organization_service_->AddObserver(this);
     }
   }
+  optimization_guide_keyed_service_ =
+      OptimizationGuideKeyedServiceFactory::GetForProfile(profile);
+  if (optimization_guide_keyed_service_) {
+    optimization_guide_keyed_service_->AddModelExecutionSettingsEnabledObserver(
+        this);
+  }
 }
 
 TabSearchPageHandler::~TabSearchPageHandler() {
@@ -225,6 +227,10 @@
   for (TabOrganizationSession* session : listened_sessions_) {
     session->RemoveObserver(this);
   }
+  if (optimization_guide_keyed_service_) {
+    optimization_guide_keyed_service_
+        ->RemoveModelExecutionSettingsEnabledObserver(this);
+  }
   pref_change_registrar_.Reset();
 }
 
@@ -1024,22 +1030,6 @@
   page_->TabSearchTabIndexChanged(index);
 }
 
-void TabSearchPageHandler::NotifySettingEnabledPrefChanged(Profile* profile) {
-  bool enabled = false;
-  if (TabOrganizationUtils::GetInstance()->IsEnabled(profile)) {
-    organization_service_ =
-        TabOrganizationServiceFactory::GetForProfile(profile);
-    if (organization_service_) {
-      enabled = true;
-      organization_service_->AddObserver(this);
-    }
-  } else if (organization_service_) {
-    organization_service_->RemoveObserver(this);
-    organization_service_ = nullptr;
-  }
-  page_->TabOrganizationEnabledChanged(enabled);
-}
-
 bool TabSearchPageHandler::IsWebContentsVisible() {
   auto visibility = web_ui_->GetWebContents()->GetVisibility();
   return visibility == content::Visibility::VISIBLE ||
@@ -1179,6 +1169,25 @@
   OnTabOrganizationSessionUpdated(session);
 }
 
+void TabSearchPageHandler::OnChangeInFeatureCurrentlyEnabledState(
+    bool is_now_enabled) {
+  Profile* const profile = Profile::FromWebUI(web_ui_);
+  // This logic is slightly more strict than is_now_enabled, may make a
+  // difference in some edge cases.
+  bool enabled = TabOrganizationUtils::GetInstance()->IsEnabled(profile);
+  if (enabled) {
+    organization_service_ =
+        TabOrganizationServiceFactory::GetForProfile(profile);
+    if (organization_service_) {
+      organization_service_->AddObserver(this);
+    }
+  } else if (organization_service_) {
+    organization_service_->RemoveObserver(this);
+    organization_service_ = nullptr;
+  }
+  page_->TabOrganizationEnabledChanged(enabled && organization_service_);
+}
+
 bool TabSearchPageHandler::ShouldTrackBrowser(Browser* browser) {
   return browser->profile() == Profile::FromWebUI(web_ui_) &&
          browser->type() == Browser::Type::TYPE_NORMAL;
diff --git a/chrome/browser/ui/webui/tab_search/tab_search_page_handler.h b/chrome/browser/ui/webui/tab_search/tab_search_page_handler.h
index 8c5a509a9..0708fb7 100644
--- a/chrome/browser/ui/webui/tab_search/tab_search_page_handler.h
+++ b/chrome/browser/ui/webui/tab_search/tab_search_page_handler.h
@@ -18,6 +18,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "chrome/browser/ui/webui/tab_search/tab_search.mojom.h"
+#include "components/optimization_guide/core/model_execution/settings_enabled_observer.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/sessions/core/tab_restore_service.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -30,6 +31,7 @@
 class Browser;
 class MetricsReporter;
 class TabOrganizationService;
+class OptimizationGuideKeyedService;
 
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
@@ -47,11 +49,13 @@
   kMaxValue = kCollapse,
 };
 
-class TabSearchPageHandler : public tab_search::mojom::PageHandler,
-                             public TabStripModelObserver,
-                             public BrowserTabStripTrackerDelegate,
-                             public TabOrganizationSession::Observer,
-                             public TabOrganizationObserver {
+class TabSearchPageHandler
+    : public tab_search::mojom::PageHandler,
+      public TabStripModelObserver,
+      public BrowserTabStripTrackerDelegate,
+      public TabOrganizationSession::Observer,
+      public TabOrganizationObserver,
+      public optimization_guide::SettingsEnabledObserver {
  public:
   TabSearchPageHandler(
       mojo::PendingReceiver<tab_search::mojom::PageHandler> receiver,
@@ -129,6 +133,9 @@
   void OnSessionCreated(const Browser* browser,
                         TabOrganizationSession* session) override;
 
+  // SettingsEnabledObserver
+  void OnChangeInFeatureCurrentlyEnabledState(bool is_now_enabled) override;
+
  protected:
   void SetTimerForTesting(std::unique_ptr<base::RetainingOneShotTimer> timer);
 
@@ -191,8 +198,6 @@
 
   void NotifyTabIndexPrefChanged(const Profile* profile);
 
-  void NotifySettingEnabledPrefChanged(Profile* profile);
-
   mojo::Receiver<tab_search::mojom::PageHandler> receiver_;
   mojo::Remote<tab_search::mojom::Page> page_;
   const raw_ptr<content::WebUI> web_ui_;
@@ -203,6 +208,7 @@
   std::unique_ptr<base::RetainingOneShotTimer> debounce_timer_;
   raw_ptr<TabOrganizationService> organization_service_;
   PrefChangeRegistrar pref_change_registrar_;
+  raw_ptr<OptimizationGuideKeyedService> optimization_guide_keyed_service_;
 
   // Tracks how many times |CloseTab()| has been evoked for the currently open
   // instance of Tab Search for logging in UMA.
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index ad0a6e4..7bf567a 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1707306295-00c8250db9526c7e49afdcc16fa0135d1641606e.profdata
+chrome-linux-main-1707328797-1c3efd3955b677a6239ff03320de90c9ee7a2b4c.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index c7fb9a4..299221a 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1707321518-4ea1c2f63aff5ddfadc4f5249f2867c6a42997e3.profdata
+chrome-mac-arm-main-1707343088-770b4a8ca743711cf1a1eab1f3fa27965e9c83f9.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 22d5857..e5b25bdb 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -350,6 +350,9 @@
 BASE_FEATURE(kChromeAppsDeprecation,
              "ChromeAppsDeprecation",
              base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kShortcutsNotApps,
+             "ShortcutsNotApps",
+             base::FEATURE_DISABLED_BY_DEFAULT);
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) ||
         // BUILDFLAG(IS_FUCHSIA)
 
@@ -1625,6 +1628,10 @@
 BASE_FEATURE(kWebAppSyncGeneratedIconUpdateFix,
              "WebAppSyncGeneratedIconUpdateFix",
              base::FEATURE_ENABLED_BY_DEFAULT);
+
+BASE_FEATURE(kWebAppUniversalInstall,
+             "WebAppUniversalInstall",
+             base::FEATURE_DISABLED_BY_DEFAULT);
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 BASE_FEATURE(kWebAppManifestPolicyAppIdentityUpdate,
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 8a3fbabb..950894cb 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -226,6 +226,8 @@
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
     BUILDFLAG(IS_FUCHSIA)
 COMPONENT_EXPORT(CHROME_FEATURES) BASE_DECLARE_FEATURE(kChromeAppsDeprecation);
+COMPONENT_EXPORT(CHROME_FEATURES)
+BASE_DECLARE_FEATURE(kShortcutsNotApps);
 #endif
 
 COMPONENT_EXPORT(CHROME_FEATURES)
@@ -1022,6 +1024,9 @@
 
 COMPONENT_EXPORT(CHROME_FEATURES)
 BASE_DECLARE_FEATURE(kWebAppSyncGeneratedIconUpdateFix);
+
+COMPONENT_EXPORT(CHROME_FEATURES)
+BASE_DECLARE_FEATURE(kWebAppUniversalInstall);
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 COMPONENT_EXPORT(CHROME_FEATURES)
diff --git a/chrome/common/google_url_loader_throttle_unittest.cc b/chrome/common/google_url_loader_throttle_unittest.cc
index 7249b09..eaad51464 100644
--- a/chrome/common/google_url_loader_throttle_unittest.cc
+++ b/chrome/common/google_url_loader_throttle_unittest.cc
@@ -391,7 +391,7 @@
   CallThrottleAndVerifyDeferExpectation(
       /*expect_defer=*/true, kGoogleSubdomainURL);
   const auto kResumeTrigger =
-      chrome::mojom::ResumeBlockedRequestsTrigger::kNetworkConnectionOffline;
+      chrome::mojom::ResumeBlockedRequestsTrigger::kCookieRefreshFetchFailure;
   UnblockRequestAndVerifyCallbackAction(
       BoundSessionRequestThrottledHandler::UnblockAction::kResume,
       /*is_expected_navigation=*/false, kResumeTrigger);
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 2c0443f..82b43b8 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -3017,6 +3017,11 @@
 
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
+// Defines administrator-set availability of Chrome for Testing.
+inline constexpr char kChromeForTestingAllowed[] = "chrome_for_testing.allowed";
+#endif
+
 // *************** SERVICE PREFS ***************
 // These are attached to the service process.
 
diff --git a/chrome/common/renderer_configuration.mojom b/chrome/common/renderer_configuration.mojom
index 18302e0..6726ff6 100644
--- a/chrome/common/renderer_configuration.mojom
+++ b/chrome/common/renderer_configuration.mojom
@@ -39,7 +39,7 @@
   kObservedFreshCookies = 0,
   kCookieRefreshFetchSuccess = 1,
   kCookieRefreshFetchFailure = 2,
-  kNetworkConnectionOffline = 3,
+  // kNetworkConnectionOffline = 3, Deprecated
   kTimeout = 4,
   kShutdownOrSessionTermination = 5,
   kCookieAlreadyFresh = 6,
diff --git a/chrome/services/qrcode_generator/qrcode_generator_service_impl.cc b/chrome/services/qrcode_generator/qrcode_generator_service_impl.cc
index 830884a..30a5fc78 100644
--- a/chrome/services/qrcode_generator/qrcode_generator_service_impl.cc
+++ b/chrome/services/qrcode_generator/qrcode_generator_service_impl.cc
@@ -273,15 +273,14 @@
     return;
   }
 
-  std::optional<qr_code_generator::QRCodeGenerator::GeneratedCode> qr_data;
+  std::optional<qr_code_generator::GeneratedCode> qr_data;
   {
     base::TimeTicks start_time = base::TimeTicks::Now();
     // The QR version (i.e. size) must be >= 5 because otherwise the dino
     // painted over the middle covers too much of the code to be decodable.
     constexpr int kMinimumQRVersion = 5;
-    qr_code_generator::QRCodeGenerator qr;
-    qr_data = qr.Generate(base::as_bytes(base::make_span(request->data)),
-                          kMinimumQRVersion);
+    qr_data = qr_code_generator::Generate(
+        base::as_bytes(base::make_span(request->data)), kMinimumQRVersion);
     base::UmaHistogramMicrosecondsTimes(
         "Sharing.QRCodeGeneration.Duration.BytesToQrPixels2",
         base::TimeTicks::Now() - start_time);
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index ac5fb5cd8..fe0157ae 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -7468,6 +7468,7 @@
       "//components/signin/public/android:signin_java_test_support",
       "//components/supervised_user/core/browser/proto",
       "//components/supervised_user/core/common",
+      "//components/translate/content/android:android",
       "//components/ukm:test_support",
       "//components/webapk:proto",
       "//components/webapps/browser",
@@ -11080,6 +11081,8 @@
           "../browser/ui/web_applications/test/system_web_app_interactive_uitest.cc",
           "../browser/ui/webui/ash/firmware_update_ui/integration_tests/firmware_update_interactive_uitest.cc",
           "../browser/ui/webui/ash/settings/integration_tests/add_new_keyboard_interactive_uitest.cc",
+          "../browser/ui/webui/ash/settings/integration_tests/device_settings_base_test.cc",
+          "../browser/ui/webui/ash/settings/integration_tests/device_settings_base_test.h",
           "../browser/ui/webui/ash/settings/integration_tests/keyboard_six_pack_keys_interactive_uitest.cc",
           "../browser/ui/webui/ash/settings/integration_tests/mouse_scroll_acceleration_interactive_uitest.cc",
           "../browser/ui/webui/ash/settings/integration_tests/open_keyboard_subpage_interactive_uitest.cc",
diff --git a/chrome/test/data/webui/.eslintrc.js b/chrome/test/data/webui/.eslintrc.js
index ccaca93..0ba7758 100644
--- a/chrome/test/data/webui/.eslintrc.js
+++ b/chrome/test/data/webui/.eslintrc.js
@@ -3,7 +3,9 @@
 // found in the LICENSE file.
 
 module.exports = {
+  // clang-format off
   'env': {'browser': true, 'es6': true},
+  'extends': '../../../../ui/webui/resources/tools/eslint_import_type.config.js',
   'rules': {
     'no-restricted-properties': [
       'error', {
@@ -23,4 +25,5 @@
     ],
     'eqeqeq': ['error', 'always', {'null': 'ignore'}],
   },
+  // clang-format on
 };
diff --git a/chrome/test/data/webui/access_code_cast/access_code_cast_app_test.ts b/chrome/test/data/webui/access_code_cast/access_code_cast_app_test.ts
index 45ddf1c9..85ac79c 100644
--- a/chrome/test/data/webui/access_code_cast/access_code_cast_app_test.ts
+++ b/chrome/test/data/webui/access_code_cast/access_code_cast_app_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://access-code-cast/access_code_cast.js';
 
-import {AccessCodeCastElement} from 'chrome://access-code-cast/access_code_cast.js';
+import type {AccessCodeCastElement} from 'chrome://access-code-cast/access_code_cast.js';
 import {AddSinkResultCode, CastDiscoveryMethod} from 'chrome://access-code-cast/access_code_cast.mojom-webui.js';
 import {BrowserProxy} from 'chrome://access-code-cast/browser_proxy.js';
 import {RouteRequestResultCode} from 'chrome://access-code-cast/route_request_result_code.mojom-webui.js';
diff --git a/chrome/test/data/webui/access_code_cast/error_message_test.ts b/chrome/test/data/webui/access_code_cast/error_message_test.ts
index 9003c24..fd88ebf 100644
--- a/chrome/test/data/webui/access_code_cast/error_message_test.ts
+++ b/chrome/test/data/webui/access_code_cast/error_message_test.ts
@@ -5,7 +5,7 @@
 import 'chrome://access-code-cast/error_message/error_message.js';
 
 import {AddSinkResultCode} from 'chrome://access-code-cast/access_code_cast.mojom-webui.js';
-import {ErrorMessageElement} from 'chrome://access-code-cast/error_message/error_message.js';
+import type {ErrorMessageElement} from 'chrome://access-code-cast/error_message/error_message.js';
 import {RouteRequestResultCode} from 'chrome://access-code-cast/route_request_result_code.mojom-webui.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/access_code_cast/passcode_input_test.ts b/chrome/test/data/webui/access_code_cast/passcode_input_test.ts
index 13005fef..82275b11 100644
--- a/chrome/test/data/webui/access_code_cast/passcode_input_test.ts
+++ b/chrome/test/data/webui/access_code_cast/passcode_input_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://access-code-cast/passcode_input/passcode_input.js';
 
-import {PasscodeInputElement} from 'chrome://access-code-cast/passcode_input/passcode_input';
+import type {PasscodeInputElement} from 'chrome://access-code-cast/passcode_input/passcode_input';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 
diff --git a/chrome/test/data/webui/access_code_cast/test_access_code_cast_browser_proxy.ts b/chrome/test/data/webui/access_code_cast/test_access_code_cast_browser_proxy.ts
index b2e26fe..032c12e 100644
--- a/chrome/test/data/webui/access_code_cast/test_access_code_cast_browser_proxy.ts
+++ b/chrome/test/data/webui/access_code_cast/test_access_code_cast_browser_proxy.ts
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {AddSinkResultCode, CastDiscoveryMethod, PageCallbackRouter} from 'chrome://access-code-cast/access_code_cast.mojom-webui.js';
-import {RouteRequestResultCode} from 'chrome://access-code-cast/route_request_result_code.mojom-webui.js';
+import type {AddSinkResultCode, CastDiscoveryMethod} from 'chrome://access-code-cast/access_code_cast.mojom-webui.js';
+import {PageCallbackRouter} from 'chrome://access-code-cast/access_code_cast.mojom-webui.js';
+import type {RouteRequestResultCode} from 'chrome://access-code-cast/route_request_result_code.mojom-webui.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 class TestAccessCodeCastBrowserProxy extends TestBrowserProxy {
diff --git a/chrome/test/data/webui/app_home/app_list_test.ts b/chrome/test/data/webui/app_home/app_list_test.ts
index 3b1966b..a97b409 100644
--- a/chrome/test/data/webui/app_home/app_list_test.ts
+++ b/chrome/test/data/webui/app_home/app_list_test.ts
@@ -6,13 +6,14 @@
 import 'chrome://apps/app_item.js';
 import 'chrome://apps/deprecated_apps_link.js';
 
-import {AppInfo, PageRemote, RunOnOsLoginMode} from 'chrome://apps/app_home.mojom-webui.js';
-import {AppHomeEmptyPageElement} from 'chrome://apps/app_home_empty_page.js';
+import type {AppInfo, PageRemote} from 'chrome://apps/app_home.mojom-webui.js';
+import {RunOnOsLoginMode} from 'chrome://apps/app_home.mojom-webui.js';
+import type {AppHomeEmptyPageElement} from 'chrome://apps/app_home_empty_page.js';
 import {AppHomeUserAction} from 'chrome://apps/app_home_utils.js';
-import {AppListElement} from 'chrome://apps/app_list.js';
+import type {AppListElement} from 'chrome://apps/app_list.js';
 import {BrowserProxy} from 'chrome://apps/browser_proxy.js';
-import {DeprecatedAppsLinkElement} from 'chrome://apps/deprecated_apps_link.js';
-import {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
+import type {DeprecatedAppsLinkElement} from 'chrome://apps/deprecated_apps_link.js';
+import type {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/app_home/test_app_home_browser_proxy.ts b/chrome/test/data/webui/app_home/test_app_home_browser_proxy.ts
index 9def166..c64bab2 100644
--- a/chrome/test/data/webui/app_home/test_app_home_browser_proxy.ts
+++ b/chrome/test/data/webui/app_home/test_app_home_browser_proxy.ts
@@ -1,8 +1,9 @@
 // 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.
-import {AppInfo, ClickEvent, PageCallbackRouter, PageHandlerInterface, PageRemote, RunOnOsLoginMode} from 'chrome://apps/app_home.mojom-webui.js';
-import {BrowserProxy} from 'chrome://apps/browser_proxy.js';
+import type {AppInfo, ClickEvent, PageHandlerInterface, PageRemote, RunOnOsLoginMode} from 'chrome://apps/app_home.mojom-webui.js';
+import {PageCallbackRouter} from 'chrome://apps/app_home.mojom-webui.js';
+import type {BrowserProxy} from 'chrome://apps/browser_proxy.js';
 import {UserDisplayMode} from 'chrome://apps/user_display_mode.mojom-webui.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/app_settings/app_management_test_support.ts b/chrome/test/data/webui/app_settings/app_management_test_support.ts
index 5278f1d..a26a206 100644
--- a/chrome/test/data/webui/app_settings/app_management_test_support.ts
+++ b/chrome/test/data/webui/app_settings/app_management_test_support.ts
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {App, AppType, InstallReason, InstallSource, PageCallbackRouter, PageHandlerRemote, PermissionType, RunOnOsLoginMode, TriState, WindowMode} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
-import {BrowserProxy} from 'chrome://resources/cr_components/app_management/browser_proxy.js';
+import type {App} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
+import {AppType, InstallReason, InstallSource, PageCallbackRouter, PageHandlerRemote, PermissionType, RunOnOsLoginMode, TriState, WindowMode} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
+import type {BrowserProxy} from 'chrome://resources/cr_components/app_management/browser_proxy.js';
 import {createTriStatePermission} from 'chrome://resources/cr_components/app_management/permission_util.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 import {TestMock} from 'chrome://webui-test/test_mock.js';
diff --git a/chrome/test/data/webui/app_settings/app_test.ts b/chrome/test/data/webui/app_settings/app_test.ts
index 1cbbf942..5b03ed5 100644
--- a/chrome/test/data/webui/app_settings/app_test.ts
+++ b/chrome/test/data/webui/app_settings/app_test.ts
@@ -4,8 +4,9 @@
 
 import 'chrome://app-settings/web_app_settings.js';
 
-import { App, AppManagementPermissionItemElement, AppManagementSupportedLinksItemElement, AppManagementSupportedLinksOverlappingAppsDialogElement, AppManagementToggleRowElement, AppType, BrowserProxy, createTriStatePermission, getPermissionValueBool, InstallReason, InstallSource, PermissionType, PermissionTypeIndex, RunOnOsLoginMode, TriState, WebAppSettingsAppElement, WindowMode } from 'chrome://app-settings/web_app_settings.js';
-import {CrRadioButtonElement} from 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button.js';
+import type {App, AppManagementPermissionItemElement, AppManagementSupportedLinksItemElement, AppManagementSupportedLinksOverlappingAppsDialogElement, AppManagementToggleRowElement, PermissionTypeIndex, WebAppSettingsAppElement} from 'chrome://app-settings/web_app_settings.js';
+import {AppType, BrowserProxy, createTriStatePermission, getPermissionValueBool, InstallReason, InstallSource, PermissionType, RunOnOsLoginMode, TriState, WindowMode} from 'chrome://app-settings/web_app_settings.js';
+import type {CrRadioButtonElement} from 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button.js';
 import {assertEquals, assertFalse, assertNull, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 
diff --git a/chrome/test/data/webui/app_settings/file_handling_item_test.ts b/chrome/test/data/webui/app_settings/file_handling_item_test.ts
index 48d1996..2da3af3 100644
--- a/chrome/test/data/webui/app_settings/file_handling_item_test.ts
+++ b/chrome/test/data/webui/app_settings/file_handling_item_test.ts
@@ -6,9 +6,9 @@
 import 'chrome://app-settings/file_handling_item.js';
 import 'chrome://resources/cr_components/localized_link/localized_link.js';
 
-import {App} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
+import type {AppManagementFileHandlingItemElement} from 'chrome://app-settings/file_handling_item.js';
+import type {App} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
 import {BrowserProxy} from 'chrome://resources/cr_components/app_management/browser_proxy.js';
-import {AppManagementFileHandlingItemElement} from 'chrome://app-settings/file_handling_item.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/app_settings/permission_item_test.ts b/chrome/test/data/webui/app_settings/permission_item_test.ts
index efdaffc..b54b63f 100644
--- a/chrome/test/data/webui/app_settings/permission_item_test.ts
+++ b/chrome/test/data/webui/app_settings/permission_item_test.ts
@@ -5,10 +5,10 @@
 /** @fileoverview Test suite for app-manageemnt-permission-item. */
 import 'chrome://app-settings/permission_item.js';
 
+import type {AppManagementPermissionItemElement} from 'chrome://app-settings/permission_item.js';
 import {TriState} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
 import {BrowserProxy} from 'chrome://resources/cr_components/app_management/browser_proxy.js';
 import {AppManagementUserAction} from 'chrome://resources/cr_components/app_management/constants.js';
-import {AppManagementPermissionItemElement} from 'chrome://app-settings/permission_item.js';
 import {getPermissionValueBool} from 'chrome://resources/cr_components/app_management/util.js';
 import {assertEquals, assertFalse} from 'chrome://webui-test/chai_assert.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/app_settings/supported_links_item_test.ts b/chrome/test/data/webui/app_settings/supported_links_item_test.ts
index ed71278a..f93fbf2 100644
--- a/chrome/test/data/webui/app_settings/supported_links_item_test.ts
+++ b/chrome/test/data/webui/app_settings/supported_links_item_test.ts
@@ -5,17 +5,19 @@
 /** @fileoverview Test suite for AppManagementSupportedLinksItemElement. */
 import 'chrome://app-settings/supported_links_item.js';
 
-import {App, AppType, WindowMode} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
+import type {AppManagementSupportedLinksItemElement} from 'chrome://app-settings/supported_links_item.js';
+import type {App} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
+import {AppType, WindowMode} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
 import {BrowserProxy} from 'chrome://resources/cr_components/app_management/browser_proxy.js';
-import {AppMap} from 'chrome://resources/cr_components/app_management/constants.js';
-import {AppManagementSupportedLinksItemElement} from 'chrome://app-settings/supported_links_item.js';
-import {CrRadioGroupElement} from 'chrome://resources/cr_elements/cr_radio_group/cr_radio_group.js';
+import type {AppMap} from 'chrome://resources/cr_components/app_management/constants.js';
+import type {CrRadioGroupElement} from 'chrome://resources/cr_elements/cr_radio_group/cr_radio_group.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertFalse, assertNull, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
 
-import {AppConfig, createTestApp, TestAppManagementBrowserProxy} from './app_management_test_support.js';
+import type {AppConfig} from './app_management_test_support.js';
+import {createTestApp, TestAppManagementBrowserProxy} from './app_management_test_support.js';
 
 suite('AppManagementSupportedLinksItemElement', function() {
   let supportedLinksItem: AppManagementSupportedLinksItemElement;
diff --git a/chrome/test/data/webui/app_settings/test_app_management_browser_proxy.ts b/chrome/test/data/webui/app_settings/test_app_management_browser_proxy.ts
index 28a3bfc..8fe6d73754 100644
--- a/chrome/test/data/webui/app_settings/test_app_management_browser_proxy.ts
+++ b/chrome/test/data/webui/app_settings/test_app_management_browser_proxy.ts
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import { App, PageCallbackRouter, PageHandlerInterface, PageRemote, Permission, RunOnOsLoginMode, WindowMode } from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
-import {BrowserProxy} from 'chrome://resources/cr_components/app_management/browser_proxy.js';
+import type {App, PageHandlerInterface, PageRemote, Permission, RunOnOsLoginMode, WindowMode} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
+import {PageCallbackRouter} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
+import type {BrowserProxy} from 'chrome://resources/cr_components/app_management/browser_proxy.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 
diff --git a/chrome/test/data/webui/app_settings/uninstall_button_test.ts b/chrome/test/data/webui/app_settings/uninstall_button_test.ts
index 64f23ec..a3fc1af 100644
--- a/chrome/test/data/webui/app_settings/uninstall_button_test.ts
+++ b/chrome/test/data/webui/app_settings/uninstall_button_test.ts
@@ -5,9 +5,10 @@
 /** @fileoverview Test suite for app-management-uninstall-button. */
 import 'chrome://app-settings/uninstall_button.js';
 
-import {App, InstallReason} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
+import type {AppManagementUninstallButtonElement} from 'chrome://app-settings/uninstall_button.js';
+import type {App} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
+import {InstallReason} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
 import {BrowserProxy} from 'chrome://resources/cr_components/app_management/browser_proxy.js';
-import {AppManagementUninstallButtonElement} from 'chrome://app-settings/uninstall_button.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
diff --git a/chrome/test/data/webui/app_settings/window_mode_item_test.ts b/chrome/test/data/webui/app_settings/window_mode_item_test.ts
index 9d7abef..a6dfe70 100644
--- a/chrome/test/data/webui/app_settings/window_mode_item_test.ts
+++ b/chrome/test/data/webui/app_settings/window_mode_item_test.ts
@@ -5,9 +5,9 @@
 /** @fileoverview Test suite for app-management-window-mode-item. */
 import 'chrome://app-settings/window_mode_item.js';
 
-import {App} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
+import type {AppManagementWindowModeElement} from 'chrome://app-settings/window_mode_item.js';
+import type {App} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
 import {BrowserProxy} from 'chrome://resources/cr_components/app_management/browser_proxy.js';
-import {AppManagementWindowModeElement} from 'chrome://app-settings/window_mode_item.js';
 import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/bookmarks/actions_test.ts b/chrome/test/data/webui/bookmarks/actions_test.ts
index 55c2aabf..31c15765 100644
--- a/chrome/test/data/webui/bookmarks/actions_test.ts
+++ b/chrome/test/data/webui/bookmarks/actions_test.ts
@@ -7,7 +7,8 @@
  * and/or have non-trivial logic.
  */
 
-import {ROOT_NODE_ID, selectFolder, selectItem, SelectItemsAction} from 'chrome://bookmarks/bookmarks.js';
+import type {SelectItemsAction} from 'chrome://bookmarks/bookmarks.js';
+import {ROOT_NODE_ID, selectFolder, selectItem} from 'chrome://bookmarks/bookmarks.js';
 import {assertDeepEquals, assertEquals} from 'chrome://webui-test/chai_assert.js';
 
 import {TestStore} from './test_store.js';
diff --git a/chrome/test/data/webui/bookmarks/app_test.ts b/chrome/test/data/webui/bookmarks/app_test.ts
index a695a0ef..a0901b4 100644
--- a/chrome/test/data/webui/bookmarks/app_test.ts
+++ b/chrome/test/data/webui/bookmarks/app_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BookmarksApiProxyImpl, BookmarksAppElement, BookmarksItemElement, HIDE_FOCUS_RING_ATTRIBUTE, LOCAL_STORAGE_FOLDER_STATE_KEY, LOCAL_STORAGE_TREE_WIDTH_KEY} from 'chrome://bookmarks/bookmarks.js';
+import type {BookmarksAppElement, BookmarksItemElement} from 'chrome://bookmarks/bookmarks.js';
+import {BookmarksApiProxyImpl, HIDE_FOCUS_RING_ATTRIBUTE, LOCAL_STORAGE_FOLDER_STATE_KEY, LOCAL_STORAGE_TREE_WIDTH_KEY} from 'chrome://bookmarks/bookmarks.js';
 import {isMac} from 'chrome://resources/js/platform.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
 import {down, keyDownOn, pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
diff --git a/chrome/test/data/webui/bookmarks/command_manager_test.ts b/chrome/test/data/webui/bookmarks/command_manager_test.ts
index 3228513f..c4b400a 100644
--- a/chrome/test/data/webui/bookmarks/command_manager_test.ts
+++ b/chrome/test/data/webui/bookmarks/command_manager_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BookmarkManagerApiProxyImpl, BookmarksApiProxyImpl, BookmarksCommandManagerElement, BookmarksFolderNodeElement, BookmarksItemElement, BookmarksListElement, Command, createBookmark, DialogFocusManager, getDisplayedList, MenuSource, selectFolder, SelectFolderAction, SelectItemsAction, setDebouncerForTesting} from 'chrome://bookmarks/bookmarks.js';
+import type {BookmarksFolderNodeElement, BookmarksItemElement, BookmarksListElement, SelectFolderAction, SelectItemsAction} from 'chrome://bookmarks/bookmarks.js';
+import {BookmarkManagerApiProxyImpl, BookmarksApiProxyImpl, BookmarksCommandManagerElement, Command, createBookmark, DialogFocusManager, getDisplayedList, MenuSource, selectFolder, setDebouncerForTesting} from 'chrome://bookmarks/bookmarks.js';
 import {isMac} from 'chrome://resources/js/platform.js';
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/test/data/webui/bookmarks/dialog_focus_manager_test.ts b/chrome/test/data/webui/bookmarks/dialog_focus_manager_test.ts
index 2a816cd6..d92400e 100644
--- a/chrome/test/data/webui/bookmarks/dialog_focus_manager_test.ts
+++ b/chrome/test/data/webui/bookmarks/dialog_focus_manager_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BookmarksCommandManagerElement, BookmarksItemElement, BookmarksListElement, DialogFocusManager, MenuSource} from 'chrome://bookmarks/bookmarks.js';
+import type {BookmarksCommandManagerElement, BookmarksItemElement, BookmarksListElement} from 'chrome://bookmarks/bookmarks.js';
+import {DialogFocusManager, MenuSource} from 'chrome://bookmarks/bookmarks.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/test/data/webui/bookmarks/dnd_manager_test.ts b/chrome/test/data/webui/bookmarks/dnd_manager_test.ts
index a769df7..698e33e 100644
--- a/chrome/test/data/webui/bookmarks/dnd_manager_test.ts
+++ b/chrome/test/data/webui/bookmarks/dnd_manager_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BookmarkElement, BookmarkManagerApiProxyImpl, BookmarksAppElement, BookmarksFolderNodeElement, BookmarksItemElement, BookmarksListElement, BrowserProxyImpl, DndManager, DragInfo, overrideFolderOpenerTimeoutDelay, setDebouncerForTesting} from 'chrome://bookmarks/bookmarks.js';
+import type {BookmarkElement, BookmarksAppElement, BookmarksFolderNodeElement, BookmarksItemElement, BookmarksListElement, DndManager} from 'chrome://bookmarks/bookmarks.js';
+import {BookmarkManagerApiProxyImpl, BrowserProxyImpl, DragInfo, overrideFolderOpenerTimeoutDelay, setDebouncerForTesting} from 'chrome://bookmarks/bookmarks.js';
 import {middleOfNode, topLeftOfNode} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertNotReached, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/bookmarks/edit_dialog_test.ts b/chrome/test/data/webui/bookmarks/edit_dialog_test.ts
index 52e3a6f..3e2838d7 100644
--- a/chrome/test/data/webui/bookmarks/edit_dialog_test.ts
+++ b/chrome/test/data/webui/bookmarks/edit_dialog_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BookmarksApiProxyImpl, BookmarksEditDialogElement, normalizeNode, setDebouncerForTesting} from 'chrome://bookmarks/bookmarks.js';
+import type {BookmarksEditDialogElement} from 'chrome://bookmarks/bookmarks.js';
+import {BookmarksApiProxyImpl, normalizeNode, setDebouncerForTesting} from 'chrome://bookmarks/bookmarks.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/bookmarks/folder_node_focus_test.ts b/chrome/test/data/webui/bookmarks/folder_node_focus_test.ts
index f946f20..2463be7 100644
--- a/chrome/test/data/webui/bookmarks/folder_node_focus_test.ts
+++ b/chrome/test/data/webui/bookmarks/folder_node_focus_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BookmarkManagerApiProxyImpl, BookmarksFolderNodeElement, changeFolderOpen, Command, selectFolder, SelectFolderAction} from 'chrome://bookmarks/bookmarks.js';
+import type {BookmarksFolderNodeElement, SelectFolderAction} from 'chrome://bookmarks/bookmarks.js';
+import {BookmarkManagerApiProxyImpl, changeFolderOpen, Command, selectFolder} from 'chrome://bookmarks/bookmarks.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/test/data/webui/bookmarks/folder_node_test.ts b/chrome/test/data/webui/bookmarks/folder_node_test.ts
index 08e813fa..b2cd5a1f 100644
--- a/chrome/test/data/webui/bookmarks/folder_node_test.ts
+++ b/chrome/test/data/webui/bookmarks/folder_node_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BookmarksFolderNodeElement, selectFolder, SelectFolderAction} from 'chrome://bookmarks/bookmarks.js';
+import type {BookmarksFolderNodeElement, SelectFolderAction} from 'chrome://bookmarks/bookmarks.js';
+import {selectFolder} from 'chrome://bookmarks/bookmarks.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/bookmarks/item_test.ts b/chrome/test/data/webui/bookmarks/item_test.ts
index 63001024..a3bfb758 100644
--- a/chrome/test/data/webui/bookmarks/item_test.ts
+++ b/chrome/test/data/webui/bookmarks/item_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BookmarkNode, BookmarksItemElement, selectItem} from 'chrome://bookmarks/bookmarks.js';
+import type {BookmarkNode, BookmarksItemElement} from 'chrome://bookmarks/bookmarks.js';
+import {selectItem} from 'chrome://bookmarks/bookmarks.js';
 import {assertDeepEquals, assertEquals, assertNotEquals} from 'chrome://webui-test/chai_assert.js';
 
 import {TestStore} from './test_store.js';
diff --git a/chrome/test/data/webui/bookmarks/list_focus_test.ts b/chrome/test/data/webui/bookmarks/list_focus_test.ts
index eae3596..3c94c23 100644
--- a/chrome/test/data/webui/bookmarks/list_focus_test.ts
+++ b/chrome/test/data/webui/bookmarks/list_focus_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BookmarkManagerApiProxyImpl, BookmarksItemElement, BookmarksListElement, Command} from 'chrome://bookmarks/bookmarks.js';
+import type {BookmarksItemElement, BookmarksListElement} from 'chrome://bookmarks/bookmarks.js';
+import {BookmarkManagerApiProxyImpl, Command} from 'chrome://bookmarks/bookmarks.js';
 import {isMac} from 'chrome://resources/js/platform.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
diff --git a/chrome/test/data/webui/bookmarks/list_test.ts b/chrome/test/data/webui/bookmarks/list_test.ts
index 60651db3..589f1866 100644
--- a/chrome/test/data/webui/bookmarks/list_test.ts
+++ b/chrome/test/data/webui/bookmarks/list_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BookmarksAppElement, BookmarksItemElement, BookmarksListElement, BrowserProxyImpl, Command, MenuSource, SelectItemsAction} from 'chrome://bookmarks/bookmarks.js';
+import type {BookmarksAppElement, BookmarksItemElement, BookmarksListElement, SelectItemsAction} from 'chrome://bookmarks/bookmarks.js';
+import {BrowserProxyImpl, Command, MenuSource} from 'chrome://bookmarks/bookmarks.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/bookmarks/policy_test.ts b/chrome/test/data/webui/bookmarks/policy_test.ts
index ad790a6f..3e18759 100644
--- a/chrome/test/data/webui/bookmarks/policy_test.ts
+++ b/chrome/test/data/webui/bookmarks/policy_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BookmarksAppElement, BookmarksCommandManagerElement, BrowserProxyImpl, Command, IncognitoAvailability} from 'chrome://bookmarks/bookmarks.js';
+import type {BookmarksAppElement} from 'chrome://bookmarks/bookmarks.js';
+import {BookmarksCommandManagerElement, BrowserProxyImpl, Command, IncognitoAvailability} from 'chrome://bookmarks/bookmarks.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/bookmarks/reducers_test.ts b/chrome/test/data/webui/bookmarks/reducers_test.ts
index 44a9d01..884c619 100644
--- a/chrome/test/data/webui/bookmarks/reducers_test.ts
+++ b/chrome/test/data/webui/bookmarks/reducers_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BookmarksPageState, changeFolderOpen, clearSearch, createBookmark, createEmptyState, deselectItems, editBookmark, FolderOpenState, getDisplayedList, isShowingSearch, moveBookmark, NodeMap, reduceAction, removeBookmark, reorderChildren, selectFolder, SelectFolderAction, SelectionState, SelectItemsAction, setSearchResults, setSearchTerm, updateAnchor, updateFolderOpenState, updateNodes, updateSelectedFolder, updateSelection} from 'chrome://bookmarks/bookmarks.js';
+import type {BookmarksPageState, FolderOpenState, NodeMap, SelectFolderAction, SelectionState, SelectItemsAction} from 'chrome://bookmarks/bookmarks.js';
+import {changeFolderOpen, clearSearch, createBookmark, createEmptyState, deselectItems, editBookmark, getDisplayedList, isShowingSearch, moveBookmark, reduceAction, removeBookmark, reorderChildren, selectFolder, setSearchResults, setSearchTerm, updateAnchor, updateFolderOpenState, updateNodes, updateSelectedFolder, updateSelection} from 'chrome://bookmarks/bookmarks.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 import {createFolder, createItem, normalizeIterable, testTree} from './test_util.js';
diff --git a/chrome/test/data/webui/bookmarks/router_test.ts b/chrome/test/data/webui/bookmarks/router_test.ts
index 83063d01f4..afa22e0 100644
--- a/chrome/test/data/webui/bookmarks/router_test.ts
+++ b/chrome/test/data/webui/bookmarks/router_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BookmarksApiProxyImpl, getDisplayedList, SelectFolderAction, StartSearchAction, Store} from 'chrome://bookmarks/bookmarks.js';
+import type {SelectFolderAction, StartSearchAction} from 'chrome://bookmarks/bookmarks.js';
+import {BookmarksApiProxyImpl, getDisplayedList, Store} from 'chrome://bookmarks/bookmarks.js';
 import {assertDeepEquals, assertEquals} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
diff --git a/chrome/test/data/webui/bookmarks/store_test.ts b/chrome/test/data/webui/bookmarks/store_test.ts
index 2c24d03..9458768e 100644
--- a/chrome/test/data/webui/bookmarks/store_test.ts
+++ b/chrome/test/data/webui/bookmarks/store_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BookmarksPageState, createEmptyState, NodeMap, removeBookmark, Store, StoreClientMixin} from 'chrome://bookmarks/bookmarks.js';
+import type {BookmarksPageState, NodeMap} from 'chrome://bookmarks/bookmarks.js';
+import {createEmptyState, removeBookmark, Store, StoreClientMixin} from 'chrome://bookmarks/bookmarks.js';
 import {flush, html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/bookmarks/test_bookmark_manager_api_proxy.ts b/chrome/test/data/webui/bookmarks/test_bookmark_manager_api_proxy.ts
index c8df146..bcab0c2 100644
--- a/chrome/test/data/webui/bookmarks/test_bookmark_manager_api_proxy.ts
+++ b/chrome/test/data/webui/bookmarks/test_bookmark_manager_api_proxy.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BookmarkManagerApiProxy} from 'chrome://bookmarks/bookmarks.js';
+import type {BookmarkManagerApiProxy} from 'chrome://bookmarks/bookmarks.js';
 import {FakeChromeEvent} from 'chrome://webui-test/fake_chrome_event.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/bookmarks/test_bookmarks_api_proxy.ts b/chrome/test/data/webui/bookmarks/test_bookmarks_api_proxy.ts
index 44cf4b9..d82b67a 100644
--- a/chrome/test/data/webui/bookmarks/test_bookmarks_api_proxy.ts
+++ b/chrome/test/data/webui/bookmarks/test_bookmarks_api_proxy.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BookmarksApiProxy, Query} from 'chrome://bookmarks/bookmarks.js';
+import type {BookmarksApiProxy, Query} from 'chrome://bookmarks/bookmarks.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export class TestBookmarksApiProxy extends TestBrowserProxy implements
diff --git a/chrome/test/data/webui/bookmarks/test_browser_proxy.ts b/chrome/test/data/webui/bookmarks/test_browser_proxy.ts
index 39e2422c..ddb39a9 100644
--- a/chrome/test/data/webui/bookmarks/test_browser_proxy.ts
+++ b/chrome/test/data/webui/bookmarks/test_browser_proxy.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BrowserProxy, IncognitoAvailability} from 'chrome://bookmarks/bookmarks.js';
+import type {BrowserProxy} from 'chrome://bookmarks/bookmarks.js';
+import {IncognitoAvailability} from 'chrome://bookmarks/bookmarks.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 /**
diff --git a/chrome/test/data/webui/bookmarks/test_command_manager.ts b/chrome/test/data/webui/bookmarks/test_command_manager.ts
index aca5f75..bd6dbc0 100644
--- a/chrome/test/data/webui/bookmarks/test_command_manager.ts
+++ b/chrome/test/data/webui/bookmarks/test_command_manager.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BookmarksCommandManagerElement, Command} from 'chrome://bookmarks/bookmarks.js';
+import type {BookmarksCommandManagerElement, Command} from 'chrome://bookmarks/bookmarks.js';
 import {assertDeepEquals, assertEquals} from 'chrome://webui-test/chai_assert.js';
 
 import {normalizeIterable} from './test_util.js';
diff --git a/chrome/test/data/webui/bookmarks/test_store.ts b/chrome/test/data/webui/bookmarks/test_store.ts
index 9125d422..585ff19 100644
--- a/chrome/test/data/webui/bookmarks/test_store.ts
+++ b/chrome/test/data/webui/bookmarks/test_store.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BookmarksPageState, createEmptyState, reduceAction, Store} from 'chrome://bookmarks/bookmarks.js';
+import type {BookmarksPageState} from 'chrome://bookmarks/bookmarks.js';
+import {createEmptyState, reduceAction, Store} from 'chrome://bookmarks/bookmarks.js';
 import {TestStore as CrUiTestStore} from 'chrome://webui-test/test_store.js';
 
 export class TestStore extends CrUiTestStore<BookmarksPageState> {
diff --git a/chrome/test/data/webui/bookmarks/test_util.ts b/chrome/test/data/webui/bookmarks/test_util.ts
index fdc62312..4548522 100644
--- a/chrome/test/data/webui/bookmarks/test_util.ts
+++ b/chrome/test/data/webui/bookmarks/test_util.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BookmarksFolderNodeElement, FolderOpenState, NodeMap, normalizeNodes} from 'chrome://bookmarks/bookmarks.js';
+import type {BookmarksFolderNodeElement, FolderOpenState, NodeMap} from 'chrome://bookmarks/bookmarks.js';
+import {normalizeNodes} from 'chrome://bookmarks/bookmarks.js';
 import {isMac} from 'chrome://resources/js/platform.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/bookmarks/toolbar_test.ts b/chrome/test/data/webui/bookmarks/toolbar_test.ts
index 0b5fe9e..c89b5c2 100644
--- a/chrome/test/data/webui/bookmarks/toolbar_test.ts
+++ b/chrome/test/data/webui/bookmarks/toolbar_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BookmarkManagerApiProxyImpl, BookmarksToolbarElement, Command} from 'chrome://bookmarks/bookmarks.js';
+import type {BookmarksToolbarElement} from 'chrome://bookmarks/bookmarks.js';
+import {BookmarkManagerApiProxyImpl, Command} from 'chrome://bookmarks/bookmarks.js';
 import {isMac} from 'chrome://resources/js/platform.js';
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/test/data/webui/certificate_viewer_dialog/certificate_viewer_dialog_test.ts b/chrome/test/data/webui/certificate_viewer_dialog/certificate_viewer_dialog_test.ts
index c68727d..8a2ecae 100644
--- a/chrome/test/data/webui/certificate_viewer_dialog/certificate_viewer_dialog_test.ts
+++ b/chrome/test/data/webui/certificate_viewer_dialog/certificate_viewer_dialog_test.ts
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import type {CrTreeBaseElement} from 'chrome://resources/cr_elements/cr_tree/cr_tree_base.js';
+import {getRequiredElement} from 'chrome://resources/js/util.js';
+import type {TreeItemDetail} from 'chrome://view-cert/certificate_viewer.js';
 import {assertEquals, assertFalse, assertLT, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
-import {CrTreeBaseElement} from 'chrome://resources/cr_elements/cr_tree/cr_tree_base.js';
-import {getRequiredElement} from 'chrome://resources/js/util.js';
-import {TreeItemDetail} from 'chrome://view-cert/certificate_viewer.js';
 
 /**
  * Find the first tree item (in the certificate fields tree) with a value.
diff --git a/chrome/test/data/webui/chromeos/.eslintrc.js b/chrome/test/data/webui/chromeos/.eslintrc.js
index 8cf00457..8b72b130 100644
--- a/chrome/test/data/webui/chromeos/.eslintrc.js
+++ b/chrome/test/data/webui/chromeos/.eslintrc.js
@@ -14,6 +14,10 @@
       // Turn off until all violations under this folder are fixed. This was
       // done for other parts of the codebase in http://crbug.com/1521107
       'no-restricted-syntax': 'off',
+
+      // Turn off until all violations under this folder are fixed. This was
+      // done for other parts of the codebase in http://crbug.com/1494527
+      '@typescript-eslint/consistent-type-imports' : 'off',
     },
   }],
   // clang-format on
diff --git a/chrome/test/data/webui/commander/commander_app_test.ts b/chrome/test/data/webui/commander/commander_app_test.ts
index c25d010..1f8941c5 100644
--- a/chrome/test/data/webui/commander/commander_app_test.ts
+++ b/chrome/test/data/webui/commander/commander_app_test.ts
@@ -4,14 +4,14 @@
 
 import 'chrome://commander/app.js';
 
-import {CommanderAppElement} from 'chrome://commander/app.js';
+import type {CommanderAppElement} from 'chrome://commander/app.js';
 import {BrowserProxyImpl} from 'chrome://commander/browser_proxy.js';
-import {Action, Entity, ViewModel} from 'chrome://commander/types.js';
+import type {ViewModel} from 'chrome://commander/types.js';
+import {Action, Entity} from 'chrome://commander/types.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
-import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
-
 import {assertDeepEquals, assertEquals, assertGT, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
 import {TestCommanderBrowserProxy} from './test_commander_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/commander/test_commander_browser_proxy.ts b/chrome/test/data/webui/commander/test_commander_browser_proxy.ts
index cbe74d82..64d2f672 100644
--- a/chrome/test/data/webui/commander/test_commander_browser_proxy.ts
+++ b/chrome/test/data/webui/commander/test_commander_browser_proxy.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BrowserProxy} from 'chrome://commander/browser_proxy.js';
+import type {BrowserProxy} from 'chrome://commander/browser_proxy.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export class TestCommanderBrowserProxy extends TestBrowserProxy implements
diff --git a/chrome/test/data/webui/compose/compose_animator_test.ts b/chrome/test/data/webui/compose/compose_animator_test.ts
index 0ceaa6a..0fa7838 100644
--- a/chrome/test/data/webui/compose/compose_animator_test.ts
+++ b/chrome/test/data/webui/compose/compose_animator_test.ts
@@ -4,8 +4,8 @@
 
 import 'chrome://compose/animations/animator.js';
 
+import {getTrustedHTML} from '//resources/js/static_types.js';
 import {Animator} from 'chrome://compose/animations/animator.js';
-import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 
 suite('ComposeAnimator', () => {
diff --git a/chrome/test/data/webui/compose/compose_app_focus_test.ts b/chrome/test/data/webui/compose/compose_app_focus_test.ts
index 9c2b2c1..836fa44 100644
--- a/chrome/test/data/webui/compose/compose_app_focus_test.ts
+++ b/chrome/test/data/webui/compose/compose_app_focus_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://compose/app.js';
 
-import {ComposeAppElement} from 'chrome://compose/app.js';
+import type {ComposeAppElement} from 'chrome://compose/app.js';
 import {Length, Tone, UserFeedback} from 'chrome://compose/compose.mojom-webui.js';
 import {ComposeApiProxyImpl} from 'chrome://compose/compose_api_proxy.js';
 import {ComposeStatus} from 'chrome://compose/compose_enums.mojom-webui.js';
diff --git a/chrome/test/data/webui/compose/compose_app_test.ts b/chrome/test/data/webui/compose/compose_app_test.ts
index f572393..a49271c 100644
--- a/chrome/test/data/webui/compose/compose_app_test.ts
+++ b/chrome/test/data/webui/compose/compose_app_test.ts
@@ -4,12 +4,13 @@
 
 import 'chrome://compose/app.js';
 
-import {ComposeAppElement, ComposeAppState} from 'chrome://compose/app.js';
-import {CloseReason, ComposeState, Length, Tone, UserFeedback} from 'chrome://compose/compose.mojom-webui.js';
+import {CrFeedbackOption} from '//resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.js';
+import {loadTimeData} from '//resources/js/load_time_data.js';
+import type {ComposeAppElement, ComposeAppState} from 'chrome://compose/app.js';
+import type {ComposeState} from 'chrome://compose/compose.mojom-webui.js';
+import {CloseReason, Length, Tone, UserFeedback} from 'chrome://compose/compose.mojom-webui.js';
 import {ComposeApiProxyImpl} from 'chrome://compose/compose_api_proxy.js';
 import {ComposeStatus} from 'chrome://compose/compose_enums.mojom-webui.js';
-import {CrFeedbackOption} from 'chrome://resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertStringContains, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {isVisible, whenCheck} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/compose/compose_textarea_test.ts b/chrome/test/data/webui/compose/compose_textarea_test.ts
index c35d389..b94b2e17 100644
--- a/chrome/test/data/webui/compose/compose_textarea_test.ts
+++ b/chrome/test/data/webui/compose/compose_textarea_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://compose/textarea.js';
 
-import {ComposeTextareaElement} from 'chrome://compose/textarea.js';
+import type {ComposeTextareaElement} from 'chrome://compose/textarea.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
 
diff --git a/chrome/test/data/webui/compose/result_text_test.ts b/chrome/test/data/webui/compose/result_text_test.ts
index 38c0924d..84dcd81 100644
--- a/chrome/test/data/webui/compose/result_text_test.ts
+++ b/chrome/test/data/webui/compose/result_text_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://compose/result_text.js';
 
-import {ComposeResultTextElement} from 'chrome://compose/result_text.js';
+import type {ComposeResultTextElement} from 'chrome://compose/result_text.js';
 // import {assertEquals, assertFalse, assertStringEquals, assertTrue} from
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/compose/test_compose_api_proxy.ts b/chrome/test/data/webui/compose/test_compose_api_proxy.ts
index 4b7175d..792d278 100644
--- a/chrome/test/data/webui/compose/test_compose_api_proxy.ts
+++ b/chrome/test/data/webui/compose/test_compose_api_proxy.ts
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {CloseReason, ComposeDialogCallbackRouter, ComposeState, OpenMetadata, StyleModifiers, UserFeedback} from 'chrome://compose/compose.mojom-webui.js';
-import {ComposeApiProxy} from 'chrome://compose/compose_api_proxy.js';
+import type {CloseReason, ComposeState, OpenMetadata, StyleModifiers} from 'chrome://compose/compose.mojom-webui.js';
+import {ComposeDialogCallbackRouter, UserFeedback} from 'chrome://compose/compose.mojom-webui.js';
+import type {ComposeApiProxy} from 'chrome://compose/compose_api_proxy.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 function getDefaultComposeState(): ComposeState {
diff --git a/chrome/test/data/webui/cr_components/certificate_manager_provisioning_test.ts b/chrome/test/data/webui/cr_components/certificate_manager_provisioning_test.ts
index cd26093..a922921 100644
--- a/chrome/test/data/webui/cr_components/certificate_manager_provisioning_test.ts
+++ b/chrome/test/data/webui/cr_components/certificate_manager_provisioning_test.ts
@@ -6,11 +6,13 @@
 import 'chrome://resources/cr_components/certificate_manager/certificate_provisioning_list.js';
 import 'chrome://resources/cr_components/certificate_manager/certificate_provisioning_entry.js';
 
-import {CertificateProvisioningActionEventDetail, CertificateProvisioningViewDetailsActionEvent} from 'chrome://resources/cr_components/certificate_manager/certificate_manager_types.js';
-import {CertificateProvisioningBrowserProxy, CertificateProvisioningBrowserProxyImpl, CertificateProvisioningProcess} from 'chrome://resources/cr_components/certificate_manager/certificate_provisioning_browser_proxy.js';
-import {CertificateProvisioningDetailsDialogElement} from 'chrome://resources/cr_components/certificate_manager/certificate_provisioning_details_dialog.js';
-import {CertificateProvisioningEntryElement} from 'chrome://resources/cr_components/certificate_manager/certificate_provisioning_entry.js';
-import {CertificateProvisioningListElement} from 'chrome://resources/cr_components/certificate_manager/certificate_provisioning_list.js';
+import type {CertificateProvisioningActionEventDetail} from 'chrome://resources/cr_components/certificate_manager/certificate_manager_types.js';
+import {CertificateProvisioningViewDetailsActionEvent} from 'chrome://resources/cr_components/certificate_manager/certificate_manager_types.js';
+import type {CertificateProvisioningBrowserProxy, CertificateProvisioningProcess} from 'chrome://resources/cr_components/certificate_manager/certificate_provisioning_browser_proxy.js';
+import {CertificateProvisioningBrowserProxyImpl} from 'chrome://resources/cr_components/certificate_manager/certificate_provisioning_browser_proxy.js';
+import type {CertificateProvisioningDetailsDialogElement} from 'chrome://resources/cr_components/certificate_manager/certificate_provisioning_details_dialog.js';
+import type {CertificateProvisioningEntryElement} from 'chrome://resources/cr_components/certificate_manager/certificate_provisioning_entry.js';
+import type {CertificateProvisioningListElement} from 'chrome://resources/cr_components/certificate_manager/certificate_provisioning_list.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/cr_components/certificate_manager_test.ts b/chrome/test/data/webui/cr_components/certificate_manager_test.ts
index 7c9cbf5..d4c4db6 100644
--- a/chrome/test/data/webui/cr_components/certificate_manager_test.ts
+++ b/chrome/test/data/webui/cr_components/certificate_manager_test.ts
@@ -16,15 +16,17 @@
 import 'chrome://resources/cr_components/certificate_manager/certificate_manager.js';
 import 'chrome://resources/cr_components/certificate_manager/certificate_subentry.js';
 
-import {CaTrustEditDialogElement} from 'chrome://resources/cr_components/certificate_manager/ca_trust_edit_dialog.js';
-import {CertificateDeleteConfirmationDialogElement} from 'chrome://resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.js';
-import {CertificateListElement} from 'chrome://resources/cr_components/certificate_manager/certificate_list.js';
-import {CertificateManagerElement} from 'chrome://resources/cr_components/certificate_manager/certificate_manager.js';
-import {CertificateAction, CertificateActionEvent, CertificateActionEventDetail} from 'chrome://resources/cr_components/certificate_manager/certificate_manager_types.js';
-import {CertificatePasswordDecryptionDialogElement} from 'chrome://resources/cr_components/certificate_manager/certificate_password_decryption_dialog.js';
-import {CertificatePasswordEncryptionDialogElement} from 'chrome://resources/cr_components/certificate_manager/certificate_password_encryption_dialog.js';
-import {CertificateSubentryElement} from 'chrome://resources/cr_components/certificate_manager/certificate_subentry.js';
-import {CaTrustInfo, CertificatesBrowserProxy, CertificatesBrowserProxyImpl, CertificatesError, CertificatesOrgGroup, CertificateSubnode, CertificateType} from 'chrome://resources/cr_components/certificate_manager/certificates_browser_proxy.js';
+import type {CaTrustEditDialogElement} from 'chrome://resources/cr_components/certificate_manager/ca_trust_edit_dialog.js';
+import type {CertificateDeleteConfirmationDialogElement} from 'chrome://resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.js';
+import type {CertificateListElement} from 'chrome://resources/cr_components/certificate_manager/certificate_list.js';
+import type {CertificateManagerElement} from 'chrome://resources/cr_components/certificate_manager/certificate_manager.js';
+import type { CertificateActionEventDetail} from 'chrome://resources/cr_components/certificate_manager/certificate_manager_types.js';
+import {CertificateAction, CertificateActionEvent} from 'chrome://resources/cr_components/certificate_manager/certificate_manager_types.js';
+import type {CertificatePasswordDecryptionDialogElement} from 'chrome://resources/cr_components/certificate_manager/certificate_password_decryption_dialog.js';
+import type {CertificatePasswordEncryptionDialogElement} from 'chrome://resources/cr_components/certificate_manager/certificate_password_encryption_dialog.js';
+import type {CertificateSubentryElement} from 'chrome://resources/cr_components/certificate_manager/certificate_subentry.js';
+import type {CaTrustInfo, CertificatesBrowserProxy, CertificatesError, CertificatesOrgGroup, CertificateSubnode} from 'chrome://resources/cr_components/certificate_manager/certificates_browser_proxy.js';
+import { CertificatesBrowserProxyImpl, CertificateType} from 'chrome://resources/cr_components/certificate_manager/certificates_browser_proxy.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {keyEventOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/test/data/webui/cr_components/chromeos/traffic_counters/traffic_counters_test.ts b/chrome/test/data/webui/cr_components/chromeos/traffic_counters/traffic_counters_test.ts
index 8d60acc..5d962e1 100644
--- a/chrome/test/data/webui/cr_components/chromeos/traffic_counters/traffic_counters_test.ts
+++ b/chrome/test/data/webui/cr_components/chromeos/traffic_counters/traffic_counters_test.ts
@@ -7,9 +7,9 @@
 
 import {MojoInterfaceProviderImpl} from 'chrome://resources/ash/common/network/mojo_interface_provider.js';
 import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js';
-import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
-import {TrafficCountersElement} from 'chrome://resources/ash/common/traffic_counters/traffic_counters.js';
+import type {TrafficCountersElement} from 'chrome://resources/ash/common/traffic_counters/traffic_counters.js';
 import {ConnectionStateType, NetworkType} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
 import {assertEquals, assertTrue} from '../../../chromeos/chai_assert.js';
 import {FakeNetworkConfig} from '../../../chromeos/fake_network_config_mojom.js';
diff --git a/chrome/test/data/webui/cr_components/customize_color_scheme_mode_test.ts b/chrome/test/data/webui/cr_components/customize_color_scheme_mode_test.ts
index e87f8d5d..56d5ad2 100644
--- a/chrome/test/data/webui/cr_components/customize_color_scheme_mode_test.ts
+++ b/chrome/test/data/webui/cr_components/customize_color_scheme_mode_test.ts
@@ -5,9 +5,11 @@
 import 'chrome://customize-chrome-side-panel.top-chrome/strings.m.js';
 
 import {CustomizeColorSchemeModeBrowserProxy} from 'chrome://resources/cr_components/customize_color_scheme_mode/browser_proxy.js';
-import {ColorSchemeModeOption, colorSchemeModeOptions, CustomizeColorSchemeModeElement} from 'chrome://resources/cr_components/customize_color_scheme_mode/customize_color_scheme_mode.js';
-import {ColorSchemeMode, CustomizeColorSchemeModeClientCallbackRouter, CustomizeColorSchemeModeClientRemote, CustomizeColorSchemeModeHandlerRemote} from 'chrome://resources/cr_components/customize_color_scheme_mode/customize_color_scheme_mode.mojom-webui.js';
-import {CrSegmentedButtonElement} from 'chrome://resources/cr_elements/cr_segmented_button/cr_segmented_button.js';
+import type {ColorSchemeModeOption} from 'chrome://resources/cr_components/customize_color_scheme_mode/customize_color_scheme_mode.js';
+import {colorSchemeModeOptions, CustomizeColorSchemeModeElement} from 'chrome://resources/cr_components/customize_color_scheme_mode/customize_color_scheme_mode.js';
+import type {ColorSchemeMode, CustomizeColorSchemeModeClientRemote} from 'chrome://resources/cr_components/customize_color_scheme_mode/customize_color_scheme_mode.mojom-webui.js';
+import {CustomizeColorSchemeModeClientCallbackRouter, CustomizeColorSchemeModeHandlerRemote} from 'chrome://resources/cr_components/customize_color_scheme_mode/customize_color_scheme_mode.mojom-webui.js';
+import type {CrSegmentedButtonElement} from 'chrome://resources/cr_elements/cr_segmented_button/cr_segmented_button.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 import {TestMock} from 'chrome://webui-test/test_mock.js';
diff --git a/chrome/test/data/webui/cr_components/customize_themes_test.ts b/chrome/test/data/webui/cr_components/customize_themes_test.ts
index fd7f7cdc..65b6b054 100644
--- a/chrome/test/data/webui/cr_components/customize_themes_test.ts
+++ b/chrome/test/data/webui/cr_components/customize_themes_test.ts
@@ -6,11 +6,13 @@
 import 'chrome://resources/cr_components/customize_themes/theme_icon.js';
 import 'chrome://resources/cr_components/customize_themes/customize_themes.js';
 
-import {CustomizeThemesBrowserProxy, CustomizeThemesBrowserProxyImpl} from 'chrome://resources/cr_components/customize_themes/browser_proxy.js';
-import {CustomizeThemesElement} from 'chrome://resources/cr_components/customize_themes/customize_themes.js';
-import {ChromeTheme, CustomizeThemesClientCallbackRouter, CustomizeThemesHandlerInterface, ThemeType} from 'chrome://resources/cr_components/customize_themes/customize_themes.mojom-webui.js';
-import {ThemeIconElement} from 'chrome://resources/cr_components/customize_themes/theme_icon.js';
-import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
+import type {CustomizeThemesBrowserProxy} from 'chrome://resources/cr_components/customize_themes/browser_proxy.js';
+import {CustomizeThemesBrowserProxyImpl} from 'chrome://resources/cr_components/customize_themes/browser_proxy.js';
+import type {CustomizeThemesElement} from 'chrome://resources/cr_components/customize_themes/customize_themes.js';
+import type {ChromeTheme, CustomizeThemesHandlerInterface} from 'chrome://resources/cr_components/customize_themes/customize_themes.mojom-webui.js';
+import {CustomizeThemesClientCallbackRouter, ThemeType} from 'chrome://resources/cr_components/customize_themes/customize_themes.mojom-webui.js';
+import type {ThemeIconElement} from 'chrome://resources/cr_components/customize_themes/theme_icon.js';
+import type {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
diff --git a/chrome/test/data/webui/cr_components/help_bubble_mixin_test.ts b/chrome/test/data/webui/cr_components/help_bubble_mixin_test.ts
index 0544d11..7e58acdd 100644
--- a/chrome/test/data/webui/cr_components/help_bubble_mixin_test.ts
+++ b/chrome/test/data/webui/cr_components/help_bubble_mixin_test.ts
@@ -4,12 +4,16 @@
 
 import 'chrome://resources/cr_components/help_bubble/help_bubble.js';
 
-import {IronIconElement} from '//resources/polymer/v3_0/iron-icon/iron-icon.js';
-import {HelpBubbleElement} from 'chrome://resources/cr_components/help_bubble/help_bubble.js';
-import {HelpBubbleArrowPosition, HelpBubbleClientCallbackRouter, HelpBubbleClientRemote, HelpBubbleClosedReason, HelpBubbleHandlerInterface, HelpBubbleParams} from 'chrome://resources/cr_components/help_bubble/help_bubble.mojom-webui.js';
-import {ANCHOR_HIGHLIGHT_CLASS, HelpBubbleController} from 'chrome://resources/cr_components/help_bubble/help_bubble_controller.js';
-import {HelpBubbleMixin, HelpBubbleMixinInterface} from 'chrome://resources/cr_components/help_bubble/help_bubble_mixin.js';
-import {HelpBubbleProxy, HelpBubbleProxyImpl} from 'chrome://resources/cr_components/help_bubble/help_bubble_proxy.js';
+import type {IronIconElement} from '//resources/polymer/v3_0/iron-icon/iron-icon.js';
+import type {HelpBubbleElement} from 'chrome://resources/cr_components/help_bubble/help_bubble.js';
+import type {HelpBubbleClientRemote, HelpBubbleHandlerInterface, HelpBubbleParams} from 'chrome://resources/cr_components/help_bubble/help_bubble.mojom-webui.js';
+import {HelpBubbleArrowPosition, HelpBubbleClientCallbackRouter, HelpBubbleClosedReason} from 'chrome://resources/cr_components/help_bubble/help_bubble.mojom-webui.js';
+import type {HelpBubbleController} from 'chrome://resources/cr_components/help_bubble/help_bubble_controller.js';
+import {ANCHOR_HIGHLIGHT_CLASS} from 'chrome://resources/cr_components/help_bubble/help_bubble_controller.js';
+import type {HelpBubbleMixinInterface} from 'chrome://resources/cr_components/help_bubble/help_bubble_mixin.js';
+import {HelpBubbleMixin} from 'chrome://resources/cr_components/help_bubble/help_bubble_mixin.js';
+import type {HelpBubbleProxy} from 'chrome://resources/cr_components/help_bubble/help_bubble_proxy.js';
+import {HelpBubbleProxyImpl} from 'chrome://resources/cr_components/help_bubble/help_bubble_proxy.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertThrows, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/cr_components/help_bubble_test.ts b/chrome/test/data/webui/cr_components/help_bubble_test.ts
index 697a29b..ae52f17 100644
--- a/chrome/test/data/webui/cr_components/help_bubble_test.ts
+++ b/chrome/test/data/webui/cr_components/help_bubble_test.ts
@@ -4,10 +4,12 @@
 
 import 'chrome://resources/cr_components/help_bubble/help_bubble.js';
 
-import {CrButtonElement} from '//resources/cr_elements/cr_button/cr_button.js';
-import {IronIconElement} from '//resources/polymer/v3_0/iron-icon/iron-icon.js';
-import {HELP_BUBBLE_DISMISSED_EVENT, HELP_BUBBLE_TIMED_OUT_EVENT, HelpBubbleDismissedEvent, HelpBubbleElement, HelpBubbleTimedOutEvent} from 'chrome://resources/cr_components/help_bubble/help_bubble.js';
-import {HelpBubbleArrowPosition, HelpBubbleButtonParams} from 'chrome://resources/cr_components/help_bubble/help_bubble.mojom-webui.js';
+import type {CrButtonElement} from '//resources/cr_elements/cr_button/cr_button.js';
+import type {IronIconElement} from '//resources/polymer/v3_0/iron-icon/iron-icon.js';
+import type {HelpBubbleDismissedEvent, HelpBubbleTimedOutEvent} from 'chrome://resources/cr_components/help_bubble/help_bubble.js';
+import {HELP_BUBBLE_DISMISSED_EVENT, HELP_BUBBLE_TIMED_OUT_EVENT, HelpBubbleElement} from 'chrome://resources/cr_components/help_bubble/help_bubble.js';
+import type {HelpBubbleButtonParams} from 'chrome://resources/cr_components/help_bubble/help_bubble.mojom-webui.js';
+import {HelpBubbleArrowPosition} from 'chrome://resources/cr_components/help_bubble/help_bubble.mojom-webui.js';
 import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/cr_components/history_clusters/history_clusters_test.ts b/chrome/test/data/webui/cr_components/history_clusters/history_clusters_test.ts
index f51e278..9c9f8385 100644
--- a/chrome/test/data/webui/cr_components/history_clusters/history_clusters_test.ts
+++ b/chrome/test/data/webui/cr_components/history_clusters/history_clusters_test.ts
@@ -6,8 +6,9 @@
 
 import {BrowserProxyImpl} from 'chrome://resources/cr_components/history_clusters/browser_proxy.js';
 import {HistoryClustersElement} from 'chrome://resources/cr_components/history_clusters/clusters.js';
-import {Cluster, RawVisitData, URLVisit} from 'chrome://resources/cr_components/history_clusters/history_cluster_types.mojom-webui.js';
-import {PageCallbackRouter, PageHandlerRemote, PageRemote, QueryResult} from 'chrome://resources/cr_components/history_clusters/history_clusters.mojom-webui.js';
+import type {Cluster, RawVisitData, URLVisit} from 'chrome://resources/cr_components/history_clusters/history_cluster_types.mojom-webui.js';
+import type {PageRemote, QueryResult} from 'chrome://resources/cr_components/history_clusters/history_clusters.mojom-webui.js';
+import {PageCallbackRouter, PageHandlerRemote} from 'chrome://resources/cr_components/history_clusters/history_clusters.mojom-webui.js';
 import {PageImageServiceBrowserProxy} from 'chrome://resources/cr_components/page_image_service/browser_proxy.js';
 import {ClientId as PageImageServiceClientId, PageImageServiceHandlerRemote} from 'chrome://resources/cr_components/page_image_service/page_image_service.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
diff --git a/chrome/test/data/webui/cr_components/history_clusters/horizontal_carousel_test.ts b/chrome/test/data/webui/cr_components/history_clusters/horizontal_carousel_test.ts
index 67a860d..18ff933 100644
--- a/chrome/test/data/webui/cr_components/history_clusters/horizontal_carousel_test.ts
+++ b/chrome/test/data/webui/cr_components/history_clusters/horizontal_carousel_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://resources/cr_components/history_clusters/horizontal_carousel.js';
 
-import {HorizontalCarouselElement} from 'chrome://resources/cr_components/history_clusters/horizontal_carousel.js';
+import type {HorizontalCarouselElement} from 'chrome://resources/cr_components/history_clusters/horizontal_carousel.js';
 import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 let carouselElement : HorizontalCarouselElement;
diff --git a/chrome/test/data/webui/cr_components/localized_link_test.ts b/chrome/test/data/webui/cr_components/localized_link_test.ts
index 1c30398..7e65f1b 100644
--- a/chrome/test/data/webui/cr_components/localized_link_test.ts
+++ b/chrome/test/data/webui/cr_components/localized_link_test.ts
@@ -4,7 +4,7 @@
 
 import '//resources/cr_components/localized_link/localized_link.js';
 
-import {LocalizedLinkElement} from '//resources/cr_components/localized_link/localized_link.js';
+import type {LocalizedLinkElement} from '//resources/cr_components/localized_link/localized_link.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/cr_components/managed_dialog_test.ts b/chrome/test/data/webui/cr_components/managed_dialog_test.ts
index c94fe7881..ac27160 100644
--- a/chrome/test/data/webui/cr_components/managed_dialog_test.ts
+++ b/chrome/test/data/webui/cr_components/managed_dialog_test.ts
@@ -6,7 +6,7 @@
 
 import 'chrome://resources/cr_components/managed_dialog/managed_dialog.js';
 
-import {ManagedDialogElement} from 'chrome://resources/cr_components/managed_dialog/managed_dialog.js';
+import type {ManagedDialogElement} from 'chrome://resources/cr_components/managed_dialog/managed_dialog.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/cr_components/managed_footnote_test.ts b/chrome/test/data/webui/cr_components/managed_footnote_test.ts
index c76e048..6bccbc3 100644
--- a/chrome/test/data/webui/cr_components/managed_footnote_test.ts
+++ b/chrome/test/data/webui/cr_components/managed_footnote_test.ts
@@ -7,7 +7,7 @@
 // clang-format off
 import 'chrome://resources/cr_components/managed_footnote/managed_footnote.js';
 
-import {ManagedFootnoteElement} from 'chrome://resources/cr_components/managed_footnote/managed_footnote.js';
+import type {ManagedFootnoteElement} from 'chrome://resources/cr_components/managed_footnote/managed_footnote.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/test/data/webui/cr_components/most_visited_focus_test.ts b/chrome/test/data/webui/cr_components/most_visited_focus_test.ts
index 167a685f..5a4c13eb 100644
--- a/chrome/test/data/webui/cr_components/most_visited_focus_test.ts
+++ b/chrome/test/data/webui/cr_components/most_visited_focus_test.ts
@@ -4,7 +4,8 @@
 
 import {MostVisitedBrowserProxy} from 'chrome://resources/cr_components/most_visited/browser_proxy.js';
 import {MostVisitedElement} from 'chrome://resources/cr_components/most_visited/most_visited.js';
-import {MostVisitedPageCallbackRouter, MostVisitedPageHandlerRemote, MostVisitedPageRemote} from 'chrome://resources/cr_components/most_visited/most_visited.mojom-webui.js';
+import type {MostVisitedPageRemote} from 'chrome://resources/cr_components/most_visited/most_visited.mojom-webui.js';
+import {MostVisitedPageCallbackRouter, MostVisitedPageHandlerRemote} from 'chrome://resources/cr_components/most_visited/most_visited.mojom-webui.js';
 import {TextDirection} from 'chrome://resources/mojo/mojo/public/mojom/base/text_direction.mojom-webui.js';
 import {TestMock} from 'chrome://webui-test/test_mock.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/cr_components/most_visited_test.ts b/chrome/test/data/webui/cr_components/most_visited_test.ts
index f614859..d1a9f85 100644
--- a/chrome/test/data/webui/cr_components/most_visited_test.ts
+++ b/chrome/test/data/webui/cr_components/most_visited_test.ts
@@ -4,11 +4,12 @@
 
 import {MostVisitedBrowserProxy} from 'chrome://resources/cr_components/most_visited/browser_proxy.js';
 import {MostVisitedElement} from 'chrome://resources/cr_components/most_visited/most_visited.js';
-import {MostVisitedPageCallbackRouter, MostVisitedPageHandlerRemote, MostVisitedPageRemote, MostVisitedTile} from 'chrome://resources/cr_components/most_visited/most_visited.mojom-webui.js';
+import type {MostVisitedPageRemote, MostVisitedTile} from 'chrome://resources/cr_components/most_visited/most_visited.mojom-webui.js';
+import {MostVisitedPageCallbackRouter, MostVisitedPageHandlerRemote} from 'chrome://resources/cr_components/most_visited/most_visited.mojom-webui.js';
 import {MostVisitedWindowProxy} from 'chrome://resources/cr_components/most_visited/window_proxy.js';
-import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
-import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
-import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
+import type {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import type {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import type {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {isMac} from 'chrome://resources/js/platform.js';
 import {TextDirection} from 'chrome://resources/mojo/mojo/public/mojom/base/text_direction.mojom-webui.js';
diff --git a/chrome/test/data/webui/cr_components/omnibox/realbox_match_test.ts b/chrome/test/data/webui/cr_components/omnibox/realbox_match_test.ts
index 4e76a94..231b08ea 100644
--- a/chrome/test/data/webui/cr_components/omnibox/realbox_match_test.ts
+++ b/chrome/test/data/webui/cr_components/omnibox/realbox_match_test.ts
@@ -7,7 +7,7 @@
 
 import {NavigationPredictor} from 'chrome://resources/cr_components/omnibox/omnibox.mojom-webui.js';
 import {RealboxBrowserProxy} from 'chrome://resources/cr_components/omnibox/realbox_browser_proxy.js';
-import {RealboxMatchElement} from 'chrome://resources/cr_components/omnibox/realbox_match.js';
+import type {RealboxMatchElement} from 'chrome://resources/cr_components/omnibox/realbox_match.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
diff --git a/chrome/test/data/webui/cr_components/omnibox/realbox_test_utils.ts b/chrome/test/data/webui/cr_components/omnibox/realbox_test_utils.ts
index d876c12..b803ce6 100644
--- a/chrome/test/data/webui/cr_components/omnibox/realbox_test_utils.ts
+++ b/chrome/test/data/webui/cr_components/omnibox/realbox_test_utils.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {AutocompleteMatch, PageCallbackRouter, PageHandlerRemote} from 'chrome://resources/cr_components/omnibox/omnibox.mojom-webui.js';
+import type {AutocompleteMatch} from 'chrome://resources/cr_components/omnibox/omnibox.mojom-webui.js';
+import {PageCallbackRouter, PageHandlerRemote} from 'chrome://resources/cr_components/omnibox/omnibox.mojom-webui.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 import {TestMock} from 'chrome://webui-test/test_mock.js';
 
diff --git a/chrome/test/data/webui/cr_components/settings_prefs_test.ts b/chrome/test/data/webui/cr_components/settings_prefs_test.ts
index 7c6be696..bb94a8d 100644
--- a/chrome/test/data/webui/cr_components/settings_prefs_test.ts
+++ b/chrome/test/data/webui/cr_components/settings_prefs_test.ts
@@ -5,7 +5,7 @@
 // clang-format off
 import 'chrome://resources/cr_components/settings_prefs/prefs.js';
 
-import {SettingsPrefsElement} from 'chrome://resources/cr_components/settings_prefs/prefs.js';
+import type {SettingsPrefsElement} from 'chrome://resources/cr_components/settings_prefs/prefs.js';
 import {CrSettingsPrefs} from 'chrome://resources/cr_components/settings_prefs/prefs_types.js';
 import {assertEquals, assertNotEquals} from 'chrome://webui-test/chai_assert.js';
 import {FakeSettingsPrivate} from 'chrome://webui-test/fake_settings_private.js';
diff --git a/chrome/test/data/webui/cr_components/theme_color_picker/check_mark_wrapper_test.ts b/chrome/test/data/webui/cr_components/theme_color_picker/check_mark_wrapper_test.ts
index e3d9a70..3d387c66 100644
--- a/chrome/test/data/webui/cr_components/theme_color_picker/check_mark_wrapper_test.ts
+++ b/chrome/test/data/webui/cr_components/theme_color_picker/check_mark_wrapper_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://resources/cr_components/theme_color_picker/check_mark_wrapper.js';
 
-import {CheckMarkWrapperElement} from 'chrome://resources/cr_components/theme_color_picker/check_mark_wrapper.js';
+import type {CheckMarkWrapperElement} from 'chrome://resources/cr_components/theme_color_picker/check_mark_wrapper.js';
 import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
 
diff --git a/chrome/test/data/webui/cr_components/theme_color_picker/theme_color_picker_focus_test.ts b/chrome/test/data/webui/cr_components/theme_color_picker/theme_color_picker_focus_test.ts
index fb4cae10..23230bee 100644
--- a/chrome/test/data/webui/cr_components/theme_color_picker/theme_color_picker_focus_test.ts
+++ b/chrome/test/data/webui/cr_components/theme_color_picker/theme_color_picker_focus_test.ts
@@ -6,7 +6,7 @@
 import 'chrome://resources/cr_components/theme_color_picker/theme_color_picker.js';
 
 import {ThemeColorPickerBrowserProxy} from 'chrome://resources/cr_components/theme_color_picker/browser_proxy.js';
-import {ThemeColorPickerElement} from 'chrome://resources/cr_components/theme_color_picker/theme_color_picker.js';
+import type {ThemeColorPickerElement} from 'chrome://resources/cr_components/theme_color_picker/theme_color_picker.js';
 import {ThemeColorPickerClientCallbackRouter, ThemeColorPickerHandlerRemote} from 'chrome://resources/cr_components/theme_color_picker/theme_color_picker.mojom-webui.js';
 import {TestMock} from 'chrome://webui-test/test_mock.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/cr_components/theme_color_picker/theme_color_picker_test.ts b/chrome/test/data/webui/cr_components/theme_color_picker/theme_color_picker_test.ts
index f292b4a9..47409a2 100644
--- a/chrome/test/data/webui/cr_components/theme_color_picker/theme_color_picker_test.ts
+++ b/chrome/test/data/webui/cr_components/theme_color_picker/theme_color_picker_test.ts
@@ -5,12 +5,14 @@
 import 'chrome://customize-chrome-side-panel.top-chrome/strings.m.js';
 import 'chrome://resources/cr_components/theme_color_picker/theme_color_picker.js';
 
-import {ManagedDialogElement} from 'chrome://resources/cr_components/managed_dialog/managed_dialog.js';
+import type {ManagedDialogElement} from 'chrome://resources/cr_components/managed_dialog/managed_dialog.js';
 import {ThemeColorPickerBrowserProxy} from 'chrome://resources/cr_components/theme_color_picker/browser_proxy.js';
-import {Color, DARK_BASELINE_BLUE_COLOR, DARK_BASELINE_GREY_COLOR, DARK_DEFAULT_COLOR, LIGHT_BASELINE_BLUE_COLOR, LIGHT_BASELINE_GREY_COLOR, LIGHT_DEFAULT_COLOR} from 'chrome://resources/cr_components/theme_color_picker/color_utils.js';
-import {ThemeColorElement} from 'chrome://resources/cr_components/theme_color_picker/theme_color.js';
-import {ThemeColorPickerElement} from 'chrome://resources/cr_components/theme_color_picker/theme_color_picker.js';
-import {ChromeColor, Theme, ThemeColorPickerClientCallbackRouter, ThemeColorPickerClientRemote, ThemeColorPickerHandlerRemote} from 'chrome://resources/cr_components/theme_color_picker/theme_color_picker.mojom-webui.js';
+import type {Color} from 'chrome://resources/cr_components/theme_color_picker/color_utils.js';
+import {DARK_BASELINE_BLUE_COLOR, DARK_BASELINE_GREY_COLOR, DARK_DEFAULT_COLOR, LIGHT_BASELINE_BLUE_COLOR, LIGHT_BASELINE_GREY_COLOR, LIGHT_DEFAULT_COLOR} from 'chrome://resources/cr_components/theme_color_picker/color_utils.js';
+import type {ThemeColorElement} from 'chrome://resources/cr_components/theme_color_picker/theme_color.js';
+import type {ThemeColorPickerElement} from 'chrome://resources/cr_components/theme_color_picker/theme_color_picker.js';
+import type {ChromeColor, Theme, ThemeColorPickerClientRemote} from 'chrome://resources/cr_components/theme_color_picker/theme_color_picker.mojom-webui.js';
+import {ThemeColorPickerClientCallbackRouter, ThemeColorPickerHandlerRemote} from 'chrome://resources/cr_components/theme_color_picker/theme_color_picker.mojom-webui.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {BrowserColorVariant} from 'chrome://resources/mojo/ui/base/mojom/themes.mojom-webui.js';
 import {assertDeepEquals, assertEquals, assertNotReached, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/cr_components/theme_color_picker/theme_color_test.ts b/chrome/test/data/webui/cr_components/theme_color_picker/theme_color_test.ts
index 4104fcd..1dbb6c3 100644
--- a/chrome/test/data/webui/cr_components/theme_color_picker/theme_color_test.ts
+++ b/chrome/test/data/webui/cr_components/theme_color_picker/theme_color_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://resources/cr_components/theme_color_picker/theme_color.js';
 
-import {ThemeColorElement} from 'chrome://resources/cr_components/theme_color_picker/theme_color.js';
+import type {ThemeColorElement} from 'chrome://resources/cr_components/theme_color_picker/theme_color.js';
 import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {hasStyle, isVisible} from 'chrome://webui-test/test_util.js';
 
diff --git a/chrome/test/data/webui/cr_components/theme_color_picker/theme_hue_slider_dialog_test.ts b/chrome/test/data/webui/cr_components/theme_color_picker/theme_hue_slider_dialog_test.ts
index dcdf1d3..efcbf62 100644
--- a/chrome/test/data/webui/cr_components/theme_color_picker/theme_hue_slider_dialog_test.ts
+++ b/chrome/test/data/webui/cr_components/theme_color_picker/theme_hue_slider_dialog_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://customize-chrome-side-panel.top-chrome/app.js';
 
-import {ThemeHueSliderDialogElement} from 'chrome://resources/cr_components/theme_color_picker/theme_hue_slider_dialog.js';
+import type {ThemeHueSliderDialogElement} from 'chrome://resources/cr_components/theme_color_picker/theme_hue_slider_dialog.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
diff --git a/chrome/test/data/webui/cr_elements/cr_action_menu_test.ts b/chrome/test/data/webui/cr_elements/cr_action_menu_test.ts
index c5bcd8c..7386ec5 100644
--- a/chrome/test/data/webui/cr_elements/cr_action_menu_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_action_menu_test.ts
@@ -5,8 +5,9 @@
 // clang-format off
 import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
 
-import {AnchorAlignment, CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
-import {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
+import type { CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
+import {AnchorAlignment} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
+import type {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
 import {isMac, isWindows} from 'chrome://resources/js/platform.js';
 import {FocusOutlineManager} from 'chrome://resources/js/focus_outline_manager.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_button_test.ts b/chrome/test/data/webui/cr_elements/cr_button_test.ts
index 8f2c370..93429c9 100644
--- a/chrome/test/data/webui/cr_elements/cr_button_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_button_test.ts
@@ -6,7 +6,7 @@
 import 'chrome://resources/cr_elements/cr_button/cr_button.js';
 
 import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
-import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import type {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
diff --git a/chrome/test/data/webui/cr_elements/cr_card_radio_button_test.ts b/chrome/test/data/webui/cr_elements/cr_card_radio_button_test.ts
index bf085cc2..3928b29 100644
--- a/chrome/test/data/webui/cr_elements/cr_card_radio_button_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_card_radio_button_test.ts
@@ -5,7 +5,7 @@
 // clang-format off
 import 'chrome://resources/cr_elements/cr_radio_button/cr_card_radio_button.js';
 
-import {CrCardRadioButtonElement} from 'chrome://resources/cr_elements/cr_radio_button/cr_card_radio_button.js';
+import type {CrCardRadioButtonElement} from 'chrome://resources/cr_elements/cr_radio_button/cr_card_radio_button.js';
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 // clang-format on
diff --git a/chrome/test/data/webui/cr_elements/cr_checkbox_test.ts b/chrome/test/data/webui/cr_elements/cr_checkbox_test.ts
index 972fff6..65e9be2 100644
--- a/chrome/test/data/webui/cr_elements/cr_checkbox_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_checkbox_test.ts
@@ -6,7 +6,7 @@
 import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
 
 import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
-import {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
+import type {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
 import {keyDownOn, keyUpOn, pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {assertEquals, assertFalse, assertTrue, assertLT, assertGT} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_dialog_test.ts b/chrome/test/data/webui/cr_elements/cr_dialog_test.ts
index b3be541..6a3d9b7 100644
--- a/chrome/test/data/webui/cr_elements/cr_dialog_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_dialog_test.ts
@@ -7,8 +7,8 @@
 import 'chrome://resources/cr_elements/cr_input/cr_input.js';
 
 import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
-import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
-import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
+import type {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import type {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
 import {keyDownOn, keyEventOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {assertEquals, assertFalse, assertNotEquals, assertNotReached, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_drawer_test.ts b/chrome/test/data/webui/cr_elements/cr_drawer_test.ts
index 8b86713e..2e1f23c 100644
--- a/chrome/test/data/webui/cr_elements/cr_drawer_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_drawer_test.ts
@@ -5,7 +5,7 @@
 // clang-format off
 import 'chrome://resources/cr_elements/cr_drawer/cr_drawer.js';
 
-import {CrDrawerElement} from 'chrome://resources/cr_elements/cr_drawer/cr_drawer.js';
+import type {CrDrawerElement} from 'chrome://resources/cr_elements/cr_drawer/cr_drawer.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {getTrustedHtml} from 'chrome://webui-test/trusted_html.js';
 import {assertEquals, assertFalse, assertNotEquals, assertThrows, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_expand_button_test.ts b/chrome/test/data/webui/cr_elements/cr_expand_button_test.ts
index 4f8ba91..b99c687 100644
--- a/chrome/test/data/webui/cr_elements/cr_expand_button_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_expand_button_test.ts
@@ -5,8 +5,8 @@
 // clang-format off
 import 'chrome://resources/cr_elements/cr_expand_button/cr_expand_button.js';
 
-import {CrExpandButtonElement} from 'chrome://resources/cr_elements/cr_expand_button/cr_expand_button.js';
-import {CrIconButtonElement} from 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
+import type {CrExpandButtonElement} from 'chrome://resources/cr_elements/cr_expand_button/cr_expand_button.js';
+import type {CrIconButtonElement} from 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 // clang-format on
diff --git a/chrome/test/data/webui/cr_elements/cr_feedback_buttons_test.ts b/chrome/test/data/webui/cr_elements/cr_feedback_buttons_test.ts
index ce295e2..7a9a4b4 100644
--- a/chrome/test/data/webui/cr_elements/cr_feedback_buttons_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_feedback_buttons_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.js';
 
-import {CrFeedbackButtonsElement, CrFeedbackOption} from 'chrome://resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.js';
+import type {CrFeedbackButtonsElement} from 'chrome://resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.js';
+import {CrFeedbackOption} from 'chrome://resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_fingerprint_progress_arc_test.ts b/chrome/test/data/webui/cr_elements/cr_fingerprint_progress_arc_test.ts
index 8cf9aa7..c4b1ce8 100644
--- a/chrome/test/data/webui/cr_elements/cr_fingerprint_progress_arc_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_fingerprint_progress_arc_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://resources/cr_elements/cr_lottie/cr_lottie.js';
 
-import {CrFingerprintProgressArcElement, FINGERPRINT_CHECK_DARK_URL, FINGERPRINT_CHECK_LIGHT_URL, FINGERPRINT_SCANNED_ICON_DARK, FINGERPRINT_SCANNED_ICON_LIGHT, PROGRESS_CIRCLE_BACKGROUND_COLOR_DARK, PROGRESS_CIRCLE_BACKGROUND_COLOR_LIGHT, PROGRESS_CIRCLE_FILL_COLOR_DARK, PROGRESS_CIRCLE_FILL_COLOR_LIGHT} from 'chrome://resources/cr_elements/cr_fingerprint/cr_fingerprint_progress_arc.js';
+import type {CrFingerprintProgressArcElement} from 'chrome://resources/cr_elements/cr_fingerprint/cr_fingerprint_progress_arc.js';
+import {FINGERPRINT_CHECK_DARK_URL, FINGERPRINT_CHECK_LIGHT_URL, FINGERPRINT_SCANNED_ICON_DARK, FINGERPRINT_SCANNED_ICON_LIGHT, PROGRESS_CIRCLE_BACKGROUND_COLOR_DARK, PROGRESS_CIRCLE_BACKGROUND_COLOR_LIGHT, PROGRESS_CIRCLE_FILL_COLOR_DARK, PROGRESS_CIRCLE_FILL_COLOR_LIGHT} from 'chrome://resources/cr_elements/cr_fingerprint/cr_fingerprint_progress_arc.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 import {MockController} from 'chrome://webui-test/mock_controller.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_grid_focus_test.ts b/chrome/test/data/webui/cr_elements/cr_grid_focus_test.ts
index 62c4fc34..6c67a16 100644
--- a/chrome/test/data/webui/cr_elements/cr_grid_focus_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_grid_focus_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://resources/cr_elements/cr_grid/cr_grid.js';
 
-import {CrGridElement} from 'chrome://resources/cr_elements/cr_grid/cr_grid.js';
+import type {CrGridElement} from 'chrome://resources/cr_elements/cr_grid/cr_grid.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_icon_button_test.ts b/chrome/test/data/webui/cr_elements/cr_icon_button_test.ts
index f20a1c0a..7484026 100644
--- a/chrome/test/data/webui/cr_elements/cr_icon_button_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_icon_button_test.ts
@@ -8,7 +8,7 @@
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 
 import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
-import {CrIconButtonElement} from 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
+import type {CrIconButtonElement} from 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
 import {downAndUp, pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_input_test.ts b/chrome/test/data/webui/cr_elements/cr_input_test.ts
index 1f48765..63766ff 100644
--- a/chrome/test/data/webui/cr_elements/cr_input_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_input_test.ts
@@ -5,7 +5,7 @@
 // clang-format off
 import 'chrome://resources/cr_elements/cr_input/cr_input.js';
 
-import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
+import type {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertNotEquals, assertThrows, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise, isChildVisible} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_lazy_render_test.ts b/chrome/test/data/webui/cr_elements/cr_lazy_render_test.ts
index 230a99d..7c035b6 100644
--- a/chrome/test/data/webui/cr_elements/cr_lazy_render_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_lazy_render_test.ts
@@ -7,7 +7,7 @@
 import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
 
 import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
-import {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
+import type {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 // clang-format on
diff --git a/chrome/test/data/webui/cr_elements/cr_link_row_test.ts b/chrome/test/data/webui/cr_elements/cr_link_row_test.ts
index d72a165..b019758d 100644
--- a/chrome/test/data/webui/cr_elements/cr_link_row_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_link_row_test.ts
@@ -5,8 +5,8 @@
 // clang-format off
 import 'chrome://resources/cr_elements/cr_link_row/cr_link_row.js';
 
-import {CrIconButtonElement} from 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
-import {CrLinkRowElement} from 'chrome://resources/cr_elements/cr_link_row/cr_link_row.js';
+import type {CrIconButtonElement} from 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
+import type {CrLinkRowElement} from 'chrome://resources/cr_elements/cr_link_row/cr_link_row.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/cr_elements/cr_lottie_test.ts b/chrome/test/data/webui/cr_elements/cr_lottie_test.ts
index 8547c8cc..80a408831 100644
--- a/chrome/test/data/webui/cr_elements/cr_lottie_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_lottie_test.ts
@@ -5,10 +5,11 @@
 // clang-format off
 import 'chrome://resources/cr_elements/cr_lottie/cr_lottie.js';
 
-import {CrLottieElement} from 'chrome://resources/cr_elements/cr_lottie/cr_lottie.js';
+import type {CrLottieElement} from 'chrome://resources/cr_elements/cr_lottie/cr_lottie.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertNotEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {MockController, MockMethod} from 'chrome://webui-test/mock_controller.js';
+import type { MockMethod} from 'chrome://webui-test/mock_controller.js';
+import {MockController} from 'chrome://webui-test/mock_controller.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 // clang-format on
 
diff --git a/chrome/test/data/webui/cr_elements/cr_menu_selector_focus_test.ts b/chrome/test/data/webui/cr_elements/cr_menu_selector_focus_test.ts
index c99e51e0..643ebe6 100644
--- a/chrome/test/data/webui/cr_elements/cr_menu_selector_focus_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_menu_selector_focus_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://resources/cr_elements/cr_menu_selector/cr_menu_selector.js';
 
-import {CrMenuSelector} from 'chrome://resources/cr_elements/cr_menu_selector/cr_menu_selector.js';
+import type {CrMenuSelector} from 'chrome://resources/cr_elements/cr_menu_selector/cr_menu_selector.js';
 import {FocusOutlineManager} from 'chrome://resources/js/focus_outline_manager.js';
 import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_policy_indicator_test.ts b/chrome/test/data/webui/cr_elements/cr_policy_indicator_test.ts
index d102563..8281582 100644
--- a/chrome/test/data/webui/cr_elements/cr_policy_indicator_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_policy_indicator_test.ts
@@ -7,7 +7,7 @@
 import 'chrome://resources/cr_elements/policy/cr_tooltip_icon.js';
 import './cr_policy_strings.js';
 
-import {CrPolicyIndicatorElement} from 'chrome://resources/cr_elements/policy/cr_policy_indicator.js';
+import type {CrPolicyIndicatorElement} from 'chrome://resources/cr_elements/policy/cr_policy_indicator.js';
 import {CrPolicyIndicatorType} from 'chrome://resources/cr_elements/policy/cr_policy_indicator_mixin.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 // clang-format on
diff --git a/chrome/test/data/webui/cr_elements/cr_policy_pref_indicator_test.ts b/chrome/test/data/webui/cr_elements/cr_policy_pref_indicator_test.ts
index 6df73b8..a0a4e69 100644
--- a/chrome/test/data/webui/cr_elements/cr_policy_pref_indicator_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_policy_pref_indicator_test.ts
@@ -5,7 +5,7 @@
 // clang-format off
 import 'chrome://resources/cr_elements/policy/cr_policy_pref_indicator.js';
 
-import {CrPolicyPrefIndicatorElement} from 'chrome://resources/cr_elements/policy/cr_policy_pref_indicator.js';
+import type {CrPolicyPrefIndicatorElement} from 'chrome://resources/cr_elements/policy/cr_policy_pref_indicator.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/cr_elements/cr_policy_strings.ts b/chrome/test/data/webui/cr_elements/cr_policy_strings.ts
index d790f43c..b6de9e57 100644
--- a/chrome/test/data/webui/cr_elements/cr_policy_strings.ts
+++ b/chrome/test/data/webui/cr_elements/cr_policy_strings.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {CrPolicyStringsType} from 'chrome://resources/cr_elements/policy/cr_policy_indicator_mixin.js';
+import type {CrPolicyStringsType} from 'chrome://resources/cr_elements/policy/cr_policy_indicator_mixin.js';
 
 /** @fileoverview Sets up strings used by policy indicator elements. */
 export const CrPolicyStrings: CrPolicyStringsType = {
diff --git a/chrome/test/data/webui/cr_elements/cr_profile_avatar_selector_test.ts b/chrome/test/data/webui/cr_elements/cr_profile_avatar_selector_test.ts
index 838315a..a3bbc32 100644
--- a/chrome/test/data/webui/cr_elements/cr_profile_avatar_selector_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_profile_avatar_selector_test.ts
@@ -5,9 +5,9 @@
 // clang-format off
 import 'chrome://resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.js';
 
-import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
-import {CrProfileAvatarSelectorElement} from 'chrome://resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.js';
-import {CrProfileAvatarSelectorGridElement} from 'chrome://resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector_grid.js';
+import type {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import type {CrProfileAvatarSelectorElement} from 'chrome://resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.js';
+import type {CrProfileAvatarSelectorGridElement} from 'chrome://resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector_grid.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
 import {keyDownOn, pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_radio_button_test.ts b/chrome/test/data/webui/cr_elements/cr_radio_button_test.ts
index c3a6a64..7c361cca 100644
--- a/chrome/test/data/webui/cr_elements/cr_radio_button_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_radio_button_test.ts
@@ -5,7 +5,7 @@
 // clang-format off
 import 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button.js';
 
-import {CrRadioButtonElement} from 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button.js';
+import type {CrRadioButtonElement} from 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button.js';
 import {assertEquals, assertNotEquals, assertFalse, assertTrue, assertLT, assertGT} from 'chrome://webui-test/chai_assert.js';
 // clang-format on
 
diff --git a/chrome/test/data/webui/cr_elements/cr_radio_group_test.ts b/chrome/test/data/webui/cr_elements/cr_radio_group_test.ts
index 561491c..5a683188 100644
--- a/chrome/test/data/webui/cr_elements/cr_radio_group_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_radio_group_test.ts
@@ -5,8 +5,8 @@
 import 'chrome://resources/cr_elements/cr_radio_group/cr_radio_group.js';
 import 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button.js';
 
-import {CrRadioButtonElement} from 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button.js';
-import {CrRadioGroupElement} from 'chrome://resources/cr_elements/cr_radio_group/cr_radio_group.js';
+import type {CrRadioButtonElement} from 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button.js';
+import type {CrRadioGroupElement} from 'chrome://resources/cr_elements/cr_radio_group/cr_radio_group.js';
 import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_scrollable_mixin_test.ts b/chrome/test/data/webui/cr_elements/cr_scrollable_mixin_test.ts
index 3db72a76..2e4f80ca 100644
--- a/chrome/test/data/webui/cr_elements/cr_scrollable_mixin_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_scrollable_mixin_test.ts
@@ -6,7 +6,7 @@
 import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
 
 import {CrScrollableMixin} from 'chrome://resources/cr_elements/cr_scrollable_mixin.js';
-import {IronListElement} from 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
+import type {IronListElement} from 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
 import {flush, html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_search_field_test.ts b/chrome/test/data/webui/cr_elements/cr_search_field_test.ts
index 7840ace7..57dee88 100644
--- a/chrome/test/data/webui/cr_elements/cr_search_field_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_search_field_test.ts
@@ -5,7 +5,7 @@
 // clang-format off
 import 'chrome://resources/cr_elements/cr_search_field/cr_search_field.js';
 
-import {CrSearchFieldElement} from 'chrome://resources/cr_elements/cr_search_field/cr_search_field.js';
+import type {CrSearchFieldElement} from 'chrome://resources/cr_elements/cr_search_field/cr_search_field.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertNotReached, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_searchable_drop_down_test.ts b/chrome/test/data/webui/cr_elements/cr_searchable_drop_down_test.ts
index a94391e..b34e52a 100644
--- a/chrome/test/data/webui/cr_elements/cr_searchable_drop_down_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_searchable_drop_down_test.ts
@@ -6,8 +6,8 @@
 import 'chrome://resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js';
 
 import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
-import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
-import {CrSearchableDropDownElement} from 'chrome://resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js';
+import type {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
+import type {CrSearchableDropDownElement} from 'chrome://resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js';
 import {keyDownOn, move} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_slider_test.ts b/chrome/test/data/webui/cr_elements/cr_slider_test.ts
index b5b30a7..29d87cc 100644
--- a/chrome/test/data/webui/cr_elements/cr_slider_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_slider_test.ts
@@ -6,7 +6,7 @@
 import 'chrome://resources/cr_elements/cr_slider/cr_slider.js';
 
 import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
-import {CrSliderElement} from 'chrome://resources/cr_elements/cr_slider/cr_slider.js';
+import type {CrSliderElement} from 'chrome://resources/cr_elements/cr_slider/cr_slider.js';
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_splitter_test.ts b/chrome/test/data/webui/cr_elements/cr_splitter_test.ts
index e04ba4c..3be4b1db 100644
--- a/chrome/test/data/webui/cr_elements/cr_splitter_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_splitter_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://resources/cr_elements/cr_splitter/cr_splitter.js';
 
-import {CrSplitterElement} from 'chrome://resources/cr_elements/cr_splitter/cr_splitter.js';
+import type {CrSplitterElement} from 'chrome://resources/cr_elements/cr_splitter/cr_splitter.js';
 import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/cr_elements/cr_tabs_test.ts b/chrome/test/data/webui/cr_elements/cr_tabs_test.ts
index ceacf2f..d637e767 100644
--- a/chrome/test/data/webui/cr_elements/cr_tabs_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_tabs_test.ts
@@ -5,7 +5,7 @@
 // clang-format off
 import 'chrome://resources/cr_elements/cr_tabs/cr_tabs.js';
 
-import {CrTabsElement} from 'chrome://resources/cr_elements/cr_tabs/cr_tabs.js';
+import type {CrTabsElement} from 'chrome://resources/cr_elements/cr_tabs/cr_tabs.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {assertEquals, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_textarea_focus_test.ts b/chrome/test/data/webui/cr_elements/cr_textarea_focus_test.ts
index a988503b..4c4c94f 100644
--- a/chrome/test/data/webui/cr_elements/cr_textarea_focus_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_textarea_focus_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://resources/cr_elements/cr_textarea/cr_textarea.js';
 
-import {CrTextareaElement} from 'chrome://resources/cr_elements/cr_textarea/cr_textarea.js';
+import type {CrTextareaElement} from 'chrome://resources/cr_elements/cr_textarea/cr_textarea.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_toast_manager_test.ts b/chrome/test/data/webui/cr_elements/cr_toast_manager_test.ts
index 8d97cc16..ec467f5 100644
--- a/chrome/test/data/webui/cr_elements/cr_toast_manager_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_toast_manager_test.ts
@@ -5,8 +5,8 @@
 // clang-format off
 import 'chrome://resources/cr_elements/cr_toast/cr_toast_manager.js';
 
-import {CrToastManagerElement, getToastManager} from 'chrome://resources/cr_elements/cr_toast/cr_toast_manager.js';
-
+import type {CrToastManagerElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast_manager.js';
+import { getToastManager} from 'chrome://resources/cr_elements/cr_toast/cr_toast_manager.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 // clang-format on
diff --git a/chrome/test/data/webui/cr_elements/cr_toast_test.ts b/chrome/test/data/webui/cr_elements/cr_toast_test.ts
index b24f1f15..2a10405 100644
--- a/chrome/test/data/webui/cr_elements/cr_toast_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_toast_test.ts
@@ -5,7 +5,7 @@
 // clang-format off
 import 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
 
-import {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
+import type {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
 import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {MockTimer} from 'chrome://webui-test/mock_timer.js';
 // clang-format on
diff --git a/chrome/test/data/webui/cr_elements/cr_toggle_test.ts b/chrome/test/data/webui/cr_elements/cr_toggle_test.ts
index 8879836..e4b8f9a 100644
--- a/chrome/test/data/webui/cr_elements/cr_toggle_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_toggle_test.ts
@@ -6,7 +6,8 @@
 import 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
 
 import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
-import {CrToggleElement, MOVE_THRESHOLD_PX} from 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
+import type {CrToggleElement} from 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
+import { MOVE_THRESHOLD_PX} from 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 // clang-format on
diff --git a/chrome/test/data/webui/cr_elements/cr_toolbar_focus_test.ts b/chrome/test/data/webui/cr_elements/cr_toolbar_focus_test.ts
index fd3b17d..d919097 100644
--- a/chrome/test/data/webui/cr_elements/cr_toolbar_focus_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_toolbar_focus_test.ts
@@ -7,7 +7,7 @@
 // clang-format off
 import 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar.js';
 
-import {CrToolbarElement} from 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar.js';
+import type {CrToolbarElement} from 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 // clang-format on
 
diff --git a/chrome/test/data/webui/cr_elements/cr_toolbar_search_field_test.ts b/chrome/test/data/webui/cr_elements/cr_toolbar_search_field_test.ts
index acd1e6c..b89aa103 100644
--- a/chrome/test/data/webui/cr_elements/cr_toolbar_search_field_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_toolbar_search_field_test.ts
@@ -5,7 +5,7 @@
 // clang-format off
 import 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js';
 
-import {CrToolbarSearchFieldElement} from 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js';
+import type {CrToolbarSearchFieldElement} from 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js';
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertNotEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_toolbar_test.ts b/chrome/test/data/webui/cr_elements/cr_toolbar_test.ts
index 60d95db..83c6988 100644
--- a/chrome/test/data/webui/cr_elements/cr_toolbar_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_toolbar_test.ts
@@ -7,7 +7,7 @@
 // clang-format off
 import 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar.js';
 
-import {CrToolbarElement} from 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar.js';
+import type {CrToolbarElement} from 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {getTrustedHtml} from 'chrome://webui-test/trusted_html.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_tree_test.ts b/chrome/test/data/webui/cr_elements/cr_tree_test.ts
index 444f524b..8ac4739 100644
--- a/chrome/test/data/webui/cr_elements/cr_tree_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_tree_test.ts
@@ -6,8 +6,9 @@
 import 'chrome://resources/cr_elements/cr_tree/cr_tree_item.js';
 import 'chrome://resources/cr_elements/cr_tree/cr_tree.js';
 
-import {CrTreeElement} from 'chrome://resources/cr_elements/cr_tree/cr_tree.js';
-import {CrTreeItemElement, SELECTED_ATTR} from 'chrome://resources/cr_elements/cr_tree/cr_tree_item.js';
+import type {CrTreeElement} from 'chrome://resources/cr_elements/cr_tree/cr_tree.js';
+import type {CrTreeItemElement} from 'chrome://resources/cr_elements/cr_tree/cr_tree_item.js';
+import { SELECTED_ATTR} from 'chrome://resources/cr_elements/cr_tree/cr_tree_item.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
diff --git a/chrome/test/data/webui/cr_elements/cr_url_list_item_test.ts b/chrome/test/data/webui/cr_elements/cr_url_list_item_test.ts
index 5d0ac7d..4259b4a 100644
--- a/chrome/test/data/webui/cr_elements/cr_url_list_item_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_url_list_item_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://resources/cr_elements/cr_url_list_item/cr_url_list_item.js';
 
-import {CrUrlListItemElement, CrUrlListItemSize} from 'chrome://resources/cr_elements/cr_url_list_item/cr_url_list_item.js';
+import type {CrUrlListItemElement} from 'chrome://resources/cr_elements/cr_url_list_item/cr_url_list_item.js';
+import {CrUrlListItemSize} from 'chrome://resources/cr_elements/cr_url_list_item/cr_url_list_item.js';
 import {getFaviconForPageURL} from 'chrome://resources/js/icon.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/cr_elements/cr_view_manager_test.ts b/chrome/test/data/webui/cr_elements/cr_view_manager_test.ts
index 9c819e8..3355294 100644
--- a/chrome/test/data/webui/cr_elements/cr_view_manager_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_view_manager_test.ts
@@ -5,7 +5,7 @@
 // clang-format off
 import 'chrome://resources/cr_elements/cr_view_manager/cr_view_manager.js';
 
-import {CrViewManagerElement} from 'chrome://resources/cr_elements/cr_view_manager/cr_view_manager.js';
+import type {CrViewManagerElement} from 'chrome://resources/cr_elements/cr_view_manager/cr_view_manager.js';
 import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 import {isChildVisible} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/cr_elements/iron_list_focus_test.ts b/chrome/test/data/webui/cr_elements/iron_list_focus_test.ts
index d29e095..5d5a06cc 100644
--- a/chrome/test/data/webui/cr_elements/iron_list_focus_test.ts
+++ b/chrome/test/data/webui/cr_elements/iron_list_focus_test.ts
@@ -6,7 +6,7 @@
 import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
 
 import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
-import {IronListElement} from 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
+import type {IronListElement} from 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 // clang-format on
diff --git a/chrome/test/data/webui/cr_elements/store_client_test.ts b/chrome/test/data/webui/cr_elements/store_client_test.ts
index eac0ce6..063e0a1 100644
--- a/chrome/test/data/webui/cr_elements/store_client_test.ts
+++ b/chrome/test/data/webui/cr_elements/store_client_test.ts
@@ -3,7 +3,8 @@
 // found in the LICENSE file.
 
 import {makeStoreClientMixin} from 'chrome://resources/cr_elements/store_client/store_client.js';
-import {Action, Store} from 'chrome://resources/js/store.js';
+import type {Action} from 'chrome://resources/js/store.js';
+import {Store} from 'chrome://resources/js/store.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/downloads/item_test.ts b/chrome/test/data/webui/downloads/item_test.ts
index 3d68653..fda8515c 100644
--- a/chrome/test/data/webui/downloads/item_test.ts
+++ b/chrome/test/data/webui/downloads/item_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BrowserProxy, CrToastManagerElement, DangerType, DownloadsItemElement, IconLoaderImpl, loadTimeData, SafeBrowsingState, State} from 'chrome://downloads/downloads.js';
+import type {CrToastManagerElement, DownloadsItemElement} from 'chrome://downloads/downloads.js';
+import {BrowserProxy, DangerType, IconLoaderImpl, loadTimeData, SafeBrowsingState, State} from 'chrome://downloads/downloads.js';
 import {stringToMojoString16, stringToMojoUrl} from 'chrome://resources/js/mojo_type_util.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertNotReached, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/downloads/manager_test.ts b/chrome/test/data/webui/downloads/manager_test.ts
index 4b309dae..dc933f2 100644
--- a/chrome/test/data/webui/downloads/manager_test.ts
+++ b/chrome/test/data/webui/downloads/manager_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BrowserProxy, CrToastManagerElement, DangerType, DownloadsManagerElement, loadTimeData, PageRemote, State} from 'chrome://downloads/downloads.js';
+import type {CrToastManagerElement, DownloadsManagerElement, PageRemote} from 'chrome://downloads/downloads.js';
+import {BrowserProxy, DangerType, loadTimeData, State} from 'chrome://downloads/downloads.js';
 import {stringToMojoString16, stringToMojoUrl} from 'chrome://resources/js/mojo_type_util.js';
 import {isMac} from 'chrome://resources/js/platform.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
diff --git a/chrome/test/data/webui/downloads/test_support.ts b/chrome/test/data/webui/downloads/test_support.ts
index c786f2c..b232eabb 100644
--- a/chrome/test/data/webui/downloads/test_support.ts
+++ b/chrome/test/data/webui/downloads/test_support.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {DangerType, IconLoader, MojomData, PageCallbackRouter, PageHandlerInterface, PageRemote, SafeBrowsingState, State} from 'chrome://downloads/downloads.js';
+import type {IconLoader, MojomData, PageHandlerInterface, PageRemote} from 'chrome://downloads/downloads.js';
+import {DangerType, PageCallbackRouter, SafeBrowsingState, State} from 'chrome://downloads/downloads.js';
 import {stringToMojoString16, stringToMojoUrl} from 'chrome://resources/js/mojo_type_util.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/downloads/toolbar_test.ts b/chrome/test/data/webui/downloads/toolbar_test.ts
index 6488a51..fc965a8 100644
--- a/chrome/test/data/webui/downloads/toolbar_test.ts
+++ b/chrome/test/data/webui/downloads/toolbar_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {CrToastManagerElement, DownloadsToolbarElement, SearchService} from 'chrome://downloads/downloads.js';
+import type {CrToastManagerElement, DownloadsToolbarElement} from 'chrome://downloads/downloads.js';
+import {SearchService} from 'chrome://downloads/downloads.js';
 import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 import {createDownload} from './test_support.js';
diff --git a/chrome/test/data/webui/engagement/site_engagement_test.ts b/chrome/test/data/webui/engagement/site_engagement_test.ts
index 93e5ded..220a8f1a 100644
--- a/chrome/test/data/webui/engagement/site_engagement_test.ts
+++ b/chrome/test/data/webui/engagement/site_engagement_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://site-engagement/app.js';
 
-import {SiteEngagementAppElement} from 'chrome://site-engagement/app.js';
+import type {SiteEngagementAppElement} from 'chrome://site-engagement/app.js';
 import {assertDeepEquals, assertEquals} from 'chrome://webui-test/chai_assert.js';
 
 suite('SiteEngagement', function() {
diff --git a/chrome/test/data/webui/extensions/activity_log_history_item_test.ts b/chrome/test/data/webui/extensions/activity_log_history_item_test.ts
index c5ae313..99e0a3da 100644
--- a/chrome/test/data/webui/extensions/activity_log_history_item_test.ts
+++ b/chrome/test/data/webui/extensions/activity_log_history_item_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://extensions/extensions.js';
 
-import {ActivityGroup, ActivityLogHistoryItemElement} from 'chrome://extensions/extensions.js';
+import type {ActivityGroup, ActivityLogHistoryItemElement} from 'chrome://extensions/extensions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/extensions/activity_log_history_test.ts b/chrome/test/data/webui/extensions/activity_log_history_test.ts
index 27a7ebc..88748fa 100644
--- a/chrome/test/data/webui/extensions/activity_log_history_test.ts
+++ b/chrome/test/data/webui/extensions/activity_log_history_test.ts
@@ -4,7 +4,8 @@
 
 /** @fileoverview Suite of tests for activity-log-history. */
 
-import {ActivityLogHistoryElement, ActivityLogPageState} from 'chrome://extensions/extensions.js';
+import type {ActivityLogHistoryElement} from 'chrome://extensions/extensions.js';
+import {ActivityLogPageState} from 'chrome://extensions/extensions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/extensions/activity_log_stream_item_test.ts b/chrome/test/data/webui/extensions/activity_log_stream_item_test.ts
index c526ea8..642e6bd 100644
--- a/chrome/test/data/webui/extensions/activity_log_stream_item_test.ts
+++ b/chrome/test/data/webui/extensions/activity_log_stream_item_test.ts
@@ -4,7 +4,8 @@
 
 /** @fileoverview Suite of tests for activity-log-stream-item. */
 
-import {ActivityLogStreamItemElement, ARG_URL_PLACEHOLDER, StreamItem} from 'chrome://extensions/extensions.js';
+import type {ActivityLogStreamItemElement, StreamItem} from 'chrome://extensions/extensions.js';
+import {ARG_URL_PLACEHOLDER} from 'chrome://extensions/extensions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/extensions/activity_log_stream_test.ts b/chrome/test/data/webui/extensions/activity_log_stream_test.ts
index cb0103ed..171cfd8c 100644
--- a/chrome/test/data/webui/extensions/activity_log_stream_test.ts
+++ b/chrome/test/data/webui/extensions/activity_log_stream_test.ts
@@ -6,7 +6,7 @@
 
 import 'chrome://extensions/extensions.js';
 
-import {ActivityLogStreamElement} from 'chrome://extensions/extensions.js';
+import type {ActivityLogStreamElement} from 'chrome://extensions/extensions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/extensions/activity_log_test.ts b/chrome/test/data/webui/extensions/activity_log_test.ts
index e169c96..32b9aa7 100644
--- a/chrome/test/data/webui/extensions/activity_log_test.ts
+++ b/chrome/test/data/webui/extensions/activity_log_test.ts
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ActivityLogExtensionPlaceholder, ExtensionsActivityLogElement, navigation, Page} from 'chrome://extensions/extensions.js';
-
+import type {ActivityLogExtensionPlaceholder, ExtensionsActivityLogElement} from 'chrome://extensions/extensions.js';
+import {navigation, Page} from 'chrome://extensions/extensions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals} from 'chrome://webui-test/chai_assert.js';
+
 import {TestService} from './test_service.js';
 import {createExtensionInfo, testVisible} from './test_util.js';
 
diff --git a/chrome/test/data/webui/extensions/code_section_test.ts b/chrome/test/data/webui/extensions/code_section_test.ts
index 4d38545d..9f3d981 100644
--- a/chrome/test/data/webui/extensions/code_section_test.ts
+++ b/chrome/test/data/webui/extensions/code_section_test.ts
@@ -5,7 +5,7 @@
 /** @fileoverview Suite of tests for extensions-code-section. */
 import 'chrome://extensions/extensions.js';
 
-import {ExtensionsCodeSectionElement} from 'chrome://extensions/extensions.js';
+import type {ExtensionsCodeSectionElement} from 'chrome://extensions/extensions.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {isChildVisible} from 'chrome://webui-test/test_util.js';
 
diff --git a/chrome/test/data/webui/extensions/detail_view_test.ts b/chrome/test/data/webui/extensions/detail_view_test.ts
index cc8cf3c..dc8a8f8 100644
--- a/chrome/test/data/webui/extensions/detail_view_test.ts
+++ b/chrome/test/data/webui/extensions/detail_view_test.ts
@@ -4,7 +4,8 @@
 
 /** @fileoverview Suite of tests for extensions-detail-view. */
 
-import {CrCheckboxElement, ExtensionsDetailViewElement, ExtensionsToggleRowElement, navigation, Page} from 'chrome://extensions/extensions.js';
+import type {CrCheckboxElement, ExtensionsDetailViewElement, ExtensionsToggleRowElement} from 'chrome://extensions/extensions.js';
+import {navigation, Page} from 'chrome://extensions/extensions.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/extensions/error_page_test.ts b/chrome/test/data/webui/extensions/error_page_test.ts
index f4ae09e..2381ee0 100644
--- a/chrome/test/data/webui/extensions/error_page_test.ts
+++ b/chrome/test/data/webui/extensions/error_page_test.ts
@@ -5,7 +5,7 @@
 /** @fileoverview Suite of tests for extensions-detail-view. */
 import 'chrome://extensions/extensions.js';
 
-import {ErrorPageDelegate, ExtensionsErrorPageElement} from 'chrome://extensions/extensions.js';
+import type {ErrorPageDelegate, ExtensionsErrorPageElement} from 'chrome://extensions/extensions.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/extensions/host_permissions_toggle_list_test.ts b/chrome/test/data/webui/extensions/host_permissions_toggle_list_test.ts
index 74c4c89..dbaf418 100644
--- a/chrome/test/data/webui/extensions/host_permissions_toggle_list_test.ts
+++ b/chrome/test/data/webui/extensions/host_permissions_toggle_list_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ExtensionsHostPermissionsToggleListElement, ExtensionsToggleRowElement, UserAction} from 'chrome://extensions/extensions.js';
+import type {ExtensionsHostPermissionsToggleListElement, ExtensionsToggleRowElement} from 'chrome://extensions/extensions.js';
+import {UserAction} from 'chrome://extensions/extensions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/extensions/item_list_test.ts b/chrome/test/data/webui/extensions/item_list_test.ts
index b2a6a99..3958546e 100644
--- a/chrome/test/data/webui/extensions/item_list_test.ts
+++ b/chrome/test/data/webui/extensions/item_list_test.ts
@@ -5,7 +5,7 @@
 /** @fileoverview Suite of tests for extensions-item-list. */
 import 'chrome://extensions/extensions.js';
 
-import {ExtensionsItemListElement} from 'chrome://extensions/extensions.js';
+import type {ExtensionsItemListElement} from 'chrome://extensions/extensions.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/extensions/item_test.ts b/chrome/test/data/webui/extensions/item_test.ts
index 73d0bc5..27f525f 100644
--- a/chrome/test/data/webui/extensions/item_test.ts
+++ b/chrome/test/data/webui/extensions/item_test.ts
@@ -4,7 +4,8 @@
 
 /** @fileoverview Suite of tests for extension-item. */
 
-import {ExtensionsItemElement, IronIconElement, navigation, Page} from 'chrome://extensions/extensions.js';
+import type {ExtensionsItemElement, IronIconElement} from 'chrome://extensions/extensions.js';
+import {navigation, Page} from 'chrome://extensions/extensions.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/extensions/keyboard_shortcuts_test.ts b/chrome/test/data/webui/extensions/keyboard_shortcuts_test.ts
index 217a9ad1..1bf4dd4f 100644
--- a/chrome/test/data/webui/extensions/keyboard_shortcuts_test.ts
+++ b/chrome/test/data/webui/extensions/keyboard_shortcuts_test.ts
@@ -4,7 +4,8 @@
 
 /** @fileoverview Suite of tests for extension-keyboard-shortcuts. */
 
-import {ExtensionsKeyboardShortcutsElement, isValidKeyCode, Key, keystrokeToString} from 'chrome://extensions/extensions.js';
+import type {ExtensionsKeyboardShortcutsElement} from 'chrome://extensions/extensions.js';
+import {isValidKeyCode, Key, keystrokeToString} from 'chrome://extensions/extensions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {isChildVisible} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/extensions/kiosk_mode_manager_unit_test.ts b/chrome/test/data/webui/extensions/kiosk_mode_manager_unit_test.ts
index 2ac34d5..b5dd750 100644
--- a/chrome/test/data/webui/extensions/kiosk_mode_manager_unit_test.ts
+++ b/chrome/test/data/webui/extensions/kiosk_mode_manager_unit_test.ts
@@ -8,7 +8,8 @@
  * chrome.developerPrivate API.
  */
 
-import {ExtensionsManagerElement, KioskBrowserProxyImpl, Service} from 'chrome://extensions/extensions.js';
+import type {ExtensionsManagerElement} from 'chrome://extensions/extensions.js';
+import {KioskBrowserProxyImpl, Service} from 'chrome://extensions/extensions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/extensions/kiosk_mode_test.ts b/chrome/test/data/webui/extensions/kiosk_mode_test.ts
index b2b21aa..9b944ef 100644
--- a/chrome/test/data/webui/extensions/kiosk_mode_test.ts
+++ b/chrome/test/data/webui/extensions/kiosk_mode_test.ts
@@ -4,7 +4,8 @@
 
 /** @fileoverview Suite of tests for extension-kiosk-dialog. */
 
-import {CrCheckboxElement, ExtensionsKioskDialogElement, KioskApp, KioskAppSettings, KioskBrowserProxyImpl, KioskSettings} from 'chrome://extensions/extensions.js';
+import type {CrCheckboxElement, ExtensionsKioskDialogElement, KioskApp, KioskAppSettings, KioskSettings} from 'chrome://extensions/extensions.js';
+import {KioskBrowserProxyImpl} from 'chrome://extensions/extensions.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/extensions/load_error_test.ts b/chrome/test/data/webui/extensions/load_error_test.ts
index dad0a68..9f9e16f 100644
--- a/chrome/test/data/webui/extensions/load_error_test.ts
+++ b/chrome/test/data/webui/extensions/load_error_test.ts
@@ -6,7 +6,7 @@
 
 import 'chrome://extensions/extensions.js';
 
-import {ExtensionsLoadErrorElement} from 'chrome://extensions/extensions.js';
+import type {ExtensionsLoadErrorElement} from 'chrome://extensions/extensions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/extensions/manager_test.ts b/chrome/test/data/webui/extensions/manager_test.ts
index 8d5f18ae..327f6721 100644
--- a/chrome/test/data/webui/extensions/manager_test.ts
+++ b/chrome/test/data/webui/extensions/manager_test.ts
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ChromeEvent} from '/tools/typescript/definitions/chrome_event.js';
-import {ExtensionsManagerElement, navigation, Page, Service} from 'chrome://extensions/extensions.js';
+import type {ChromeEvent} from '/tools/typescript/definitions/chrome_event.js';
+import type {ExtensionsManagerElement} from 'chrome://extensions/extensions.js';
+import {navigation, Page, Service} from 'chrome://extensions/extensions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/extensions/manager_test_with_activity_log_flag.ts b/chrome/test/data/webui/extensions/manager_test_with_activity_log_flag.ts
index e5863f7..3178d015 100644
--- a/chrome/test/data/webui/extensions/manager_test_with_activity_log_flag.ts
+++ b/chrome/test/data/webui/extensions/manager_test_with_activity_log_flag.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://extensions/extensions.js';
 
-import {ExtensionsManagerElement, navigation, Page} from 'chrome://extensions/extensions.js';
+import type {ExtensionsManagerElement} from 'chrome://extensions/extensions.js';
+import {navigation, Page} from 'chrome://extensions/extensions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/extensions/manager_test_with_id_query_param.ts b/chrome/test/data/webui/extensions/manager_test_with_id_query_param.ts
index 5024bc1..0bd4081 100644
--- a/chrome/test/data/webui/extensions/manager_test_with_id_query_param.ts
+++ b/chrome/test/data/webui/extensions/manager_test_with_id_query_param.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://extensions/extensions.js';
 
-import {ExtensionsManagerElement, navigation, Page} from 'chrome://extensions/extensions.js';
+import type {ExtensionsManagerElement} from 'chrome://extensions/extensions.js';
+import {navigation, Page} from 'chrome://extensions/extensions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/extensions/manager_unit_test.ts b/chrome/test/data/webui/extensions/manager_unit_test.ts
index 8d808e9c..a52676b2 100644
--- a/chrome/test/data/webui/extensions/manager_unit_test.ts
+++ b/chrome/test/data/webui/extensions/manager_unit_test.ts
@@ -8,7 +8,8 @@
  * chrome.developerPrivate API.
  */
 
-import {ExtensionsManagerElement, navigation, Page, Service} from 'chrome://extensions/extensions.js';
+import type {ExtensionsManagerElement} from 'chrome://extensions/extensions.js';
+import {navigation, Page, Service} from 'chrome://extensions/extensions.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/extensions/manager_unit_test_with_activity_log_flag.ts b/chrome/test/data/webui/extensions/manager_unit_test_with_activity_log_flag.ts
index c0bdeb5..0bcef65b 100644
--- a/chrome/test/data/webui/extensions/manager_unit_test_with_activity_log_flag.ts
+++ b/chrome/test/data/webui/extensions/manager_unit_test_with_activity_log_flag.ts
@@ -10,7 +10,8 @@
 
 import 'chrome://extensions/extensions.js';
 
-import {ExtensionsManagerElement, navigation, Page, Service} from 'chrome://extensions/extensions.js';
+import type {ExtensionsManagerElement} from 'chrome://extensions/extensions.js';
+import {navigation, Page, Service} from 'chrome://extensions/extensions.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 import {TestService} from './test_service.js';
diff --git a/chrome/test/data/webui/extensions/navigation_helper_test.ts b/chrome/test/data/webui/extensions/navigation_helper_test.ts
index 0d4417e..84dbf1ea 100644
--- a/chrome/test/data/webui/extensions/navigation_helper_test.ts
+++ b/chrome/test/data/webui/extensions/navigation_helper_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Dialog, NavigationHelper, Page, PageState} from 'chrome://extensions/extensions.js';
+import type {PageState} from 'chrome://extensions/extensions.js';
+import {Dialog, NavigationHelper, Page} from 'chrome://extensions/extensions.js';
 import {assertDeepEquals, assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {MockMethod} from 'chrome://webui-test/mock_controller.js';
 
diff --git a/chrome/test/data/webui/extensions/options_dialog_test.ts b/chrome/test/data/webui/extensions/options_dialog_test.ts
index 16f31122..cda949d 100644
--- a/chrome/test/data/webui/extensions/options_dialog_test.ts
+++ b/chrome/test/data/webui/extensions/options_dialog_test.ts
@@ -5,7 +5,8 @@
 /** @fileoverview Suite of tests for extension-options-dialog. */
 import 'chrome://extensions/extensions.js';
 
-import {ExtensionsOptionsDialogElement, OptionsDialogMaxHeight, OptionsDialogMinWidth, Service} from 'chrome://extensions/extensions.js';
+import type {ExtensionsOptionsDialogElement} from 'chrome://extensions/extensions.js';
+import {OptionsDialogMaxHeight, OptionsDialogMinWidth, Service} from 'chrome://extensions/extensions.js';
 import {assertEquals, assertFalse, assertGE, assertLE, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
diff --git a/chrome/test/data/webui/extensions/pack_dialog_test.ts b/chrome/test/data/webui/extensions/pack_dialog_test.ts
index 75a29458..5c38e93 100644
--- a/chrome/test/data/webui/extensions/pack_dialog_test.ts
+++ b/chrome/test/data/webui/extensions/pack_dialog_test.ts
@@ -6,7 +6,7 @@
 
 import 'chrome://extensions/extensions.js';
 
-import {ExtensionsPackDialogAlertElement, ExtensionsPackDialogElement, PackDialogDelegate} from 'chrome://extensions/extensions.js';
+import type {ExtensionsPackDialogAlertElement, ExtensionsPackDialogElement, PackDialogDelegate} from 'chrome://extensions/extensions.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/extensions/review_panel_test.ts b/chrome/test/data/webui/extensions/review_panel_test.ts
index 090bc35..ae5fe1d 100644
--- a/chrome/test/data/webui/extensions/review_panel_test.ts
+++ b/chrome/test/data/webui/extensions/review_panel_test.ts
@@ -5,7 +5,8 @@
 /** @fileoverview Suite of tests for extensions-review-panel. */
 import 'chrome://extensions/extensions.js';
 
-import {ExtensionsHatsBrowserProxyImpl, ExtensionsReviewPanelElement, PluralStringProxyImpl} from 'chrome://extensions/extensions.js';
+import type {ExtensionsReviewPanelElement} from 'chrome://extensions/extensions.js';
+import {ExtensionsHatsBrowserProxyImpl, PluralStringProxyImpl} from 'chrome://extensions/extensions.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/extensions/runtime_host_permissions_test.ts b/chrome/test/data/webui/extensions/runtime_host_permissions_test.ts
index 04620bc..26abfc8 100644
--- a/chrome/test/data/webui/extensions/runtime_host_permissions_test.ts
+++ b/chrome/test/data/webui/extensions/runtime_host_permissions_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://extensions/extensions.js';
 
-import {ExtensionsRuntimeHostPermissionsElement} from 'chrome://extensions/extensions.js';
+import type {ExtensionsRuntimeHostPermissionsElement} from 'chrome://extensions/extensions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise, isChildVisible} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.ts b/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.ts
index 49af022c..0b10a28 100644
--- a/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.ts
+++ b/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://extensions/extensions.js';
 
-import {ExtensionsRuntimeHostsDialogElement, getMatchingUserSpecifiedSites, getPatternFromSite} from 'chrome://extensions/extensions.js';
+import type {ExtensionsRuntimeHostsDialogElement} from 'chrome://extensions/extensions.js';
+import {getMatchingUserSpecifiedSites, getPatternFromSite} from 'chrome://extensions/extensions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/extensions/shortcut_input_test.ts b/chrome/test/data/webui/extensions/shortcut_input_test.ts
index afccc343..7930ede 100644
--- a/chrome/test/data/webui/extensions/shortcut_input_test.ts
+++ b/chrome/test/data/webui/extensions/shortcut_input_test.ts
@@ -10,7 +10,7 @@
 
 import 'chrome://extensions/extensions.js';
 
-import {ExtensionsShortcutInputElement} from 'chrome://extensions/extensions.js';
+import type {ExtensionsShortcutInputElement} from 'chrome://extensions/extensions.js';
 import {keyDownOn, keyUpOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/extensions/sidebar_test.ts b/chrome/test/data/webui/extensions/sidebar_test.ts
index 881e6ad8..e6d47a4 100644
--- a/chrome/test/data/webui/extensions/sidebar_test.ts
+++ b/chrome/test/data/webui/extensions/sidebar_test.ts
@@ -3,7 +3,8 @@
 // found in the LICENSE file.
 
 /** @fileoverview Suite of tests for extension-sidebar. */
-import {ExtensionsSidebarElement, navigation, Page} from 'chrome://extensions/extensions.js';
+import type {ExtensionsSidebarElement} from 'chrome://extensions/extensions.js';
+import {navigation, Page} from 'chrome://extensions/extensions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/extensions/site_permissions_by_site_test.ts b/chrome/test/data/webui/extensions/site_permissions_by_site_test.ts
index 73a0a6e8..ca38ff1c 100644
--- a/chrome/test/data/webui/extensions/site_permissions_by_site_test.ts
+++ b/chrome/test/data/webui/extensions/site_permissions_by_site_test.ts
@@ -5,7 +5,8 @@
 /** @fileoverview Suite of tests for extension-site-permissions-all-sites. */
 import 'chrome://extensions/extensions.js';
 
-import {ExtensionsSitePermissionsBySiteElement, navigation, Page} from 'chrome://extensions/extensions.js';
+import type {ExtensionsSitePermissionsBySiteElement} from 'chrome://extensions/extensions.js';
+import {navigation, Page} from 'chrome://extensions/extensions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/extensions/site_permissions_edit_permissions_dialog_test.ts b/chrome/test/data/webui/extensions/site_permissions_edit_permissions_dialog_test.ts
index e7d9ffa..4711aa3 100644
--- a/chrome/test/data/webui/extensions/site_permissions_edit_permissions_dialog_test.ts
+++ b/chrome/test/data/webui/extensions/site_permissions_edit_permissions_dialog_test.ts
@@ -8,7 +8,7 @@
  * */
 import 'chrome://extensions/extensions.js';
 
-import {SitePermissionsEditPermissionsDialogElement} from 'chrome://extensions/extensions.js';
+import type {SitePermissionsEditPermissionsDialogElement} from 'chrome://extensions/extensions.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/extensions/site_permissions_edit_url_dialog_test.ts b/chrome/test/data/webui/extensions/site_permissions_edit_url_dialog_test.ts
index 62c8e6bc..4b24a22 100644
--- a/chrome/test/data/webui/extensions/site_permissions_edit_url_dialog_test.ts
+++ b/chrome/test/data/webui/extensions/site_permissions_edit_url_dialog_test.ts
@@ -5,7 +5,8 @@
 /** @fileoverview Suite of tests for site-permissions-edit-url-dialog. */
 import 'chrome://extensions/extensions.js';
 
-import {getSitePermissionsPatternFromSite, SitePermissionsEditUrlDialogElement} from 'chrome://extensions/extensions.js';
+import type {SitePermissionsEditUrlDialogElement} from 'chrome://extensions/extensions.js';
+import {getSitePermissionsPatternFromSite} from 'chrome://extensions/extensions.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
diff --git a/chrome/test/data/webui/extensions/site_permissions_list_test.ts b/chrome/test/data/webui/extensions/site_permissions_list_test.ts
index e9e68d2..5b31f22 100644
--- a/chrome/test/data/webui/extensions/site_permissions_list_test.ts
+++ b/chrome/test/data/webui/extensions/site_permissions_list_test.ts
@@ -5,7 +5,7 @@
 /** @fileoverview Suite of tests for site-permissions-list. */
 import 'chrome://extensions/extensions.js';
 
-import {ExtensionsSitePermissionsListElement} from 'chrome://extensions/extensions.js';
+import type {ExtensionsSitePermissionsListElement} from 'chrome://extensions/extensions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/extensions/site_permissions_site_group_test.ts b/chrome/test/data/webui/extensions/site_permissions_site_group_test.ts
index 79e4363..8c0a2d6 100644
--- a/chrome/test/data/webui/extensions/site_permissions_site_group_test.ts
+++ b/chrome/test/data/webui/extensions/site_permissions_site_group_test.ts
@@ -5,7 +5,7 @@
 /** @fileoverview Suite of tests for site-permissions-site-group. */
 import 'chrome://extensions/extensions.js';
 
-import {SitePermissionsSiteGroupElement} from 'chrome://extensions/extensions.js';
+import type {SitePermissionsSiteGroupElement} from 'chrome://extensions/extensions.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/extensions/site_permissions_test.ts b/chrome/test/data/webui/extensions/site_permissions_test.ts
index 5ea00cb..76e85c6c 100644
--- a/chrome/test/data/webui/extensions/site_permissions_test.ts
+++ b/chrome/test/data/webui/extensions/site_permissions_test.ts
@@ -5,7 +5,8 @@
 /** @fileoverview Suite of tests for extension-site-permissions. */
 import 'chrome://extensions/extensions.js';
 
-import {ExtensionsSitePermissionsElement, navigation, Page, Service} from 'chrome://extensions/extensions.js';
+import type {ExtensionsSitePermissionsElement} from 'chrome://extensions/extensions.js';
+import {navigation, Page, Service} from 'chrome://extensions/extensions.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/extensions/test_extension_hats_browser_proxy.ts b/chrome/test/data/webui/extensions/test_extension_hats_browser_proxy.ts
index 354a416..e7bcf21 100644
--- a/chrome/test/data/webui/extensions/test_extension_hats_browser_proxy.ts
+++ b/chrome/test/data/webui/extensions/test_extension_hats_browser_proxy.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ExtensionsHatsBrowserProxy} from 'chrome://extensions/extensions.js';
+import type {ExtensionsHatsBrowserProxy} from 'chrome://extensions/extensions.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export class TestExtensionsHatsBrowserProxy extends TestBrowserProxy implements
diff --git a/chrome/test/data/webui/extensions/test_kiosk_browser_proxy.ts b/chrome/test/data/webui/extensions/test_kiosk_browser_proxy.ts
index f166848e..ea6204d 100644
--- a/chrome/test/data/webui/extensions/test_kiosk_browser_proxy.ts
+++ b/chrome/test/data/webui/extensions/test_kiosk_browser_proxy.ts
@@ -2,8 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {KioskAppSettings, KioskBrowserProxy, KioskSettings} from 'chrome://extensions/extensions.js';
-
+import type {KioskAppSettings, KioskBrowserProxy, KioskSettings} from 'chrome://extensions/extensions.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export class TestKioskBrowserProxy extends TestBrowserProxy implements
diff --git a/chrome/test/data/webui/extensions/test_service.ts b/chrome/test/data/webui/extensions/test_service.ts
index 3e9aa8e..6042dd4a 100644
--- a/chrome/test/data/webui/extensions/test_service.ts
+++ b/chrome/test/data/webui/extensions/test_service.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ServiceInterface} from 'chrome://extensions/extensions.js';
+import type {ServiceInterface} from 'chrome://extensions/extensions.js';
 import {FakeChromeEvent} from 'chrome://webui-test/fake_chrome_event.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/extensions/test_util.ts b/chrome/test/data/webui/extensions/test_util.ts
index bdbffc26..8052240 100644
--- a/chrome/test/data/webui/extensions/test_util.ts
+++ b/chrome/test/data/webui/extensions/test_util.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 /** @fileoverview Common utilities for extension ui tests. */
-import {ItemDelegate} from 'chrome://extensions/extensions.js';
+import type {ItemDelegate} from 'chrome://extensions/extensions.js';
 import {assertDeepEquals, assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {FakeChromeEvent} from 'chrome://webui-test/fake_chrome_event.js';
 import {MockController, MockMethod} from 'chrome://webui-test/mock_controller.js';
diff --git a/chrome/test/data/webui/extensions/toggle_row_test.ts b/chrome/test/data/webui/extensions/toggle_row_test.ts
index e5d9cc2d..4725a73 100644
--- a/chrome/test/data/webui/extensions/toggle_row_test.ts
+++ b/chrome/test/data/webui/extensions/toggle_row_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://extensions/extensions.js';
 
-import {ExtensionsToggleRowElement} from 'chrome://extensions/extensions.js';
+import type {ExtensionsToggleRowElement} from 'chrome://extensions/extensions.js';
 import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/extensions/toolbar_test.ts b/chrome/test/data/webui/extensions/toolbar_test.ts
index 5e4ba06..4c0c873 100644
--- a/chrome/test/data/webui/extensions/toolbar_test.ts
+++ b/chrome/test/data/webui/extensions/toolbar_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ExtensionsToolbarElement, getToastManager} from 'chrome://extensions/extensions.js';
+import type {ExtensionsToolbarElement} from 'chrome://extensions/extensions.js';
+import {getToastManager} from 'chrome://extensions/extensions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 // <if expr="chromeos_ash">
diff --git a/chrome/test/data/webui/feedback/feedback_test.ts b/chrome/test/data/webui/feedback/feedback_test.ts
index b08fdd2..7ea47c6 100644
--- a/chrome/test/data/webui/feedback/feedback_test.ts
+++ b/chrome/test/data/webui/feedback/feedback_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://feedback/app.js';
 
-import {FeedbackAppElement} from 'chrome://feedback/app.js';
+import type {FeedbackAppElement} from 'chrome://feedback/app.js';
 import {FeedbackBrowserProxyImpl} from 'chrome://feedback/js/feedback_browser_proxy.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {OpenWindowProxyImpl} from 'chrome://resources/js/open_window_proxy.js';
diff --git a/chrome/test/data/webui/feedback/test_feedback_browser_proxy.ts b/chrome/test/data/webui/feedback/test_feedback_browser_proxy.ts
index fd8245f..24481ca 100644
--- a/chrome/test/data/webui/feedback/test_feedback_browser_proxy.ts
+++ b/chrome/test/data/webui/feedback/test_feedback_browser_proxy.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {FeedbackBrowserProxy} from 'chrome://feedback/js/feedback_browser_proxy.js';
+import type {FeedbackBrowserProxy} from 'chrome://feedback/js/feedback_browser_proxy.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export class TestFeedbackBrowserProxy extends TestBrowserProxy implements
diff --git a/chrome/test/data/webui/flags/app_test.ts b/chrome/test/data/webui/flags/app_test.ts
index 2cd3c6e..dbe4abe 100644
--- a/chrome/test/data/webui/flags/app_test.ts
+++ b/chrome/test/data/webui/flags/app_test.ts
@@ -4,8 +4,9 @@
 
 import 'chrome://flags/app.js';
 
-import {FlagsAppElement} from 'chrome://flags/app.js';
-import {ExperimentalFeaturesData, Feature, FlagsBrowserProxyImpl} from 'chrome://flags/flags_browser_proxy.js';
+import type {FlagsAppElement} from 'chrome://flags/app.js';
+import type {ExperimentalFeaturesData, Feature} from 'chrome://flags/flags_browser_proxy.js';
+import {FlagsBrowserProxyImpl} from 'chrome://flags/flags_browser_proxy.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
 
diff --git a/chrome/test/data/webui/flags/experiment_test.ts b/chrome/test/data/webui/flags/experiment_test.ts
index d56bc1c..e3afeba 100644
--- a/chrome/test/data/webui/flags/experiment_test.ts
+++ b/chrome/test/data/webui/flags/experiment_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://flags/experiment.js';
 
-import {FlagsExperimentElement} from 'chrome://flags/experiment.js';
+import type {FlagsExperimentElement} from 'chrome://flags/experiment.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/flags/test_flags_browser_proxy.ts b/chrome/test/data/webui/flags/test_flags_browser_proxy.ts
index c707b8aa..57c63e36 100644
--- a/chrome/test/data/webui/flags/test_flags_browser_proxy.ts
+++ b/chrome/test/data/webui/flags/test_flags_browser_proxy.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ExperimentalFeaturesData, FlagsBrowserProxy} from 'chrome://flags/flags_browser_proxy.js';
+import type {ExperimentalFeaturesData, FlagsBrowserProxy} from 'chrome://flags/flags_browser_proxy.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export class TestFlagsBrowserProxy extends TestBrowserProxy implements
diff --git a/chrome/test/data/webui/flags/url_test.ts b/chrome/test/data/webui/flags/url_test.ts
index 353e473..c09a29ed 100644
--- a/chrome/test/data/webui/flags/url_test.ts
+++ b/chrome/test/data/webui/flags/url_test.ts
@@ -14,9 +14,10 @@
 
 import 'chrome://flags/app.js';
 
-import {FlagsAppElement} from 'chrome://flags/app.js';
-import {FlagsExperimentElement} from 'chrome://flags/experiment.js';
-import {ExperimentalFeaturesData, Feature, FlagsBrowserProxyImpl} from 'chrome://flags/flags_browser_proxy.js';
+import type {FlagsAppElement} from 'chrome://flags/app.js';
+import type {FlagsExperimentElement} from 'chrome://flags/experiment.js';
+import type {ExperimentalFeaturesData, Feature} from 'chrome://flags/flags_browser_proxy.js';
+import {FlagsBrowserProxyImpl} from 'chrome://flags/flags_browser_proxy.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 import {TestFlagsBrowserProxy} from './test_flags_browser_proxy.js';
diff --git a/chrome/test/data/webui/history/history_clusters/utils.ts b/chrome/test/data/webui/history/history_clusters/utils.ts
index 8649766..9cb49b11 100644
--- a/chrome/test/data/webui/history/history_clusters/utils.ts
+++ b/chrome/test/data/webui/history/history_clusters/utils.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ClusterAction, MetricsProxy, PageCallbackRouter, PageHandlerRemote, RelatedSearchAction, VisitAction, VisitType} from 'chrome://history/history.js';
+import type {ClusterAction, MetricsProxy, RelatedSearchAction, VisitAction, VisitType} from 'chrome://history/history.js';
+import {PageCallbackRouter, PageHandlerRemote} from 'chrome://history/history.js';
 import {TestBrowserProxy as BaseTestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 import {TestMock} from 'chrome://webui-test/test_mock.js';
 
diff --git a/chrome/test/data/webui/history/history_drawer_test.ts b/chrome/test/data/webui/history/history_drawer_test.ts
index cd821e3..6af88b2 100644
--- a/chrome/test/data/webui/history/history_drawer_test.ts
+++ b/chrome/test/data/webui/history/history_drawer_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://history/history.js';
 
-import {BrowserServiceImpl, ensureLazyLoaded, HistoryAppElement, HistorySideBarElement} from 'chrome://history/history.js';
+import type {HistoryAppElement, HistorySideBarElement} from 'chrome://history/history.js';
+import {BrowserServiceImpl, ensureLazyLoaded} from 'chrome://history/history.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
diff --git a/chrome/test/data/webui/history/history_item_focus_test.ts b/chrome/test/data/webui/history/history_item_focus_test.ts
index 9ed79a0..8ef4cc5 100644
--- a/chrome/test/data/webui/history/history_item_focus_test.ts
+++ b/chrome/test/data/webui/history/history_item_focus_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://history/history.js';
 
-import {BrowserServiceImpl, HistoryItemElement} from 'chrome://history/history.js';
+import type {HistoryItemElement} from 'chrome://history/history.js';
+import {BrowserServiceImpl} from 'chrome://history/history.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/history/history_item_test.ts b/chrome/test/data/webui/history/history_item_test.ts
index bc8af698..51b4575 100644
--- a/chrome/test/data/webui/history/history_item_test.ts
+++ b/chrome/test/data/webui/history/history_item_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://history/history.js';
 
-import {BrowserServiceImpl, HistoryItemElement, HistoryListElement} from 'chrome://history/history.js';
+import type {HistoryItemElement, HistoryListElement} from 'chrome://history/history.js';
+import {BrowserServiceImpl} from 'chrome://history/history.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/history/history_list_focus_test.ts b/chrome/test/data/webui/history/history_list_focus_test.ts
index ee704b0a..27df030 100644
--- a/chrome/test/data/webui/history/history_list_focus_test.ts
+++ b/chrome/test/data/webui/history/history_list_focus_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BrowserServiceImpl, ensureLazyLoaded, HistoryAppElement, HistoryEntry, HistoryListElement} from 'chrome://history/history.js';
+import type {HistoryAppElement, HistoryEntry, HistoryListElement} from 'chrome://history/history.js';
+import {BrowserServiceImpl, ensureLazyLoaded} from 'chrome://history/history.js';
 import {isMac} from 'chrome://resources/js/platform.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
diff --git a/chrome/test/data/webui/history/history_list_test.ts b/chrome/test/data/webui/history/history_list_test.ts
index 337f8bf..588dd259 100644
--- a/chrome/test/data/webui/history/history_list_test.ts
+++ b/chrome/test/data/webui/history/history_list_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BrowserServiceImpl, CrDialogElement, ensureLazyLoaded, HistoryAppElement, HistoryEntry, HistoryItemElement, HistoryListElement, HistoryToolbarElement} from 'chrome://history/history.js';
+import type {CrDialogElement, HistoryAppElement, HistoryEntry, HistoryItemElement, HistoryListElement, HistoryToolbarElement} from 'chrome://history/history.js';
+import {BrowserServiceImpl, ensureLazyLoaded} from 'chrome://history/history.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {isMac} from 'chrome://resources/js/platform.js';
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
diff --git a/chrome/test/data/webui/history/history_metrics_test.ts b/chrome/test/data/webui/history/history_metrics_test.ts
index eeec40c..6925ebe 100644
--- a/chrome/test/data/webui/history/history_metrics_test.ts
+++ b/chrome/test/data/webui/history/history_metrics_test.ts
@@ -5,7 +5,8 @@
 import 'chrome://history/history.js';
 import 'chrome://history/lazy_load.js';
 
-import {BrowserServiceImpl, ensureLazyLoaded, HistoryAppElement, HistoryEntry, HistoryPageViewHistogram, SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram} from 'chrome://history/history.js';
+import type {HistoryAppElement, HistoryEntry} from 'chrome://history/history.js';
+import {BrowserServiceImpl, ensureLazyLoaded, HistoryPageViewHistogram, SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram} from 'chrome://history/history.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/test/data/webui/history/history_overflow_menu_test.ts b/chrome/test/data/webui/history/history_overflow_menu_test.ts
index 24b386f4..5f3d51a 100644
--- a/chrome/test/data/webui/history/history_overflow_menu_test.ts
+++ b/chrome/test/data/webui/history/history_overflow_menu_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ActionMenuModel, BrowserServiceImpl, CrActionMenuElement, ensureLazyLoaded, HistoryListElement} from 'chrome://history/history.js';
+import type {ActionMenuModel, CrActionMenuElement, HistoryListElement} from 'chrome://history/history.js';
+import {BrowserServiceImpl, ensureLazyLoaded} from 'chrome://history/history.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
diff --git a/chrome/test/data/webui/history/history_routing_test.ts b/chrome/test/data/webui/history/history_routing_test.ts
index b660806c..a117ec0 100644
--- a/chrome/test/data/webui/history/history_routing_test.ts
+++ b/chrome/test/data/webui/history/history_routing_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://history/history.js';
 
-import {BrowserProxyImpl, BrowserServiceImpl, HistoryAppElement, HistorySideBarElement, MetricsProxyImpl} from 'chrome://history/history.js';
+import type {HistoryAppElement, HistorySideBarElement} from 'chrome://history/history.js';
+import {BrowserProxyImpl, BrowserServiceImpl, MetricsProxyImpl} from 'chrome://history/history.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/history/history_routing_with_query_param_test.ts b/chrome/test/data/webui/history/history_routing_with_query_param_test.ts
index c2babdf..fb47c9d 100644
--- a/chrome/test/data/webui/history/history_routing_with_query_param_test.ts
+++ b/chrome/test/data/webui/history/history_routing_with_query_param_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://history/history.js';
 
-import {BrowserServiceImpl, HistoryAppElement} from 'chrome://history/history.js';
+import type {HistoryAppElement} from 'chrome://history/history.js';
+import {BrowserServiceImpl} from 'chrome://history/history.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
diff --git a/chrome/test/data/webui/history/history_supervised_user_test.ts b/chrome/test/data/webui/history/history_supervised_user_test.ts
index 6592ee0..69a278b4 100644
--- a/chrome/test/data/webui/history/history_supervised_user_test.ts
+++ b/chrome/test/data/webui/history/history_supervised_user_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BrowserServiceImpl, ensureLazyLoaded, HistoryAppElement, HistoryEntry, HistoryListElement, HistoryToolbarElement} from 'chrome://history/history.js';
+import type {HistoryAppElement, HistoryEntry, HistoryListElement, HistoryToolbarElement} from 'chrome://history/history.js';
+import {BrowserServiceImpl, ensureLazyLoaded} from 'chrome://history/history.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/history/history_synced_device_manager_focus_test.ts b/chrome/test/data/webui/history/history_synced_device_manager_focus_test.ts
index 10831dc..8975a8e8 100644
--- a/chrome/test/data/webui/history/history_synced_device_manager_focus_test.ts
+++ b/chrome/test/data/webui/history/history_synced_device_manager_focus_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://history/history.js';
 
-import {ensureLazyLoaded, HistorySyncedDeviceManagerElement} from 'chrome://history/history.js';
+import type {HistorySyncedDeviceManagerElement} from 'chrome://history/history.js';
+import {ensureLazyLoaded} from 'chrome://history/history.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/history/history_synced_tabs_test.ts b/chrome/test/data/webui/history/history_synced_tabs_test.ts
index acef6a7..bf44e3f 100644
--- a/chrome/test/data/webui/history/history_synced_tabs_test.ts
+++ b/chrome/test/data/webui/history/history_synced_tabs_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BrowserServiceImpl, ensureLazyLoaded, ForeignSession, HistorySyncedDeviceCardElement, HistorySyncedDeviceManagerElement} from 'chrome://history/history.js';
+import type {ForeignSession, HistorySyncedDeviceCardElement, HistorySyncedDeviceManagerElement} from 'chrome://history/history.js';
+import {BrowserServiceImpl, ensureLazyLoaded} from 'chrome://history/history.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks, waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/history/history_toolbar_focus_test.ts b/chrome/test/data/webui/history/history_toolbar_focus_test.ts
index 5961425c..8f10e61 100644
--- a/chrome/test/data/webui/history/history_toolbar_focus_test.ts
+++ b/chrome/test/data/webui/history/history_toolbar_focus_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://history/history.js';
 
-import {BrowserServiceImpl, HistoryAppElement} from 'chrome://history/history.js';
+import type {HistoryAppElement} from 'chrome://history/history.js';
+import {BrowserServiceImpl} from 'chrome://history/history.js';
 import {isMac} from 'chrome://resources/js/platform.js';
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/history/history_toolbar_test.ts b/chrome/test/data/webui/history/history_toolbar_test.ts
index f5420cc..1efd859a 100644
--- a/chrome/test/data/webui/history/history_toolbar_test.ts
+++ b/chrome/test/data/webui/history/history_toolbar_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://history/history.js';
 
-import {BrowserServiceImpl, ensureLazyLoaded, HistoryAppElement, HistoryEntry} from 'chrome://history/history.js';
+import type {HistoryAppElement, HistoryEntry} from 'chrome://history/history.js';
+import {BrowserServiceImpl, ensureLazyLoaded} from 'chrome://history/history.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
diff --git a/chrome/test/data/webui/history/searched_label_test.ts b/chrome/test/data/webui/history/searched_label_test.ts
index aefc293..4c7a3bf 100644
--- a/chrome/test/data/webui/history/searched_label_test.ts
+++ b/chrome/test/data/webui/history/searched_label_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://history/history.js';
 
-import {HistorySearchedLabelElement} from 'chrome://history/history.js';
+import type {HistorySearchedLabelElement} from 'chrome://history/history.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/history/test_browser_service.ts b/chrome/test/data/webui/history/test_browser_service.ts
index 838279d..3f1492ea 100644
--- a/chrome/test/data/webui/history/test_browser_service.ts
+++ b/chrome/test/data/webui/history/test_browser_service.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BrowserService, ForeignSession, QueryResult, RemoveVisitsRequest} from 'chrome://history/history.js';
+import type {BrowserService, ForeignSession, QueryResult, RemoveVisitsRequest} from 'chrome://history/history.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
diff --git a/chrome/test/data/webui/history/test_util.ts b/chrome/test/data/webui/history/test_util.ts
index 8ff1f29..972f38f 100644
--- a/chrome/test/data/webui/history/test_util.ts
+++ b/chrome/test/data/webui/history/test_util.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ForeignSession, ForeignSessionTab, ForeignSessionWindow, HistoryAppElement, HistoryEntry, HistoryQuery} from 'chrome://history/history.js';
+import type {ForeignSession, ForeignSessionTab, ForeignSessionWindow, HistoryAppElement, HistoryEntry, HistoryQuery} from 'chrome://history/history.js';
 import {middleOfNode} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 
 
diff --git a/chrome/test/data/webui/inline_login/inline_login_test.ts b/chrome/test/data/webui/inline_login/inline_login_test.ts
index 24e2af3..099e01fe 100644
--- a/chrome/test/data/webui/inline_login/inline_login_test.ts
+++ b/chrome/test/data/webui/inline_login/inline_login_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://chrome-signin/inline_login_app.js';
 
-import {InlineLoginAppElement} from 'chrome://chrome-signin/inline_login_app.js';
+import type {InlineLoginAppElement} from 'chrome://chrome-signin/inline_login_app.js';
 import {InlineLoginBrowserProxyImpl} from 'chrome://chrome-signin/inline_login_browser_proxy.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/test/data/webui/inline_login/inline_login_test_util.ts b/chrome/test/data/webui/inline_login/inline_login_test_util.ts
index 9a91c04..74b5ed2 100644
--- a/chrome/test/data/webui/inline_login/inline_login_test_util.ts
+++ b/chrome/test/data/webui/inline_login/inline_login_test_util.ts
@@ -2,9 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {InlineLoginBrowserProxy} from 'chrome://chrome-signin/inline_login_browser_proxy.js';
-import {AuthCompletedCredentials, AuthMode, AuthParams} from 'chrome://chrome-signin/gaia_auth_host/authenticator.js';
-
+import type {AuthCompletedCredentials, AuthMode, AuthParams} from 'chrome://chrome-signin/gaia_auth_host/authenticator.js';
+import type {InlineLoginBrowserProxy} from 'chrome://chrome-signin/inline_login_browser_proxy.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export function getFakeAccountsList(): string[] {
diff --git a/chrome/test/data/webui/intro/dice_app_test.ts b/chrome/test/data/webui/intro/dice_app_test.ts
index 4c4c0a1..c114bbb 100644
--- a/chrome/test/data/webui/intro/dice_app_test.ts
+++ b/chrome/test/data/webui/intro/dice_app_test.ts
@@ -5,7 +5,7 @@
 import 'chrome://intro/dice_app.js';
 
 import {IntroBrowserProxyImpl} from 'chrome://intro/browser_proxy.js';
-import {IntroAppElement} from 'chrome://intro/dice_app.js';
+import type {IntroAppElement} from 'chrome://intro/dice_app.js';
 import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
 
diff --git a/chrome/test/data/webui/intro/lacros_app_test.ts b/chrome/test/data/webui/intro/lacros_app_test.ts
index b2ce08f9..ff5cc5a 100644
--- a/chrome/test/data/webui/intro/lacros_app_test.ts
+++ b/chrome/test/data/webui/intro/lacros_app_test.ts
@@ -5,7 +5,7 @@
 import 'chrome://intro/lacros_app.js';
 
 import {IntroBrowserProxyImpl} from 'chrome://intro/browser_proxy.js';
-import {LacrosIntroAppElement} from 'chrome://intro/lacros_app.js';
+import type {LacrosIntroAppElement} from 'chrome://intro/lacros_app.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/intro/sign_in_promo_test.ts b/chrome/test/data/webui/intro/sign_in_promo_test.ts
index 3bccf88..6bd75f7 100644
--- a/chrome/test/data/webui/intro/sign_in_promo_test.ts
+++ b/chrome/test/data/webui/intro/sign_in_promo_test.ts
@@ -5,7 +5,7 @@
 import 'chrome://intro/sign_in_promo.js';
 
 import {IntroBrowserProxyImpl} from 'chrome://intro/browser_proxy.js';
-import {SignInPromoElement} from 'chrome://intro/sign_in_promo.js';
+import type {SignInPromoElement} from 'chrome://intro/sign_in_promo.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/intro/test_intro_browser_proxy.ts b/chrome/test/data/webui/intro/test_intro_browser_proxy.ts
index db84b822..e211d0a 100644
--- a/chrome/test/data/webui/intro/test_intro_browser_proxy.ts
+++ b/chrome/test/data/webui/intro/test_intro_browser_proxy.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {IntroBrowserProxy} from 'chrome://intro/browser_proxy.js';
+import type {IntroBrowserProxy} from 'chrome://intro/browser_proxy.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export class TestIntroBrowserProxy extends TestBrowserProxy implements
diff --git a/chrome/test/data/webui/js/cr_test.ts b/chrome/test/data/webui/js/cr_test.ts
index 21b5477..91dcd61 100644
--- a/chrome/test/data/webui/js/cr_test.ts
+++ b/chrome/test/data/webui/js/cr_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {addWebUiListener, removeWebUiListener, sendWithPromise, WebUiListener, webUIListenerCallback, webUIResponse} from 'chrome://resources/js/cr.js';
+import type {WebUiListener} from 'chrome://resources/js/cr.js';
+import {addWebUiListener, removeWebUiListener, sendWithPromise, webUIListenerCallback, webUIResponse} from 'chrome://resources/js/cr.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {assertEquals, assertFalse, assertNotReached, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/js/metrics_reporter/metrics_reporter_test.ts b/chrome/test/data/webui/js/metrics_reporter/metrics_reporter_test.ts
index 47b21b89..368356a3 100644
--- a/chrome/test/data/webui/js/metrics_reporter/metrics_reporter_test.ts
+++ b/chrome/test/data/webui/js/metrics_reporter/metrics_reporter_test.ts
@@ -4,7 +4,8 @@
 
 import {PageMetricsCallbackRouter} from 'chrome://resources/js/metrics_reporter.mojom-webui.js';
 import {BrowserProxyImpl} from 'chrome://resources/js/metrics_reporter/browser_proxy.js';
-import {MetricsReporter, MetricsReporterImpl} from 'chrome://resources/js/metrics_reporter/metrics_reporter.js';
+import type {MetricsReporter} from 'chrome://resources/js/metrics_reporter/metrics_reporter.js';
+import {MetricsReporterImpl} from 'chrome://resources/js/metrics_reporter/metrics_reporter.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {TestMock} from 'chrome://webui-test/test_mock.js';
 
diff --git a/chrome/test/data/webui/js/store_test.ts b/chrome/test/data/webui/js/store_test.ts
index 16f1c8cc..3f615f5 100644
--- a/chrome/test/data/webui/js/store_test.ts
+++ b/chrome/test/data/webui/js/store_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Action, Store, StoreObserver} from 'chrome://resources/js/store.js';
+import type {Action, StoreObserver} from 'chrome://resources/js/store.js';
+import {Store} from 'chrome://resources/js/store.js';
 import {assertDeepEquals, assertEquals, assertFalse} from 'chrome://webui-test/chai_assert.js';
 
 interface TestState {
diff --git a/chrome/test/data/webui/location_internals/location_internals_test.ts b/chrome/test/data/webui/location_internals/location_internals_test.ts
index e8c61fe..e05fb60 100644
--- a/chrome/test/data/webui/location_internals/location_internals_test.ts
+++ b/chrome/test/data/webui/location_internals/location_internals_test.ts
@@ -3,13 +3,15 @@
 // found in the LICENSE file.
 
 import {LAST_NETWORK_REQUEST_TABLE_ID, LAST_NETWORK_RESPONSE_TABLE_ID, POSITION_CACHE_TABLE_ID, WIFI_DATA_TABLE_ID, WIFI_POLLING_POLICY_TABLE_ID} from 'chrome://location-internals/diagnose_info_view.js';
-import {AccessPointData, GeolocationDiagnostics, GeolocationInternalsInterface, GeolocationInternalsObserverRemote, GeolocationInternalsPendingReceiver, GeolocationInternalsReceiver, INVALID_CHANNEL, INVALID_RADIO_SIGNAL_STRENGTH, INVALID_SIGNAL_TO_NOISE, NetworkLocationResponse} from 'chrome://location-internals/geolocation_internals.mojom-webui.js';
+import type {AccessPointData, GeolocationDiagnostics, GeolocationInternalsInterface, GeolocationInternalsObserverRemote, GeolocationInternalsPendingReceiver, NetworkLocationResponse} from 'chrome://location-internals/geolocation_internals.mojom-webui.js';
+import {GeolocationInternalsReceiver, INVALID_CHANNEL, INVALID_RADIO_SIGNAL_STRENGTH, INVALID_SIGNAL_TO_NOISE} from 'chrome://location-internals/geolocation_internals.mojom-webui.js';
 import {BAD_ACCURACY, BAD_ALTITUDE, BAD_HEADING, BAD_LATITUDE_LONGITUDE, BAD_SPEED} from 'chrome://location-internals/geoposition.mojom-webui.js';
 import {DIAGNOSE_INFO_VIEW_ID, initializeMojo, REFRESH_FINISH_EVENT, REFRESH_STATUS_ID, REFRESH_STATUS_SUCCESS, REFRESH_STATUS_UNINITIALIZED, WATCH_BUTTON_ID} from 'chrome://location-internals/location_internals.js';
-import {LocationInternalsHandler, LocationInternalsHandlerInterface, LocationInternalsHandlerReceiver} from 'chrome://location-internals/location_internals.mojom-webui.js';
+import type {LocationInternalsHandlerInterface} from 'chrome://location-internals/location_internals.mojom-webui.js';
+import {LocationInternalsHandler, LocationInternalsHandlerReceiver} from 'chrome://location-internals/location_internals.mojom-webui.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {getRequiredElement} from 'chrome://resources/js/util.js';
-import {Time, TimeDelta} from 'chrome://resources/mojo/mojo/public/mojom/base/time.mojom-webui.js';
+import type {Time, TimeDelta} from 'chrome://resources/mojo/mojo/public/mojom/base/time.mojom-webui.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
diff --git a/chrome/test/data/webui/media_router/cast_feedback_ui_test.ts b/chrome/test/data/webui/media_router/cast_feedback_ui_test.ts
index 0ac84a2..686d596b 100644
--- a/chrome/test/data/webui/media_router/cast_feedback_ui_test.ts
+++ b/chrome/test/data/webui/media_router/cast_feedback_ui_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {FeedbackEvent, FeedbackUiBrowserProxy, FeedbackUiBrowserProxyImpl, FeedbackUiElement} from 'chrome://cast-feedback/cast_feedback_ui.js';
+import type {FeedbackUiBrowserProxy, FeedbackUiElement} from 'chrome://cast-feedback/cast_feedback_ui.js';
+import {FeedbackEvent, FeedbackUiBrowserProxyImpl} from 'chrome://cast-feedback/cast_feedback_ui.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
diff --git a/chrome/test/data/webui/metrics_internals/no_logs_test.ts b/chrome/test/data/webui/metrics_internals/no_logs_test.ts
index 23df16eb..2bfcf4d8 100644
--- a/chrome/test/data/webui/metrics_internals/no_logs_test.ts
+++ b/chrome/test/data/webui/metrics_internals/no_logs_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://metrics-internals/app.js';
 
-import {MetricsInternalsAppElement} from 'chrome://metrics-internals/app.js';
+import type {MetricsInternalsAppElement} from 'chrome://metrics-internals/app.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {assertEquals, assertGT, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/metrics_internals/with_log_test.ts b/chrome/test/data/webui/metrics_internals/with_log_test.ts
index c0df04f..f974ebf 100644
--- a/chrome/test/data/webui/metrics_internals/with_log_test.ts
+++ b/chrome/test/data/webui/metrics_internals/with_log_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://metrics-internals/app.js';
 
-import {MetricsInternalsAppElement} from 'chrome://metrics-internals/app.js';
+import type {MetricsInternalsAppElement} from 'chrome://metrics-internals/app.js';
 import {getEventsPeekString, sizeToString, timestampToString} from 'chrome://metrics-internals/log_utils.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {assertEquals, assertGT, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/mocked_metrics_reporter.ts b/chrome/test/data/webui/mocked_metrics_reporter.ts
index 10d06d4..7c7ebec2 100644
--- a/chrome/test/data/webui/mocked_metrics_reporter.ts
+++ b/chrome/test/data/webui/mocked_metrics_reporter.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {MetricsReporter} from 'chrome://resources/js/metrics_reporter/metrics_reporter.js';
+import type {MetricsReporter} from 'chrome://resources/js/metrics_reporter/metrics_reporter.js';
 
 export class MockedMetricsReporter implements MetricsReporter {
   mark(_name: string): void {}
diff --git a/chrome/test/data/webui/new_tab_page/app_test.ts b/chrome/test/data/webui/new_tab_page/app_test.ts
index 84e7df48..001544d 100644
--- a/chrome/test/data/webui/new_tab_page/app_test.ts
+++ b/chrome/test/data/webui/new_tab_page/app_test.ts
@@ -2,18 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {counterfactualLoad, Module, ModuleDescriptor, ModuleRegistry} from 'chrome://new-tab-page/lazy_load.js';
-import {$$, AppElement, BackgroundManager, BrowserCommandProxy, CUSTOMIZE_CHROME_BUTTON_ELEMENT_ID, CustomizeDialogPage, NewTabPageProxy, NtpCustomizeChromeEntryPoint, NtpElement, VoiceAction, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
-import {CustomizeChromeSection, NtpBackgroundImageSource, PageCallbackRouter, PageHandlerRemote, PageRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
-import {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
+import type {Module} from 'chrome://new-tab-page/lazy_load.js';
+import {counterfactualLoad, ModuleDescriptor, ModuleRegistry} from 'chrome://new-tab-page/lazy_load.js';
+import type {AppElement} from 'chrome://new-tab-page/new_tab_page.js';
+import {$$, BackgroundManager, BrowserCommandProxy, CUSTOMIZE_CHROME_BUTTON_ELEMENT_ID, CustomizeDialogPage, NewTabPageProxy, NtpCustomizeChromeEntryPoint, NtpElement, VoiceAction, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
+import type {PageRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import {CustomizeChromeSection, NtpBackgroundImageSource, PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import type {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
 import {Command, CommandHandlerRemote} from 'chrome://resources/js/browser_command.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {isMac} from 'chrome://resources/js/platform.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {assertNotStyle, assertStyle, createBackgroundImage, createTheme, installMock} from './test_support.js';
diff --git a/chrome/test/data/webui/new_tab_page/customize_backgrounds_test.ts b/chrome/test/data/webui/new_tab_page/customize_backgrounds_test.ts
index 6dae906..fd32ec5 100644
--- a/chrome/test/data/webui/new_tab_page/customize_backgrounds_test.ts
+++ b/chrome/test/data/webui/new_tab_page/customize_backgrounds_test.ts
@@ -4,12 +4,13 @@
 
 import 'chrome://new-tab-page/lazy_load.js';
 
-import {CustomizeBackgroundsElement} from 'chrome://new-tab-page/lazy_load.js';
+import type {CustomizeBackgroundsElement} from 'chrome://new-tab-page/lazy_load.js';
 import {NewTabPageProxy, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
-import {BackgroundCollection, CollectionImage, PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import type {BackgroundCollection, CollectionImage} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import {PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
 
 import {assertNotStyle, assertStyle, createBackgroundImage, createTheme, installMock} from './test_support.js';
diff --git a/chrome/test/data/webui/new_tab_page/customize_dialog_focus_test.ts b/chrome/test/data/webui/new_tab_page/customize_dialog_focus_test.ts
index ee0589f9..b1e8ac0d 100644
--- a/chrome/test/data/webui/new_tab_page/customize_dialog_focus_test.ts
+++ b/chrome/test/data/webui/new_tab_page/customize_dialog_focus_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://new-tab-page/lazy_load.js';
 
-import {CustomizeDialogElement} from 'chrome://new-tab-page/lazy_load.js';
+import type {CustomizeDialogElement} from 'chrome://new-tab-page/lazy_load.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
diff --git a/chrome/test/data/webui/new_tab_page/customize_dialog_test.ts b/chrome/test/data/webui/new_tab_page/customize_dialog_test.ts
index 7cff296..939e870 100644
--- a/chrome/test/data/webui/new_tab_page/customize_dialog_test.ts
+++ b/chrome/test/data/webui/new_tab_page/customize_dialog_test.ts
@@ -4,12 +4,12 @@
 
 import 'chrome://new-tab-page/lazy_load.js';
 
-import {CustomizeDialogElement} from 'chrome://new-tab-page/lazy_load.js';
+import type {CustomizeDialogElement} from 'chrome://new-tab-page/lazy_load.js';
 import {CustomizeDialogPage, NewTabPageProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 
 import {createBackgroundImage, createTheme, installMock} from './test_support.js';
 
diff --git a/chrome/test/data/webui/new_tab_page/customize_modules_test.ts b/chrome/test/data/webui/new_tab_page/customize_modules_test.ts
index 59b917d..759a315 100644
--- a/chrome/test/data/webui/new_tab_page/customize_modules_test.ts
+++ b/chrome/test/data/webui/new_tab_page/customize_modules_test.ts
@@ -5,13 +5,16 @@
 import 'chrome://new-tab-page/lazy_load.js';
 
 import {CartHandlerRemote} from 'chrome://new-tab-page/chrome_cart.mojom-webui.js';
-import {ChromeCartProxy, CustomizeModulesElement} from 'chrome://new-tab-page/lazy_load.js';
+import type {CustomizeModulesElement} from 'chrome://new-tab-page/lazy_load.js';
+import {ChromeCartProxy} from 'chrome://new-tab-page/lazy_load.js';
 import {$$, NewTabPageProxy} from 'chrome://new-tab-page/new_tab_page.js';
-import {ModuleIdName, PageCallbackRouter, PageHandlerRemote, PageRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import type {ModuleIdName, PageRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import {PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
 
 import {assertNotStyle, assertStyle, installMock} from './test_support.js';
diff --git a/chrome/test/data/webui/new_tab_page/customize_shortcuts_test.ts b/chrome/test/data/webui/new_tab_page/customize_shortcuts_test.ts
index 3e22f6d..1f7655b 100644
--- a/chrome/test/data/webui/new_tab_page/customize_shortcuts_test.ts
+++ b/chrome/test/data/webui/new_tab_page/customize_shortcuts_test.ts
@@ -4,11 +4,11 @@
 
 import 'chrome://new-tab-page/lazy_load.js';
 
-import {CustomizeShortcutsElement} from 'chrome://new-tab-page/lazy_load.js';
+import type {CustomizeShortcutsElement} from 'chrome://new-tab-page/lazy_load.js';
 import {NewTabPageProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 
 import {installMock} from './test_support.js';
 
diff --git a/chrome/test/data/webui/new_tab_page/doodle_share_dialog_focus_test.ts b/chrome/test/data/webui/new_tab_page/doodle_share_dialog_focus_test.ts
index e57d53dc..032e77b 100644
--- a/chrome/test/data/webui/new_tab_page/doodle_share_dialog_focus_test.ts
+++ b/chrome/test/data/webui/new_tab_page/doodle_share_dialog_focus_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://new-tab-page/new_tab_page.js';
 
-import {DoodleShareDialogElement} from 'chrome://new-tab-page/new_tab_page.js';
+import type {DoodleShareDialogElement} from 'chrome://new-tab-page/new_tab_page.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 
 suite('NewTabPageDoodleShareDialogFocusTest', () => {
diff --git a/chrome/test/data/webui/new_tab_page/doodle_share_dialog_test.ts b/chrome/test/data/webui/new_tab_page/doodle_share_dialog_test.ts
index 5edd897..1c0d6fe 100644
--- a/chrome/test/data/webui/new_tab_page/doodle_share_dialog_test.ts
+++ b/chrome/test/data/webui/new_tab_page/doodle_share_dialog_test.ts
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {DoodleShareDialogElement, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
+import type {DoodleShareDialogElement} from 'chrome://new-tab-page/new_tab_page.js';
+import {WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 
 import {installMock} from './test_support.js';
 
diff --git a/chrome/test/data/webui/new_tab_page/lens_form_test.ts b/chrome/test/data/webui/new_tab_page/lens_form_test.ts
index 8b6c77d..ae7b84c 100644
--- a/chrome/test/data/webui/new_tab_page/lens_form_test.ts
+++ b/chrome/test/data/webui/new_tab_page/lens_form_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://new-tab-page/new_tab_page.js';
 
-import {LensErrorType, LensFormElement, LensSubmitType} from 'chrome://new-tab-page/lazy_load.js';
+import type {LensFormElement} from 'chrome://new-tab-page/lazy_load.js';
+import {LensErrorType, LensSubmitType} from 'chrome://new-tab-page/lazy_load.js';
 import {assertEquals, assertFalse, assertGT, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 suite('LensFormTest', () => {
diff --git a/chrome/test/data/webui/new_tab_page/lens_upload_dialog_test.ts b/chrome/test/data/webui/new_tab_page/lens_upload_dialog_test.ts
index 9de2fec..5d99ff7 100644
--- a/chrome/test/data/webui/new_tab_page/lens_upload_dialog_test.ts
+++ b/chrome/test/data/webui/new_tab_page/lens_upload_dialog_test.ts
@@ -4,12 +4,14 @@
 
 import 'chrome://new-tab-page/new_tab_page.js';
 
-import {LensErrorType, LensSubmitType, LensUploadDialogAction, LensUploadDialogElement, LensUploadDialogError} from 'chrome://new-tab-page/lazy_load.js';
+import type {LensUploadDialogElement} from 'chrome://new-tab-page/lazy_load.js';
+import {LensErrorType, LensSubmitType, LensUploadDialogAction, LensUploadDialogError} from 'chrome://new-tab-page/lazy_load.js';
 import {WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 
 import {installMock} from './test_support.js';
 
diff --git a/chrome/test/data/webui/new_tab_page/logo_test.ts b/chrome/test/data/webui/new_tab_page/logo_test.ts
index d67e49b9..1901c36 100644
--- a/chrome/test/data/webui/new_tab_page/logo_test.ts
+++ b/chrome/test/data/webui/new_tab_page/logo_test.ts
@@ -2,14 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {$$, IframeElement, LogoElement, NewTabPageProxy, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
-import {Doodle, DoodleImageType, DoodleShareChannel, PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import type {IframeElement, LogoElement} from 'chrome://new-tab-page/new_tab_page.js';
+import {$$, NewTabPageProxy, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
+import type {Doodle} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import {DoodleImageType, DoodleShareChannel, PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {hexColorToSkColor, skColorToRgba} from 'chrome://resources/js/color_utils.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertGE, assertLE, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {assertNotStyle, assertStyle, installMock, keydown} from './test_support.js';
diff --git a/chrome/test/data/webui/new_tab_page/metrics_utils_test.ts b/chrome/test/data/webui/new_tab_page/metrics_utils_test.ts
index 9a322631..c7b4f5f 100644
--- a/chrome/test/data/webui/new_tab_page/metrics_utils_test.ts
+++ b/chrome/test/data/webui/new_tab_page/metrics_utils_test.ts
@@ -5,7 +5,8 @@
 import {recordDuration, recordLoadDuration, recordOccurence, recordPerdecage} from 'chrome://new-tab-page/new_tab_page.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 
 suite('NewTabPageMetricsUtilsTest', () => {
   let metrics: MetricsTracker;
diff --git a/chrome/test/data/webui/new_tab_page/middle_slot_promo_test.ts b/chrome/test/data/webui/new_tab_page/middle_slot_promo_test.ts
index 6f858ab8..8bf565a 100644
--- a/chrome/test/data/webui/new_tab_page/middle_slot_promo_test.ts
+++ b/chrome/test/data/webui/new_tab_page/middle_slot_promo_test.ts
@@ -4,15 +4,19 @@
 
 import 'chrome://new-tab-page/lazy_load.js';
 
-import {MiddleSlotPromoElement, PromoDismissAction} from 'chrome://new-tab-page/lazy_load.js';
-import {$$, BrowserCommandProxy, CrAutoImgElement, NewTabPageProxy} from 'chrome://new-tab-page/new_tab_page.js';
-import {PageCallbackRouter, PageHandlerRemote, PageRemote, Promo} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import type {MiddleSlotPromoElement} from 'chrome://new-tab-page/lazy_load.js';
+import {PromoDismissAction} from 'chrome://new-tab-page/lazy_load.js';
+import type {CrAutoImgElement} from 'chrome://new-tab-page/new_tab_page.js';
+import {$$, BrowserCommandProxy, NewTabPageProxy} from 'chrome://new-tab-page/new_tab_page.js';
+import type {PageRemote, Promo} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import {PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {Command, CommandHandlerRemote} from 'chrome://resources/js/browser_command.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertDeepEquals, assertEquals} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {installMock} from './test_support.js';
diff --git a/chrome/test/data/webui/new_tab_page/modules/cart/discount_consent_card_test.ts b/chrome/test/data/webui/new_tab_page/modules/cart/discount_consent_card_test.ts
index 689ff951..739481a 100644
--- a/chrome/test/data/webui/new_tab_page/modules/cart/discount_consent_card_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/cart/discount_consent_card_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://new-tab-page/lazy_load.js';
 
-import {DiscountConsentCard} from 'chrome://new-tab-page/lazy_load.js';
+import type {DiscountConsentCard} from 'chrome://new-tab-page/lazy_load.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
diff --git a/chrome/test/data/webui/new_tab_page/modules/cart/discount_consent_card_test_utils.ts b/chrome/test/data/webui/new_tab_page/modules/cart/discount_consent_card_test_utils.ts
index fcfff6fe..6dca5dd 100644
--- a/chrome/test/data/webui/new_tab_page/modules/cart/discount_consent_card_test_utils.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/cart/discount_consent_card_test_utils.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {DiscountConsentCard} from 'chrome://new-tab-page/lazy_load.js';
+import type {DiscountConsentCard} from 'chrome://new-tab-page/lazy_load.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 
 export function clickAcceptButton(discountConsentCard: DiscountConsentCard) {
diff --git a/chrome/test/data/webui/new_tab_page/modules/cart/discount_consent_dialog_test.ts b/chrome/test/data/webui/new_tab_page/modules/cart/discount_consent_dialog_test.ts
index d5c2535..a961fbb0 100644
--- a/chrome/test/data/webui/new_tab_page/modules/cart/discount_consent_dialog_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/cart/discount_consent_dialog_test.ts
@@ -4,8 +4,8 @@
 
 import 'chrome://new-tab-page/lazy_load.js';
 
-import {DiscountConsentDialog} from 'chrome://new-tab-page/lazy_load.js';
-import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import type {DiscountConsentDialog} from 'chrome://new-tab-page/lazy_load.js';
+import type {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/new_tab_page/modules/cart/module_test.ts b/chrome/test/data/webui/new_tab_page/modules/cart/module_test.ts
index 9f66e93..493e104 100644
--- a/chrome/test/data/webui/new_tab_page/modules/cart/module_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/cart/module_test.ts
@@ -3,13 +3,16 @@
 // found in the LICENSE file.
 
 import {CartHandlerRemote, ConsentStatus} from 'chrome://new-tab-page/chrome_cart.mojom-webui.js';
-import {chromeCartDescriptor, ChromeCartModuleElement, ChromeCartProxy, DiscountConsentCard, DiscountConsentVariation} from 'chrome://new-tab-page/lazy_load.js';
-import {$$, CrAutoImgElement} from 'chrome://new-tab-page/new_tab_page.js';
+import type {ChromeCartModuleElement, DiscountConsentCard} from 'chrome://new-tab-page/lazy_load.js';
+import {chromeCartDescriptor, ChromeCartProxy, DiscountConsentVariation} from 'chrome://new-tab-page/lazy_load.js';
+import type {CrAutoImgElement} from 'chrome://new-tab-page/new_tab_page.js';
+import {$$} from 'chrome://new-tab-page/new_tab_page.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
 
 import {assertNotStyle, installMock} from '../../test_support.js';
diff --git a/chrome/test/data/webui/new_tab_page/modules/drive/module_test.ts b/chrome/test/data/webui/new_tab_page/modules/drive/module_test.ts
index 603eede..674bd2a 100644
--- a/chrome/test/data/webui/new_tab_page/modules/drive/module_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/drive/module_test.ts
@@ -3,10 +3,12 @@
 // found in the LICENSE file.
 
 import {DriveHandlerRemote} from 'chrome://new-tab-page/drive.mojom-webui.js';
-import {DismissModuleEvent, driveDescriptor, DriveModuleElement, DriveProxy} from 'chrome://new-tab-page/lazy_load.js';
-import {$$, CrAutoImgElement} from 'chrome://new-tab-page/new_tab_page.js';
+import type {DismissModuleEvent, DriveModuleElement} from 'chrome://new-tab-page/lazy_load.js';
+import {driveDescriptor, DriveProxy} from 'chrome://new-tab-page/lazy_load.js';
+import type {CrAutoImgElement} from 'chrome://new-tab-page/new_tab_page.js';
+import {$$} from 'chrome://new-tab-page/new_tab_page.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
 
 import {installMock} from '../../test_support.js';
diff --git a/chrome/test/data/webui/new_tab_page/modules/feed/module_test.ts b/chrome/test/data/webui/new_tab_page/modules/feed/module_test.ts
index 68f1204..d2a0a4e7 100644
--- a/chrome/test/data/webui/new_tab_page/modules/feed/module_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/feed/module_test.ts
@@ -3,10 +3,11 @@
 // found in the LICENSE file.
 
 import {FeedHandlerRemote} from 'chrome://new-tab-page/feed.mojom-webui.js';
-import {feedDescriptor, FeedModuleElement, FeedProxy} from 'chrome://new-tab-page/lazy_load.js';
-import {CrAutoImgElement} from 'chrome://new-tab-page/new_tab_page.js';
+import type {FeedModuleElement} from 'chrome://new-tab-page/lazy_load.js';
+import {feedDescriptor, FeedProxy} from 'chrome://new-tab-page/lazy_load.js';
+import type {CrAutoImgElement} from 'chrome://new-tab-page/new_tab_page.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
 
 import {installMock} from '../../test_support.js';
diff --git a/chrome/test/data/webui/new_tab_page/modules/history_clusters/cart/cart_tile_test.ts b/chrome/test/data/webui/new_tab_page/modules/history_clusters/cart/cart_tile_test.ts
index 50ff8dee..901f6c9 100644
--- a/chrome/test/data/webui/new_tab_page/modules/history_clusters/cart/cart_tile_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/history_clusters/cart/cart_tile_test.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Cart} from 'chrome://new-tab-page/cart.mojom-webui.js';
+import type {Cart} from 'chrome://new-tab-page/cart.mojom-webui.js';
 import {CartTileModuleElement} from 'chrome://new-tab-page/lazy_load.js';
 import {$$} from 'chrome://new-tab-page/new_tab_page.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
diff --git a/chrome/test/data/webui/new_tab_page/modules/history_clusters/module_test.ts b/chrome/test/data/webui/new_tab_page/modules/history_clusters/module_test.ts
index 36ebfa2..4832cfe 100644
--- a/chrome/test/data/webui/new_tab_page/modules/history_clusters/module_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/history_clusters/module_test.ts
@@ -2,21 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Cart} from 'chrome://new-tab-page/cart.mojom-webui.js';
-import {Discount} from 'chrome://new-tab-page/discount.mojom-webui.js';
-import {Cluster, URLVisit} from 'chrome://new-tab-page/history_cluster_types.mojom-webui.js';
+import type {Cart} from 'chrome://new-tab-page/cart.mojom-webui.js';
+import type {Discount} from 'chrome://new-tab-page/discount.mojom-webui.js';
+import type {Cluster, URLVisit} from 'chrome://new-tab-page/history_cluster_types.mojom-webui.js';
 import {PageHandlerRemote} from 'chrome://new-tab-page/history_clusters.mojom-webui.js';
 import {LayoutType} from 'chrome://new-tab-page/history_clusters_layout_type.mojom-webui.js';
-import {DismissModuleEvent, HistoryClusterElementType, HistoryClusterImageDisplayState, historyClustersDescriptor, HistoryClustersModuleElement, HistoryClustersProxyImpl, LAYOUT_1_MIN_IMAGE_VISITS, LAYOUT_1_MIN_VISITS, LAYOUT_2_MIN_IMAGE_VISITS, LAYOUT_2_MIN_VISITS, LAYOUT_3_MIN_IMAGE_VISITS, LAYOUT_3_MIN_VISITS, PageImageServiceBrowserProxy, TileModuleElement} from 'chrome://new-tab-page/lazy_load.js';
+import type {DismissModuleEvent, HistoryClustersModuleElement, TileModuleElement} from 'chrome://new-tab-page/lazy_load.js';
+import {HistoryClusterElementType, HistoryClusterImageDisplayState, historyClustersDescriptor, HistoryClustersProxyImpl, LAYOUT_1_MIN_IMAGE_VISITS, LAYOUT_1_MIN_VISITS, LAYOUT_2_MIN_IMAGE_VISITS, LAYOUT_2_MIN_VISITS, LAYOUT_3_MIN_IMAGE_VISITS, LAYOUT_3_MIN_VISITS, PageImageServiceBrowserProxy} from 'chrome://new-tab-page/lazy_load.js';
 import {$$, NewTabPageProxy} from 'chrome://new-tab-page/new_tab_page.js';
-import {PageRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import type {PageRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {PageImageServiceHandlerRemote} from 'chrome://resources/cr_components/page_image_service/page_image_service.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
+import type {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {installMock} from '../../test_support.js';
diff --git a/chrome/test/data/webui/new_tab_page/modules/history_clusters/test_support.ts b/chrome/test/data/webui/new_tab_page/modules/history_clusters/test_support.ts
index 0d1efaa..49d6e63 100644
--- a/chrome/test/data/webui/new_tab_page/modules/history_clusters/test_support.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/history_clusters/test_support.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {SearchQuery, URLVisit} from 'chrome://new-tab-page/history_cluster_types.mojom-webui.js';
+import type {SearchQuery, URLVisit} from 'chrome://new-tab-page/history_cluster_types.mojom-webui.js';
 import {LAYOUT_1_MIN_IMAGE_VISITS, LAYOUT_1_MIN_VISITS} from 'chrome://new-tab-page/lazy_load.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/new_tab_page/modules/history_clusters/tile_test.ts b/chrome/test/data/webui/new_tab_page/modules/history_clusters/tile_test.ts
index 6d227b5..90f3bc7 100644
--- a/chrome/test/data/webui/new_tab_page/modules/history_clusters/tile_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/history_clusters/tile_test.ts
@@ -2,14 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Annotation, URLVisit} from 'chrome://new-tab-page/history_cluster_types.mojom-webui.js';
+import type {URLVisit} from 'chrome://new-tab-page/history_cluster_types.mojom-webui.js';
+import {Annotation} from 'chrome://new-tab-page/history_cluster_types.mojom-webui.js';
 import {PageImageServiceBrowserProxy, TileModuleElement} from 'chrome://new-tab-page/lazy_load.js';
 import {$$} from 'chrome://new-tab-page/new_tab_page.js';
 import {ClientId as PageImageServiceClientId, PageImageServiceHandlerRemote} from 'chrome://resources/cr_components/page_image_service/page_image_service.mojom-webui.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 
 import {assertStyle, installMock} from '../../test_support.js';
 
diff --git a/chrome/test/data/webui/new_tab_page/modules/module_descriptor_test.ts b/chrome/test/data/webui/new_tab_page/modules/module_descriptor_test.ts
index 7c0ff7c..71c5ca35 100644
--- a/chrome/test/data/webui/new_tab_page/modules/module_descriptor_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/module_descriptor_test.ts
@@ -6,8 +6,9 @@
 import {WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 
 import {createElement, initNullModule, installMock} from '../test_support.js';
 
diff --git a/chrome/test/data/webui/new_tab_page/modules/module_registry_test.ts b/chrome/test/data/webui/new_tab_page/modules/module_registry_test.ts
index 790e8a6..9dcb910 100644
--- a/chrome/test/data/webui/new_tab_page/modules/module_registry_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/module_registry_test.ts
@@ -4,13 +4,15 @@
 
 import {ModuleDescriptor, ModuleRegistry} from 'chrome://new-tab-page/lazy_load.js';
 import {NewTabPageProxy, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
-import {ModuleIdName, PageCallbackRouter, PageHandlerRemote, PageRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import type {ModuleIdName, PageRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import {PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {assertDeepEquals, assertEquals} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 
 import {createElement, initNullModule, installMock} from '../test_support.js';
 
diff --git a/chrome/test/data/webui/new_tab_page/modules/module_wrapper_test.ts b/chrome/test/data/webui/new_tab_page/modules/module_wrapper_test.ts
index 44aab047..f40a4ef 100644
--- a/chrome/test/data/webui/new_tab_page/modules/module_wrapper_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/module_wrapper_test.ts
@@ -7,8 +7,9 @@
 import {PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertDeepEquals, assertEquals, assertThrows} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {createElement, initNullModule, installMock} from '../test_support.js';
diff --git a/chrome/test/data/webui/new_tab_page/modules/modules_test.ts b/chrome/test/data/webui/new_tab_page/modules/modules_test.ts
index c7bd3d9..5129119 100644
--- a/chrome/test/data/webui/new_tab_page/modules/modules_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/modules_test.ts
@@ -2,14 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Module, ModuleDescriptor, ModuleRegistry, ModulesElement} from 'chrome://new-tab-page/lazy_load.js';
+import type {Module} from 'chrome://new-tab-page/lazy_load.js';
+import {ModuleDescriptor, ModuleRegistry, ModulesElement} from 'chrome://new-tab-page/lazy_load.js';
 import {$$, NewTabPageProxy} from 'chrome://new-tab-page/new_tab_page.js';
-import {PageCallbackRouter, PageHandlerRemote, PageRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import type {PageRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import {PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 
 import {assertNotStyle, assertStyle, capture, createElement, initNullModule, installMock, render} from '../test_support.js';
 
diff --git a/chrome/test/data/webui/new_tab_page/modules/photos/module_test.ts b/chrome/test/data/webui/new_tab_page/modules/photos/module_test.ts
index 8bda24d9..846887a 100644
--- a/chrome/test/data/webui/new_tab_page/modules/photos/module_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/photos/module_test.ts
@@ -2,13 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {DisableModuleEvent, DismissModuleEvent, photosDescriptor, PhotosModuleElement, PhotosProxy} from 'chrome://new-tab-page/lazy_load.js';
-import {$$, DomIf} from 'chrome://new-tab-page/new_tab_page.js';
+import type {DisableModuleEvent, DismissModuleEvent, PhotosModuleElement} from 'chrome://new-tab-page/lazy_load.js';
+import {photosDescriptor, PhotosProxy} from 'chrome://new-tab-page/lazy_load.js';
+import type {DomIf} from 'chrome://new-tab-page/new_tab_page.js';
+import {$$} from 'chrome://new-tab-page/new_tab_page.js';
 import {PhotosHandlerRemote} from 'chrome://new-tab-page/photos.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
 
 import {installMock} from '../../test_support.js';
diff --git a/chrome/test/data/webui/new_tab_page/modules/recipes/module_test.ts b/chrome/test/data/webui/new_tab_page/modules/recipes/module_test.ts
index bec2cc8..96879b4 100644
--- a/chrome/test/data/webui/new_tab_page/modules/recipes/module_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/recipes/module_test.ts
@@ -2,13 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {DismissModuleEvent, RecipesHandlerProxy, RecipesModuleElement, recipeTasksDescriptor} from 'chrome://new-tab-page/lazy_load.js';
-import {$$, CrAutoImgElement} from 'chrome://new-tab-page/new_tab_page.js';
+import type {DismissModuleEvent, RecipesModuleElement} from 'chrome://new-tab-page/lazy_load.js';
+import {RecipesHandlerProxy, recipeTasksDescriptor} from 'chrome://new-tab-page/lazy_load.js';
+import type {CrAutoImgElement} from 'chrome://new-tab-page/new_tab_page.js';
+import {$$} from 'chrome://new-tab-page/new_tab_page.js';
 import {RecipesHandlerRemote} from 'chrome://new-tab-page/recipes.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {installMock} from '../../test_support.js';
diff --git a/chrome/test/data/webui/new_tab_page/modules/v2/drive/module_test.ts b/chrome/test/data/webui/new_tab_page/modules/v2/drive/module_test.ts
index 7709b5df..9f2105a 100644
--- a/chrome/test/data/webui/new_tab_page/modules/v2/drive/module_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/v2/drive/module_test.ts
@@ -3,10 +3,12 @@
 // found in the LICENSE file.
 
 import {DriveHandlerRemote} from 'chrome://new-tab-page/drive.mojom-webui.js';
-import {DisableModuleEvent, DismissModuleEvent, DriveProxy, driveV2Descriptor, DriveV2ModuleElement} from 'chrome://new-tab-page/lazy_load.js';
-import {$$, CrAutoImgElement} from 'chrome://new-tab-page/new_tab_page.js';
+import type {DisableModuleEvent, DismissModuleEvent, DriveV2ModuleElement} from 'chrome://new-tab-page/lazy_load.js';
+import {DriveProxy, driveV2Descriptor} from 'chrome://new-tab-page/lazy_load.js';
+import type {CrAutoImgElement} from 'chrome://new-tab-page/new_tab_page.js';
+import {$$} from 'chrome://new-tab-page/new_tab_page.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
 
 import {installMock} from '../../../test_support.js';
diff --git a/chrome/test/data/webui/new_tab_page/modules/v2/dummy/module_test.ts b/chrome/test/data/webui/new_tab_page/modules/v2/dummy/module_test.ts
index 7cbc169..1906a2a 100644
--- a/chrome/test/data/webui/new_tab_page/modules/v2/dummy/module_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/v2/dummy/module_test.ts
@@ -3,11 +3,12 @@
 // found in the LICENSE file.
 
 import {FooHandlerRemote} from 'chrome://new-tab-page/foo.mojom-webui.js';
-import {DummyModuleElement, dummyV2Descriptor, FooProxy} from 'chrome://new-tab-page/lazy_load.js';
-import {CrAutoImgElement} from 'chrome://new-tab-page/new_tab_page.js';
+import type {DummyModuleElement} from 'chrome://new-tab-page/lazy_load.js';
+import {dummyV2Descriptor, FooProxy} from 'chrome://new-tab-page/lazy_load.js';
+import type {CrAutoImgElement} from 'chrome://new-tab-page/new_tab_page.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
 
 import {installMock} from '../../../test_support.js';
diff --git a/chrome/test/data/webui/new_tab_page/modules/v2/history_clusters/cart/cart_tile_test.ts b/chrome/test/data/webui/new_tab_page/modules/v2/history_clusters/cart/cart_tile_test.ts
index 8bfc75ae..f1f1d28 100644
--- a/chrome/test/data/webui/new_tab_page/modules/v2/history_clusters/cart/cart_tile_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/v2/history_clusters/cart/cart_tile_test.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Cart} from 'chrome://new-tab-page/cart.mojom-webui.js';
+import type {Cart} from 'chrome://new-tab-page/cart.mojom-webui.js';
 import {CartTileModuleElementV2} from 'chrome://new-tab-page/lazy_load.js';
 import {$$} from 'chrome://new-tab-page/new_tab_page.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
diff --git a/chrome/test/data/webui/new_tab_page/modules/v2/history_clusters/module_test.ts b/chrome/test/data/webui/new_tab_page/modules/v2/history_clusters/module_test.ts
index b54040ac..53f1856 100644
--- a/chrome/test/data/webui/new_tab_page/modules/v2/history_clusters/module_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/v2/history_clusters/module_test.ts
@@ -2,19 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Discount} from 'chrome://new-tab-page/discount.mojom-webui.js';
-import {Cluster, InteractionState, URLVisit} from 'chrome://new-tab-page/history_cluster_types.mojom-webui.js';
+import type {Discount} from 'chrome://new-tab-page/discount.mojom-webui.js';
+import type {Cluster, URLVisit} from 'chrome://new-tab-page/history_cluster_types.mojom-webui.js';
+import {InteractionState} from 'chrome://new-tab-page/history_cluster_types.mojom-webui.js';
 import {LayoutType} from 'chrome://new-tab-page/history_clusters_layout_type.mojom-webui.js';
 import {PageHandlerRemote} from 'chrome://new-tab-page/history_clusters_v2.mojom-webui.js';
-import {DismissModuleInstanceEvent, HistoryClustersProxyImplV2, historyClustersV2Descriptor, HistoryClustersV2ModuleElement, HistoryClusterV2ImageDisplayState, PageImageServiceBrowserProxy, VisitTileModuleElement} from 'chrome://new-tab-page/lazy_load.js';
+import type {DismissModuleInstanceEvent, HistoryClustersV2ModuleElement, VisitTileModuleElement} from 'chrome://new-tab-page/lazy_load.js';
+import {HistoryClustersProxyImplV2, historyClustersV2Descriptor, HistoryClusterV2ImageDisplayState, PageImageServiceBrowserProxy} from 'chrome://new-tab-page/lazy_load.js';
 import {$$} from 'chrome://new-tab-page/new_tab_page.js';
 import {PageImageServiceHandlerRemote} from 'chrome://resources/cr_components/page_image_service/page_image_service.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
+import type {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 import {assertDeepEquals, assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {installMock} from '../../../test_support.js';
diff --git a/chrome/test/data/webui/new_tab_page/modules/v2/modules_test.ts b/chrome/test/data/webui/new_tab_page/modules/v2/modules_test.ts
index 1960d4c..8a40a6d 100644
--- a/chrome/test/data/webui/new_tab_page/modules/v2/modules_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/v2/modules_test.ts
@@ -2,14 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Module, MODULE_CUSTOMIZE_ELEMENT_ID, ModuleDescriptor, ModuleRegistry, ModulesV2Element, ModuleWrapperElement, NamedWidth, SUPPORTED_MODULE_WIDTHS} from 'chrome://new-tab-page/lazy_load.js';
+import type {Module, ModuleWrapperElement, NamedWidth} from 'chrome://new-tab-page/lazy_load.js';
+import {MODULE_CUSTOMIZE_ELEMENT_ID, ModuleDescriptor, ModuleRegistry, ModulesV2Element, SUPPORTED_MODULE_WIDTHS} from 'chrome://new-tab-page/lazy_load.js';
 import {NewTabPageProxy} from 'chrome://new-tab-page/new_tab_page.js';
-import {PageCallbackRouter, PageHandlerRemote, PageRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import type {PageRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import {PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 
 import {assertNotStyle, assertStyle, createElement, initNullModule, installMock} from '../../test_support.js';
 
diff --git a/chrome/test/data/webui/new_tab_page/modules/v2/tab_resumption/module_test.ts b/chrome/test/data/webui/new_tab_page/modules/v2/tab_resumption/module_test.ts
index 4a59186..81adc02 100644
--- a/chrome/test/data/webui/new_tab_page/modules/v2/tab_resumption/module_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/v2/tab_resumption/module_test.ts
@@ -2,13 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Tab} from 'chrome://new-tab-page/history_types.mojom-webui.js';
-import {tabResumptionDescriptor, TabResumptionModuleElement, TabResumptionProxyImpl} from 'chrome://new-tab-page/lazy_load.js';
+import type {Tab} from 'chrome://new-tab-page/history_types.mojom-webui.js';
+import type {TabResumptionModuleElement} from 'chrome://new-tab-page/lazy_load.js';
+import {tabResumptionDescriptor, TabResumptionProxyImpl} from 'chrome://new-tab-page/lazy_load.js';
 import {$$} from 'chrome://new-tab-page/new_tab_page.js';
 import {PageHandlerRemote} from 'chrome://new-tab-page/tab_resumption.mojom-webui.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 
 import {installMock} from '../../../test_support.js';
 
diff --git a/chrome/test/data/webui/new_tab_page/realbox/lens_test.ts b/chrome/test/data/webui/new_tab_page/realbox/lens_test.ts
index cd256f15..4dbe497b 100644
--- a/chrome/test/data/webui/new_tab_page/realbox/lens_test.ts
+++ b/chrome/test/data/webui/new_tab_page/realbox/lens_test.ts
@@ -4,8 +4,9 @@
 
 import 'chrome://new-tab-page/new_tab_page.js';
 
-import {BrowserProxyImpl, MetricsReporterImpl, mojoString16, RealboxBrowserProxy, RealboxElement} from 'chrome://new-tab-page/new_tab_page.js';
-import {AutocompleteMatch} from 'chrome://resources/cr_components/omnibox/omnibox.mojom-webui.js';
+import type {RealboxElement} from 'chrome://new-tab-page/new_tab_page.js';
+import {BrowserProxyImpl, MetricsReporterImpl, mojoString16, RealboxBrowserProxy} from 'chrome://new-tab-page/new_tab_page.js';
+import type {AutocompleteMatch} from 'chrome://resources/cr_components/omnibox/omnibox.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {PageMetricsCallbackRouter} from 'chrome://resources/js/metrics_reporter.mojom-webui.js';
 import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/new_tab_page/realbox/realbox_test.ts b/chrome/test/data/webui/new_tab_page/realbox/realbox_test.ts
index fc9dbb5..e384caa 100644
--- a/chrome/test/data/webui/new_tab_page/realbox/realbox_test.ts
+++ b/chrome/test/data/webui/new_tab_page/realbox/realbox_test.ts
@@ -4,8 +4,10 @@
 
 import 'chrome://new-tab-page/new_tab_page.js';
 
-import {$$, BrowserProxyImpl, decodeString16, MetricsReporterImpl, mojoString16, RealboxBrowserProxy, RealboxElement, RealboxIconElement, RealboxMatchElement} from 'chrome://new-tab-page/new_tab_page.js';
-import {AutocompleteMatch, NavigationPredictor, RenderType, SideType} from 'chrome://resources/cr_components/omnibox/omnibox.mojom-webui.js';
+import type {RealboxElement, RealboxIconElement, RealboxMatchElement} from 'chrome://new-tab-page/new_tab_page.js';
+import {$$, BrowserProxyImpl, decodeString16, MetricsReporterImpl, mojoString16, RealboxBrowserProxy} from 'chrome://new-tab-page/new_tab_page.js';
+import type {AutocompleteMatch} from 'chrome://resources/cr_components/omnibox/omnibox.mojom-webui.js';
+import {NavigationPredictor, RenderType, SideType} from 'chrome://resources/cr_components/omnibox/omnibox.mojom-webui.js';
 import {getFaviconForPageURL} from 'chrome://resources/js/icon.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {PageMetricsCallbackRouter} from 'chrome://resources/js/metrics_reporter.mojom-webui.js';
diff --git a/chrome/test/data/webui/new_tab_page/realbox/test_realbox_browser_proxy.ts b/chrome/test/data/webui/new_tab_page/realbox/test_realbox_browser_proxy.ts
index e4a12261..be246be 100644
--- a/chrome/test/data/webui/new_tab_page/realbox/test_realbox_browser_proxy.ts
+++ b/chrome/test/data/webui/new_tab_page/realbox/test_realbox_browser_proxy.ts
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {NavigationPredictor, PageCallbackRouter, PageHandlerInterface, PageRemote} from 'chrome://resources/cr_components/omnibox/omnibox.mojom-webui.js';
-import {String16} from 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js';
-import {TimeTicks} from 'chrome://resources/mojo/mojo/public/mojom/base/time.mojom-webui.js';
-import {Size} from 'chrome://resources/mojo/ui/gfx/geometry/mojom/geometry.mojom-webui.js';
-import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
+import type {NavigationPredictor, PageHandlerInterface, PageRemote} from 'chrome://resources/cr_components/omnibox/omnibox.mojom-webui.js';
+import {PageCallbackRouter} from 'chrome://resources/cr_components/omnibox/omnibox.mojom-webui.js';
+import type {String16} from 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js';
+import type {TimeTicks} from 'chrome://resources/mojo/mojo/public/mojom/base/time.mojom-webui.js';
+import type {Size} from 'chrome://resources/mojo/ui/gfx/geometry/mojom/geometry.mojom-webui.js';
+import type {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 
diff --git a/chrome/test/data/webui/new_tab_page/test_support.ts b/chrome/test/data/webui/new_tab_page/test_support.ts
index 0d7361133..9cec75f 100644
--- a/chrome/test/data/webui/new_tab_page/test_support.ts
+++ b/chrome/test/data/webui/new_tab_page/test_support.ts
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {DomIf} from 'chrome://new-tab-page/new_tab_page.js';
-import {BackgroundImage, NtpBackgroundImageSource, Theme} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import type {DomIf} from 'chrome://new-tab-page/new_tab_page.js';
+import type {BackgroundImage, Theme} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import {NtpBackgroundImageSource} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {assertEquals, assertNotEquals} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/new_tab_page/voice_search_overlay_test.ts b/chrome/test/data/webui/new_tab_page/voice_search_overlay_test.ts
index 6264f1aa..4167ba0e 100644
--- a/chrome/test/data/webui/new_tab_page/voice_search_overlay_test.ts
+++ b/chrome/test/data/webui/new_tab_page/voice_search_overlay_test.ts
@@ -4,14 +4,15 @@
 
 import 'chrome://new-tab-page/lazy_load.js';
 
-import {VoiceSearchOverlayElement} from 'chrome://new-tab-page/lazy_load.js';
+import type {VoiceSearchOverlayElement} from 'chrome://new-tab-page/lazy_load.js';
 import {$$, NewTabPageProxy, VoiceAction as Action, VoiceError as Error, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
 
 import {assertNotStyle, assertStyle, installMock, keydown} from './test_support.js';
diff --git a/chrome/test/data/webui/password_manager/add_password_dialog_test.ts b/chrome/test/data/webui/password_manager/add_password_dialog_test.ts
index 323e960..7de054c4 100644
--- a/chrome/test/data/webui/password_manager/add_password_dialog_test.ts
+++ b/chrome/test/data/webui/password_manager/add_password_dialog_test.ts
@@ -6,7 +6,8 @@
 
 import {Page, PasswordManagerImpl, Router, SyncBrowserProxyImpl} from 'chrome://password-manager/password_manager.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
 
diff --git a/chrome/test/data/webui/password_manager/checkup_details_section_test.ts b/chrome/test/data/webui/password_manager/checkup_details_section_test.ts
index 77be369..4fe8ceaa 100644
--- a/chrome/test/data/webui/password_manager/checkup_details_section_test.ts
+++ b/chrome/test/data/webui/password_manager/checkup_details_section_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {CheckupSubpage, CrExpandButtonElement, OpenWindowProxyImpl, Page, PasswordCheckInteraction, PasswordManagerImpl, PluralStringProxyImpl, Router} from 'chrome://password-manager/password_manager.js';
+import type {CrExpandButtonElement} from 'chrome://password-manager/password_manager.js';
+import {CheckupSubpage, OpenWindowProxyImpl, Page, PasswordCheckInteraction, PasswordManagerImpl, PluralStringProxyImpl, Router} from 'chrome://password-manager/password_manager.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/password_manager/checkup_section_test.ts b/chrome/test/data/webui/password_manager/checkup_section_test.ts
index 033ecfc..a76a61b 100644
--- a/chrome/test/data/webui/password_manager/checkup_section_test.ts
+++ b/chrome/test/data/webui/password_manager/checkup_section_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {CheckupSubpage, Page, PasswordCheckInteraction, PasswordManagerImpl, PluralStringProxy, PluralStringProxyImpl, Router, UrlParam} from 'chrome://password-manager/password_manager.js';
+import type {PluralStringProxy} from 'chrome://password-manager/password_manager.js';
+import {CheckupSubpage, Page, PasswordCheckInteraction, PasswordManagerImpl, PluralStringProxyImpl, Router, UrlParam} from 'chrome://password-manager/password_manager.js';
 import {assertArrayEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
diff --git a/chrome/test/data/webui/password_manager/credential_field_test.ts b/chrome/test/data/webui/password_manager/credential_field_test.ts
index 8ae5f97..6852d386 100644
--- a/chrome/test/data/webui/password_manager/credential_field_test.ts
+++ b/chrome/test/data/webui/password_manager/credential_field_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {CredentialFieldElement, Page, PasswordManagerImpl, PasswordViewPageInteractions, Router} from 'chrome://password-manager/password_manager.js';
+import type {CredentialFieldElement} from 'chrome://password-manager/password_manager.js';
+import {Page, PasswordManagerImpl, PasswordViewPageInteractions, Router} from 'chrome://password-manager/password_manager.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
diff --git a/chrome/test/data/webui/password_manager/credential_note_test.ts b/chrome/test/data/webui/password_manager/credential_note_test.ts
index 1e39458..9a2bcf0 100644
--- a/chrome/test/data/webui/password_manager/credential_note_test.ts
+++ b/chrome/test/data/webui/password_manager/credential_note_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {CredentialNoteElement, Page, PasswordManagerImpl, Router} from 'chrome://password-manager/password_manager.js';
+import type {CredentialNoteElement} from 'chrome://password-manager/password_manager.js';
+import {Page, PasswordManagerImpl, Router} from 'chrome://password-manager/password_manager.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/password_manager/delete_passkey_dialog_test.ts b/chrome/test/data/webui/password_manager/delete_passkey_dialog_test.ts
index 223199b..473bce0 100644
--- a/chrome/test/data/webui/password_manager/delete_passkey_dialog_test.ts
+++ b/chrome/test/data/webui/password_manager/delete_passkey_dialog_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {DeletePasskeyDialogElement, PasswordManagerImpl} from 'chrome://password-manager/password_manager.js';
+import type {DeletePasskeyDialogElement} from 'chrome://password-manager/password_manager.js';
+import {PasswordManagerImpl} from 'chrome://password-manager/password_manager.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/password_manager/edit_passkey_dialog_test.ts b/chrome/test/data/webui/password_manager/edit_passkey_dialog_test.ts
index 18302cd..af9dc69d 100644
--- a/chrome/test/data/webui/password_manager/edit_passkey_dialog_test.ts
+++ b/chrome/test/data/webui/password_manager/edit_passkey_dialog_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {EditPasskeyDialogElement, PasswordManagerImpl} from 'chrome://password-manager/password_manager.js';
+import type {EditPasskeyDialogElement} from 'chrome://password-manager/password_manager.js';
+import {PasswordManagerImpl} from 'chrome://password-manager/password_manager.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertFalse} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/password_manager/edit_password_dialog_test.ts b/chrome/test/data/webui/password_manager/edit_password_dialog_test.ts
index 9d990fb..5826748 100644
--- a/chrome/test/data/webui/password_manager/edit_password_dialog_test.ts
+++ b/chrome/test/data/webui/password_manager/edit_password_dialog_test.ts
@@ -7,7 +7,8 @@
 import {Page, PasswordManagerImpl, Router} from 'chrome://password-manager/password_manager.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
 import {TestPasswordManagerProxy} from './test_password_manager_proxy.js';
diff --git a/chrome/test/data/webui/password_manager/passkey_details_card_test.ts b/chrome/test/data/webui/password_manager/passkey_details_card_test.ts
index 352a6312..91dfae0 100644
--- a/chrome/test/data/webui/password_manager/passkey_details_card_test.ts
+++ b/chrome/test/data/webui/password_manager/passkey_details_card_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {DeletePasskeyDialogElement, EditPasskeyDialogElement, Page, PasskeyDetailsCardElement, PasswordManagerImpl, PasswordViewPageInteractions, Router} from 'chrome://password-manager/password_manager.js';
+import type {DeletePasskeyDialogElement, EditPasskeyDialogElement, PasskeyDetailsCardElement} from 'chrome://password-manager/password_manager.js';
+import {Page, PasswordManagerImpl, PasswordViewPageInteractions, Router} from 'chrome://password-manager/password_manager.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/password_manager/password_details_card_test.ts b/chrome/test/data/webui/password_manager/password_details_card_test.ts
index fe8adf4..ff67cae5 100644
--- a/chrome/test/data/webui/password_manager/password_details_card_test.ts
+++ b/chrome/test/data/webui/password_manager/password_details_card_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {EditPasswordDialogElement, Page, PasswordDetailsCardElement, PasswordManagerImpl, PasswordViewPageInteractions, Router, SyncBrowserProxyImpl} from 'chrome://password-manager/password_manager.js';
+import type {EditPasswordDialogElement, PasswordDetailsCardElement} from 'chrome://password-manager/password_manager.js';
+import {Page, PasswordManagerImpl, PasswordViewPageInteractions, Router, SyncBrowserProxyImpl} from 'chrome://password-manager/password_manager.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/password_manager/password_details_section_test.ts b/chrome/test/data/webui/password_manager/password_details_section_test.ts
index 8db245c..b0d077b 100644
--- a/chrome/test/data/webui/password_manager/password_details_section_test.ts
+++ b/chrome/test/data/webui/password_manager/password_details_section_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {Page, PasskeyDetailsCardElement, PASSWORD_SHARE_BUTTON_BUTTON_ELEMENT_ID, PasswordDetailsCardElement, PasswordDetailsSectionElement, PasswordManagerImpl, PasswordViewPageInteractions, Router, SyncBrowserProxyImpl, UrlParam} from 'chrome://password-manager/password_manager.js';
+import type {PasskeyDetailsCardElement, PasswordDetailsCardElement, PasswordDetailsSectionElement} from 'chrome://password-manager/password_manager.js';
+import {Page, PASSWORD_SHARE_BUTTON_BUTTON_ELEMENT_ID, PasswordManagerImpl, PasswordViewPageInteractions, Router, SyncBrowserProxyImpl, UrlParam} from 'chrome://password-manager/password_manager.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertArrayEquals, assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/password_manager/password_manager_app_test.ts b/chrome/test/data/webui/password_manager/password_manager_app_test.ts
index 381fdc8..7190dca 100644
--- a/chrome/test/data/webui/password_manager/password_manager_app_test.ts
+++ b/chrome/test/data/webui/password_manager/password_manager_app_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {OpenWindowProxyImpl, Page, PasswordManagerAppElement, PasswordManagerImpl, Router, UrlParam} from 'chrome://password-manager/password_manager.js';
+import type {PasswordManagerAppElement} from 'chrome://password-manager/password_manager.js';
+import {OpenWindowProxyImpl, Page, PasswordManagerImpl, Router, UrlParam} from 'chrome://password-manager/password_manager.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/password_manager/password_manager_routing_test.ts b/chrome/test/data/webui/password_manager/password_manager_routing_test.ts
index 9ff4560..e234e1bc 100644
--- a/chrome/test/data/webui/password_manager/password_manager_routing_test.ts
+++ b/chrome/test/data/webui/password_manager/password_manager_routing_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {CheckupSubpage, Page, Route, RouteObserverMixin, Router, UrlParam} from 'chrome://password-manager/password_manager.js';
+import type {Route} from 'chrome://password-manager/password_manager.js';
+import {CheckupSubpage, Page, RouteObserverMixin, Router, UrlParam} from 'chrome://password-manager/password_manager.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/password_manager/password_manager_side_bar_test.ts b/chrome/test/data/webui/password_manager/password_manager_side_bar_test.ts
index 419af11f..068883f7 100644
--- a/chrome/test/data/webui/password_manager/password_manager_side_bar_test.ts
+++ b/chrome/test/data/webui/password_manager/password_manager_side_bar_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {CheckupSubpage, Page, PasswordManagerImpl, PasswordManagerSideBarElement, Router} from 'chrome://password-manager/password_manager.js';
+import type {PasswordManagerSideBarElement} from 'chrome://password-manager/password_manager.js';
+import {CheckupSubpage, Page, PasswordManagerImpl, Router} from 'chrome://password-manager/password_manager.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/password_manager/passwords_exporter_test.ts b/chrome/test/data/webui/password_manager/passwords_exporter_test.ts
index 98f3264..45dc407 100644
--- a/chrome/test/data/webui/password_manager/passwords_exporter_test.ts
+++ b/chrome/test/data/webui/password_manager/passwords_exporter_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {CrButtonElement, CrDialogElement, PasswordManagerImpl, PasswordsExporterElement} from 'chrome://password-manager/password_manager.js';
+import type {CrButtonElement, CrDialogElement, PasswordsExporterElement} from 'chrome://password-manager/password_manager.js';
+import {PasswordManagerImpl} from 'chrome://password-manager/password_manager.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {MockTimer} from 'chrome://webui-test/mock_timer.js';
diff --git a/chrome/test/data/webui/password_manager/passwords_importer_test.ts b/chrome/test/data/webui/password_manager/passwords_importer_test.ts
index 4e6bdd6d..acb2b3d 100644
--- a/chrome/test/data/webui/password_manager/passwords_importer_test.ts
+++ b/chrome/test/data/webui/password_manager/passwords_importer_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {CrButtonElement, CrDialogElement, Page, PasswordManagerImpl, PasswordsImporterElement, PluralStringProxyImpl, Router} from 'chrome://password-manager/password_manager.js';
+import type {CrButtonElement, CrDialogElement, PasswordsImporterElement} from 'chrome://password-manager/password_manager.js';
+import {Page, PasswordManagerImpl, PluralStringProxyImpl, Router} from 'chrome://password-manager/password_manager.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertArrayEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/password_manager/passwords_section_test.ts b/chrome/test/data/webui/password_manager/passwords_section_test.ts
index 8de9916..7c09896 100644
--- a/chrome/test/data/webui/password_manager/passwords_section_test.ts
+++ b/chrome/test/data/webui/password_manager/passwords_section_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {AddPasswordDialogElement, AuthTimedOutDialogElement, Page, PasswordListItemElement, PasswordManagerImpl, PasswordsSectionElement, PasswordViewPageInteractions, PluralStringProxyImpl, Router, SyncBrowserProxyImpl, UrlParam} from 'chrome://password-manager/password_manager.js';
+import type {AddPasswordDialogElement, AuthTimedOutDialogElement, PasswordListItemElement, PasswordsSectionElement} from 'chrome://password-manager/password_manager.js';
+import {Page, PasswordManagerImpl, PasswordViewPageInteractions, PluralStringProxyImpl, Router, SyncBrowserProxyImpl, UrlParam} from 'chrome://password-manager/password_manager.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertArrayEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/password_manager/promo_cards_test.ts b/chrome/test/data/webui/password_manager/promo_cards_test.ts
index acdb1ae..27c06ba 100644
--- a/chrome/test/data/webui/password_manager/promo_cards_test.ts
+++ b/chrome/test/data/webui/password_manager/promo_cards_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {Page, PasswordManagerImpl, PasswordsSectionElement, PromoCardsProxyImpl, Router, SyncBrowserProxyImpl, UrlParam} from 'chrome://password-manager/password_manager.js';
+import type {PasswordsSectionElement} from 'chrome://password-manager/password_manager.js';
+import {Page, PasswordManagerImpl, PromoCardsProxyImpl, Router, SyncBrowserProxyImpl, UrlParam} from 'chrome://password-manager/password_manager.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/password_manager/settings_section_test.ts b/chrome/test/data/webui/password_manager/settings_section_test.ts
index 640c992..0c5318e6 100644
--- a/chrome/test/data/webui/password_manager/settings_section_test.ts
+++ b/chrome/test/data/webui/password_manager/settings_section_test.ts
@@ -19,7 +19,8 @@
 
 // clang-format off
 // <if expr="is_win or is_macosx">
-import {PasskeysBrowserProxyImpl, PrefToggleButtonElement} from 'chrome://password-manager/password_manager.js';
+import type { PrefToggleButtonElement} from 'chrome://password-manager/password_manager.js';
+import {PasskeysBrowserProxyImpl} from 'chrome://password-manager/password_manager.js';
 
 import {TestPasskeysBrowserProxy} from './test_passkeys_browser_proxy.js';
 // </if>
diff --git a/chrome/test/data/webui/password_manager/share_password_confirmation_dialog_test.ts b/chrome/test/data/webui/password_manager/share_password_confirmation_dialog_test.ts
index d5b54d6..9c4ccca 100644
--- a/chrome/test/data/webui/password_manager/share_password_confirmation_dialog_test.ts
+++ b/chrome/test/data/webui/password_manager/share_password_confirmation_dialog_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {PasswordManagerImpl, SharePasswordConfirmationDialogElement} from 'chrome://password-manager/password_manager.js';
+import type {SharePasswordConfirmationDialogElement} from 'chrome://password-manager/password_manager.js';
+import {PasswordManagerImpl} from 'chrome://password-manager/password_manager.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {MockTimer} from 'chrome://webui-test/mock_timer.js';
diff --git a/chrome/test/data/webui/password_manager/share_password_flow_test.ts b/chrome/test/data/webui/password_manager/share_password_flow_test.ts
index 93d38246..473f8cb 100644
--- a/chrome/test/data/webui/password_manager/share_password_flow_test.ts
+++ b/chrome/test/data/webui/password_manager/share_password_flow_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {PasswordManagerImpl, ShareFlowState, SharePasswordFlowElement} from 'chrome://password-manager/password_manager.js';
+import type {SharePasswordFlowElement} from 'chrome://password-manager/password_manager.js';
+import {PasswordManagerImpl, ShareFlowState} from 'chrome://password-manager/password_manager.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/password_manager/share_password_group_avatar_test.ts b/chrome/test/data/webui/password_manager/share_password_group_avatar_test.ts
index 6101995..320ebd82 100644
--- a/chrome/test/data/webui/password_manager/share_password_group_avatar_test.ts
+++ b/chrome/test/data/webui/password_manager/share_password_group_avatar_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {SharePasswordGroupAvatarElement} from 'chrome://password-manager/password_manager.js';
+import type {SharePasswordGroupAvatarElement} from 'chrome://password-manager/password_manager.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/password_manager/share_password_loading_dialog_test.ts b/chrome/test/data/webui/password_manager/share_password_loading_dialog_test.ts
index 7677a7a..ed33c24 100644
--- a/chrome/test/data/webui/password_manager/share_password_loading_dialog_test.ts
+++ b/chrome/test/data/webui/password_manager/share_password_loading_dialog_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {SharePasswordLoadingDialogElement} from 'chrome://password-manager/password_manager.js';
+import type {SharePasswordLoadingDialogElement} from 'chrome://password-manager/password_manager.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
diff --git a/chrome/test/data/webui/password_manager/share_password_recipient_test.ts b/chrome/test/data/webui/password_manager/share_password_recipient_test.ts
index 2019d33..fa63f096 100644
--- a/chrome/test/data/webui/password_manager/share_password_recipient_test.ts
+++ b/chrome/test/data/webui/password_manager/share_password_recipient_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {SharePasswordRecipientElement} from 'chrome://password-manager/password_manager.js';
+import type {SharePasswordRecipientElement} from 'chrome://password-manager/password_manager.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/password_manager/site_favicon_test.ts b/chrome/test/data/webui/password_manager/site_favicon_test.ts
index 4712655..b44c9e0 100644
--- a/chrome/test/data/webui/password_manager/site_favicon_test.ts
+++ b/chrome/test/data/webui/password_manager/site_favicon_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {SiteFaviconElement} from 'chrome://password-manager/password_manager.js';
+import type {SiteFaviconElement} from 'chrome://password-manager/password_manager.js';
 import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
 
diff --git a/chrome/test/data/webui/password_manager/test_passkeys_browser_proxy.ts b/chrome/test/data/webui/password_manager/test_passkeys_browser_proxy.ts
index 17e76b8..bba26ff 100644
--- a/chrome/test/data/webui/password_manager/test_passkeys_browser_proxy.ts
+++ b/chrome/test/data/webui/password_manager/test_passkeys_browser_proxy.ts
@@ -4,7 +4,7 @@
 
 /** @fileoverview Test implementation of PasskeysBrowserProxy. */
 
-import {PasskeysBrowserProxy} from 'chrome://password-manager/password_manager.js';
+import type {PasskeysBrowserProxy} from 'chrome://password-manager/password_manager.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export class TestPasskeysBrowserProxy extends TestBrowserProxy implements
diff --git a/chrome/test/data/webui/password_manager/test_password_manager_proxy.ts b/chrome/test/data/webui/password_manager/test_password_manager_proxy.ts
index 13b53b52..dd48f89 100644
--- a/chrome/test/data/webui/password_manager/test_password_manager_proxy.ts
+++ b/chrome/test/data/webui/password_manager/test_password_manager_proxy.ts
@@ -4,7 +4,7 @@
 
 /** @fileoverview Test implementation of PasswordManagerProxy. */
 
-import {AccountStorageOptInStateChangedListener, BlockedSite, BlockedSitesListChangedListener, CredentialsChangedListener, PasswordCheckInteraction, PasswordCheckStatusChangedListener, PasswordManagerAuthTimeoutListener, PasswordManagerProxy, PasswordsFileExportProgressListener, PasswordViewPageInteractions} from 'chrome://password-manager/password_manager.js';
+import type {AccountStorageOptInStateChangedListener, BlockedSite, BlockedSitesListChangedListener, CredentialsChangedListener, PasswordCheckInteraction, PasswordCheckStatusChangedListener, PasswordManagerAuthTimeoutListener, PasswordManagerProxy, PasswordsFileExportProgressListener, PasswordViewPageInteractions} from 'chrome://password-manager/password_manager.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 import {makeFamilyFetchResults, makePasswordCheckStatus} from './test_util.js';
diff --git a/chrome/test/data/webui/password_manager/test_promo_cards_browser_proxy.ts b/chrome/test/data/webui/password_manager/test_promo_cards_browser_proxy.ts
index 3916a132..1a59861d 100644
--- a/chrome/test/data/webui/password_manager/test_promo_cards_browser_proxy.ts
+++ b/chrome/test/data/webui/password_manager/test_promo_cards_browser_proxy.ts
@@ -4,7 +4,7 @@
 
 /** @fileoverview Test implementation of PromoCardsProxy. */
 
-import {PromoCard, PromoCardsProxy} from 'chrome://password-manager/password_manager.js';
+import type {PromoCard, PromoCardsProxy} from 'chrome://password-manager/password_manager.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 /**
diff --git a/chrome/test/data/webui/password_manager/test_sync_browser_proxy.ts b/chrome/test/data/webui/password_manager/test_sync_browser_proxy.ts
index dedc91c..a0a70e16c 100644
--- a/chrome/test/data/webui/password_manager/test_sync_browser_proxy.ts
+++ b/chrome/test/data/webui/password_manager/test_sync_browser_proxy.ts
@@ -4,7 +4,8 @@
 
 /** @fileoverview Test implementation of SyncBrowserProxy. */
 
-import {AccountInfo, SyncBrowserProxy, SyncInfo, TrustedVaultBannerState} from 'chrome://password-manager/password_manager.js';
+import type {AccountInfo, SyncBrowserProxy, SyncInfo} from 'chrome://password-manager/password_manager.js';
+import {TrustedVaultBannerState} from 'chrome://password-manager/password_manager.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 /**
diff --git a/chrome/test/data/webui/polymer_test_util.ts b/chrome/test/data/webui/polymer_test_util.ts
index c273f7f2..c6849aa 100644
--- a/chrome/test/data/webui/polymer_test_util.ts
+++ b/chrome/test/data/webui/polymer_test_util.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {afterNextRender, beforeNextRender, flush, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {afterNextRender, beforeNextRender, flush} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 /**
  * Converts beforeNextRender() API to promise-based.
diff --git a/chrome/test/data/webui/privacy_sandbox/internals/content_settings_test.ts b/chrome/test/data/webui/privacy_sandbox/internals/content_settings_test.ts
index 4f06a55e..a07e3e0e 100644
--- a/chrome/test/data/webui/privacy_sandbox/internals/content_settings_test.ts
+++ b/chrome/test/data/webui/privacy_sandbox/internals/content_settings_test.ts
@@ -3,10 +3,12 @@
 // found in the LICENSE file.
 import 'chrome://privacy-sandbox-internals/content_setting_pattern_source.js';
 
-import {ContentSettingPatternSourceElement} from 'chrome://privacy-sandbox-internals/content_setting_pattern_source.js';
-import {ContentSettingPatternSource, RuleMetaData, SessionModel} from 'chrome://privacy-sandbox-internals/content_settings.mojom-webui.js';
-import {PageHandler, PageHandlerInterface} from 'chrome://privacy-sandbox-internals/privacy_sandbox_internals.mojom-webui.js';
-import {Value} from 'chrome://resources/mojo/mojo/public/mojom/base/values.mojom-webui.js';
+import type {ContentSettingPatternSourceElement} from 'chrome://privacy-sandbox-internals/content_setting_pattern_source.js';
+import type {ContentSettingPatternSource, RuleMetaData} from 'chrome://privacy-sandbox-internals/content_settings.mojom-webui.js';
+import {SessionModel} from 'chrome://privacy-sandbox-internals/content_settings.mojom-webui.js';
+import type {PageHandlerInterface} from 'chrome://privacy-sandbox-internals/privacy_sandbox_internals.mojom-webui.js';
+import {PageHandler} from 'chrome://privacy-sandbox-internals/privacy_sandbox_internals.mojom-webui.js';
+import type {Value} from 'chrome://resources/mojo/mojo/public/mojom/base/values.mojom-webui.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 suite('ContentSettingsElementTest', function() {
diff --git a/chrome/test/data/webui/privacy_sandbox/internals/privacy_sandbox_internals_test.ts b/chrome/test/data/webui/privacy_sandbox/internals/privacy_sandbox_internals_test.ts
index 030262c..4870f01a8 100644
--- a/chrome/test/data/webui/privacy_sandbox/internals/privacy_sandbox_internals_test.ts
+++ b/chrome/test/data/webui/privacy_sandbox/internals/privacy_sandbox_internals_test.ts
@@ -6,9 +6,10 @@
 import 'chrome://privacy-sandbox-internals/value_display.js';
 import 'chrome://privacy-sandbox-internals/pref_display.js';
 
-import {PrefDisplayElement} from 'chrome://privacy-sandbox-internals/pref_display.js';
-import {timestampLogicalFn, ValueDisplayElement} from 'chrome://privacy-sandbox-internals/value_display.js';
-import {DictionaryValue, ListValue, Value} from 'chrome://resources/mojo/mojo/public/mojom/base/values.mojom-webui.js';
+import type {PrefDisplayElement} from 'chrome://privacy-sandbox-internals/pref_display.js';
+import type {ValueDisplayElement} from 'chrome://privacy-sandbox-internals/value_display.js';
+import {timestampLogicalFn} from 'chrome://privacy-sandbox-internals/value_display.js';
+import type {DictionaryValue, ListValue, Value} from 'chrome://resources/mojo/mojo/public/mojom/base/values.mojom-webui.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 // Test that the <mojo-timestamp> CustomElement renders the correct time.
diff --git a/chrome/test/data/webui/privacy_sandbox/privacy_sandbox_dialog_test.ts b/chrome/test/data/webui/privacy_sandbox/privacy_sandbox_dialog_test.ts
index 8de191d..d82fd703a 100644
--- a/chrome/test/data/webui/privacy_sandbox/privacy_sandbox_dialog_test.ts
+++ b/chrome/test/data/webui/privacy_sandbox/privacy_sandbox_dialog_test.ts
@@ -7,14 +7,15 @@
 import 'chrome://privacy-sandbox-dialog/privacy_sandbox_notice_restricted_dialog_app.js';
 import 'chrome://privacy-sandbox-dialog/privacy_sandbox_combined_dialog_app.js';
 
-import {PrivacySandboxCombinedDialogAppElement, PrivacySandboxCombinedDialogStep} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_combined_dialog_app.js';
-import {PrivacySandboxDialogAppElement} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_dialog_app.js';
+import type {PrivacySandboxCombinedDialogAppElement} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_combined_dialog_app.js';
+import {PrivacySandboxCombinedDialogStep} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_combined_dialog_app.js';
+import type {PrivacySandboxDialogAppElement} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_dialog_app.js';
 import {PrivacySandboxDialogBrowserProxy, PrivacySandboxPromptAction} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_dialog_browser_proxy.js';
-import {PrivacySandboxDialogConsentStepElement} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_dialog_consent_step';
+import type {PrivacySandboxDialogConsentStepElement} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_dialog_consent_step';
 import {PrivacySandboxDialogMixin} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_dialog_mixin.js';
-import {PrivacySandboxDialogNoticeStepElement} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_dialog_notice_step';
-import {PrivacySandboxNoticeDialogAppElement} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_notice_dialog_app.js';
-import {PrivacySandboxNoticeRestrictedDialogAppElement} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_notice_restricted_dialog_app.js';
+import type {PrivacySandboxDialogNoticeStepElement} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_dialog_notice_step';
+import type {PrivacySandboxNoticeDialogAppElement} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_notice_dialog_app.js';
+import type {PrivacySandboxNoticeRestrictedDialogAppElement} from 'chrome://privacy-sandbox-dialog/privacy_sandbox_notice_restricted_dialog_app.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush, html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/test/data/webui/search_engine_choice/search_engine_choice_test.ts b/chrome/test/data/webui/search_engine_choice/search_engine_choice_test.ts
index 8986753..0303afa 100644
--- a/chrome/test/data/webui/search_engine_choice/search_engine_choice_test.ts
+++ b/chrome/test/data/webui/search_engine_choice/search_engine_choice_test.ts
@@ -4,11 +4,12 @@
 
 import 'chrome://search-engine-choice/app.js';
 
-import {SearchEngineChoiceAppElement} from 'chrome://search-engine-choice/app.js';
+import type {SearchEngineChoiceAppElement} from 'chrome://search-engine-choice/app.js';
 import {SearchEngineChoiceBrowserProxy} from 'chrome://search-engine-choice/browser_proxy.js';
 import {PageHandlerRemote} from 'chrome://search-engine-choice/search_engine_choice.mojom-webui.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import {waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
 import {TestMock} from 'chrome://webui-test/test_mock.js';
 
diff --git a/chrome/test/data/webui/settings/.eslintrc.js b/chrome/test/data/webui/settings/.eslintrc.js
deleted file mode 100644
index 04a8f73..0000000
--- a/chrome/test/data/webui/settings/.eslintrc.js
+++ /dev/null
@@ -1,7 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module.exports = {
-  'extends': '../../../../../ui/webui/resources/tools/eslint_import_type.config.js',
-};
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 23afbd4..44f6b810 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
@@ -307,7 +307,7 @@
         assertTrue(!!rowLink);
         assertTrue(isVisible(rowLink));
         assertEquals(
-            'Manage app notifications, Do not disturb, and app badging',
+            'Manage app notifications, Do Not Disturb, and app badging',
             rowLink.subLabel);
       });
     } else {
diff --git a/chrome/test/data/webui/settings/chromeos/os_bluetooth_page/os_bluetooth_change_device_name_dialog_test.ts b/chrome/test/data/webui/settings/chromeos/os_bluetooth_page/os_bluetooth_change_device_name_dialog_test.ts
index 10b9bdd0..458c9faf 100644
--- a/chrome/test/data/webui/settings/chromeos/os_bluetooth_page/os_bluetooth_change_device_name_dialog_test.ts
+++ b/chrome/test/data/webui/settings/chromeos/os_bluetooth_page/os_bluetooth_change_device_name_dialog_test.ts
@@ -6,7 +6,7 @@
 
 import {SettingsBluetoothChangeDeviceNameDialogElement} from 'chrome://os-settings/lazy_load.js';
 import {CrInputElement} from 'chrome://os-settings/os_settings.js';
-import {getDeviceName} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
+import {getDeviceNameUnsafe} from 'chrome://resources/ash/common/bluetooth/bluetooth_utils.js';
 import {setBluetoothConfigForTesting} from 'chrome://resources/ash/common/bluetooth/cros_bluetooth_config.js';
 import {DeviceConnectionState} from 'chrome://resources/mojo/chromeos/ash/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom-webui.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
@@ -18,6 +18,7 @@
   let bluetoothDeviceChangeNameDialog:
       SettingsBluetoothChangeDeviceNameDialogElement;
   let bluetoothConfig: FakeBluetoothConfig;
+  const deviceId = '12//345&6789';
 
   setup(() => {
     bluetoothConfig = new FakeBluetoothConfig();
@@ -61,7 +62,7 @@
 
   test('Input is sanitized', async () => {
     const device1 = createDefaultBluetoothDevice(
-        /*id=*/ '12//345&6789',
+        /*id=*/ deviceId,
         /*publicName=*/ 'BeatsX',
         /*connectionState=*/
         DeviceConnectionState.kConnected,
@@ -109,8 +110,10 @@
   });
 
   test('Device name is changed', async () => {
-    const id = '12//345&6789';
-    const nickname = 'Nickname';
+    const initialNickname = 'device1';
+    const newNickname = 'nickname';
+    const htmlNickname = '<a>html</a>';
+
     const getDoneBtn = () => {
       const doneButton = bluetoothDeviceChangeNameDialog.shadowRoot!
                              .querySelector<HTMLButtonElement>('#done');
@@ -119,11 +122,11 @@
     };
 
     const device = createDefaultBluetoothDevice(
-        id,
+        deviceId,
         /*publicName=*/ 'BeatsX',
         /*connectionState=*/
         DeviceConnectionState.kConnected,
-        /*opt_nickname=*/ 'device1');
+        /*opt_nickname=*/ initialNickname);
 
     bluetoothDeviceChangeNameDialog.set('device', {...device});
     bluetoothConfig.appendToPairedDeviceList([device]);
@@ -132,18 +135,27 @@
     const input = bluetoothDeviceChangeNameDialog.shadowRoot!
                       .querySelector<CrInputElement>('#changeNameInput');
     assertTrue(!!input);
-    assertEquals('device1', input.value);
+    assertEquals(initialNickname, input.value);
     assertTrue(getDoneBtn().disabled);
 
-    input.value = nickname;
+    input.value = newNickname;
     await flushTasks();
     assertFalse(getDoneBtn().disabled);
 
     getDoneBtn().click();
     await flushTasks();
+    assertEquals(
+        newNickname,
+        getDeviceNameUnsafe(bluetoothConfig.getPairedDeviceById(deviceId)));
 
-    const newName = getDeviceName(bluetoothConfig.getPairedDeviceById(id));
+    input.value = htmlNickname;
+    await flushTasks();
+    assertFalse(getDoneBtn().disabled);
 
-    assertEquals(nickname, newName);
+    getDoneBtn().click();
+    await flushTasks();
+    assertEquals(
+        htmlNickname,
+        getDeviceNameUnsafe(bluetoothConfig.getPairedDeviceById(deviceId)));
   });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list_item_test.ts b/chrome/test/data/webui/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list_item_test.ts
index 92a1c4e..72264dc 100644
--- a/chrome/test/data/webui/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list_item_test.ts
+++ b/chrome/test/data/webui/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list_item_test.ts
@@ -195,6 +195,32 @@
             getItemA11yLabel());
       });
 
+  test('Device name is set correctly', async () => {
+    const getDeviceName = () => {
+      const deviceName =
+          pairedBluetoothListItem.shadowRoot!.querySelector<HTMLElement>(
+              '#deviceName');
+      assertTrue(!!deviceName);
+      return deviceName;
+    };
+
+    const publicName = 'BeatsX';
+    const nameWithHtml = '<a>testname</a>';
+    const device = createDefaultBluetoothDevice(
+        /*id=*/ '123456789', /*publicName=*/ publicName,
+        /*connectionState=*/
+        DeviceConnectionState.kConnected);
+    pairedBluetoothListItem.set('device', device);
+    await flushTasks();
+    assertEquals(publicName, getDeviceName().innerText);
+
+    device.nickname = nameWithHtml;
+    pairedBluetoothListItem.set('device', {...device});
+    await flushTasks();
+    assertTrue(!!getDeviceName());
+    assertEquals(nameWithHtml, getDeviceName().innerText);
+  });
+
   test('Battery percentage out of bounds', async () => {
     const device = createDefaultBluetoothDevice(
         /*id=*/ '123456789', /*publicName=*/ 'BeatsX',
diff --git a/chrome/test/data/webui/settings/chromeos/os_bluetooth_page/os_saved_devices_list_test.ts b/chrome/test/data/webui/settings/chromeos/os_bluetooth_page/os_saved_devices_list_test.ts
index 7a690bb5..54cba85 100644
--- a/chrome/test/data/webui/settings/chromeos/os_bluetooth_page/os_saved_devices_list_test.ts
+++ b/chrome/test/data/webui/settings/chromeos/os_bluetooth_page/os_saved_devices_list_test.ts
@@ -177,4 +177,23 @@
     assertTrue(isVisible(
         getListItems()[1]!.shadowRoot!.querySelector('#deviceImage')));
   });
+
+  test('Device names are set properly', async () => {
+    const getDeviceName = () => {
+      return getListItems()[0]!.shadowRoot!
+          .querySelector<HTMLElement>('#deviceName')!.innerText;
+    };
+    const deviceName = 'deviceName';
+    const device = {name: deviceName, imageUrl: 'fakeUrl', accountKey: '1'};
+
+    savedDevicesList.set('devices', [device]);
+    await flushTasks();
+    assertEquals(deviceName, getDeviceName());
+
+    const nameWithHtml = '<a>test</a>';
+    device.name = nameWithHtml;
+    savedDevicesList.set('devices', [{...device}]);
+    await flushTasks();
+    assertEquals(nameWithHtml, getDeviceName());
+  });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
index c0f9b191..dec9555 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -11,7 +11,7 @@
 GEN('#include "content/public/test/browser_test.h"');
 
 /** Test fixture for shared Polymer 3 elements. */
-var OSSettingsBrowserTest = class extends PolymerTest {
+const OSSettingsBrowserTest = class extends PolymerTest {
   /** @override */
   get browsePreload() {
     return 'chrome://os-settings';
@@ -34,8 +34,8 @@
   {
     enabled: [
       'ash::standalone_browser::features::kLacrosOnly',
-      'ash::standalone_browser::features::kLacrosProfileMigrationForceOff'
-    ]
+      'ash::standalone_browser::features::kLacrosProfileMigrationForceOff',
+    ],
   },
 ],
 ].forEach(test => registerTest(...test));
diff --git a/chrome/test/data/webui/settings/privacy_sandbox_page_test.ts b/chrome/test/data/webui/settings/privacy_sandbox_page_test.ts
index 57a8181b..699ebfae 100644
--- a/chrome/test/data/webui/settings/privacy_sandbox_page_test.ts
+++ b/chrome/test/data/webui/settings/privacy_sandbox_page_test.ts
@@ -1066,13 +1066,13 @@
     // When the parent topic was blocked, the child topic does not get moved
     // to the blocked items list which is why we only have 3 blocked topics
     assertEquals(3, blockedItems.length);
-    const allowButton = blockedItems[0]!.shadowRoot!.querySelector('cr-button');
-    assert(allowButton);
-    assertEquals(page.i18n('unblockTopicButtonTextV2'), allowButton.innerText);
+    const unblockButton =
+        blockedItems[0]!.shadowRoot!.querySelector('cr-button');
+    assert(unblockButton);
+    assertEquals('Unblock', unblockButton.innerText);
     assertEquals(
-        page.i18n('topicsPageAllowTopicA11yLabel', 'test-topic-1'),
-        allowButton.getAttribute('aria-label'));
-    allowButton.click();
+        'Unblock test-topic-1', unblockButton.getAttribute('aria-label'));
+    unblockButton.click();
     await testPrivacySandboxBrowserProxy.whenCalled('setTopicAllowed');
     assertEquals(
         'Settings.PrivacySandbox.Topics.TopicAdded',
diff --git a/chrome/test/data/webui/settings/security_page_test.ts b/chrome/test/data/webui/settings/security_page_test.ts
index 358e27db..0264979 100644
--- a/chrome/test/data/webui/settings/security_page_test.ts
+++ b/chrome/test/data/webui/settings/security_page_test.ts
@@ -181,6 +181,7 @@
     page = document.createElement('settings-security-page');
     page.prefs = settingsPrefs.prefs;
     document.body.appendChild(page);
+    testHatsBrowserProxy.reset();
     Router.getInstance().navigateTo(routes.SECURITY);
     return flushTasks();
   });
@@ -190,6 +191,27 @@
     Router.getInstance().navigateTo(routes.BASIC);
   });
 
+  test('SecurityPageSwitchRouteCallsHatsProxy', async function() {
+    const t1 = 10000;
+    testHatsBrowserProxy.setNow(t1);
+    window.dispatchEvent(new Event('focus'));
+
+    const t2 = 20000;
+    testHatsBrowserProxy.setNow(t2);
+    window.dispatchEvent(new Event('blur'));
+
+    // Switch tabs within the settings page.
+    Router.getInstance().navigateTo(routes.PRIVACY);
+
+    const args =
+        await testHatsBrowserProxy.whenCalled('securityPageHatsRequest');
+
+    // Verify that the method securityPageHatsRequest was called and the time
+    // the user spent on the security page was logged correctly.
+    const expectedTotalTimeInFocus = t2 - t1;
+    assertEquals(expectedTotalTimeInFocus, args[2]);
+  });
+
   test('SecurityPageBeforeUnloadCallsHatsProxy', async function() {
     // Interact with the security page.
     page.$.safeBrowsingEnhanced.click();
@@ -213,6 +235,7 @@
 
     // Fire the beforeunload event to simulate closing the page.
     window.dispatchEvent(new Event('beforeunload'));
+
     const args =
         await testHatsBrowserProxy.whenCalled('securityPageHatsRequest');
 
diff --git a/chrome/test/data/webui/side_panel/bookmarks/commerce/shopping_list_test.ts b/chrome/test/data/webui/side_panel/bookmarks/commerce/shopping_list_test.ts
index acd26460..5673b9f 100644
--- a/chrome/test/data/webui/side_panel/bookmarks/commerce/shopping_list_test.ts
+++ b/chrome/test/data/webui/side_panel/bookmarks/commerce/shopping_list_test.ts
@@ -7,12 +7,14 @@
 
 import {ActionSource} from 'chrome://bookmarks-side-panel.top-chrome/bookmarks.mojom-webui.js';
 import {BookmarksApiProxyImpl} from 'chrome://bookmarks-side-panel.top-chrome/bookmarks_api_proxy.js';
-import {ACTION_BUTTON_TRACK_IMAGE, ACTION_BUTTON_UNTRACK_IMAGE, LOCAL_STORAGE_EXPAND_STATUS_KEY, ShoppingListElement} from 'chrome://bookmarks-side-panel.top-chrome/commerce/shopping_list.js';
+import type {ShoppingListElement} from 'chrome://bookmarks-side-panel.top-chrome/commerce/shopping_list.js';
+import {ACTION_BUTTON_TRACK_IMAGE, ACTION_BUTTON_UNTRACK_IMAGE, LOCAL_STORAGE_EXPAND_STATUS_KEY} from 'chrome://bookmarks-side-panel.top-chrome/commerce/shopping_list.js';
 import {BrowserProxyImpl} from 'chrome://resources/cr_components/commerce/browser_proxy.js';
-import {BookmarkProductInfo} from 'chrome://resources/cr_components/commerce/shopping_service.mojom-webui.js';
+import type {BookmarkProductInfo} from 'chrome://resources/cr_components/commerce/shopping_service.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
 
diff --git a/chrome/test/data/webui/side_panel/bookmarks/commerce/test_shopping_service_api_proxy.ts b/chrome/test/data/webui/side_panel/bookmarks/commerce/test_shopping_service_api_proxy.ts
index 2f3668f..3a1fbfd 100644
--- a/chrome/test/data/webui/side_panel/bookmarks/commerce/test_shopping_service_api_proxy.ts
+++ b/chrome/test/data/webui/side_panel/bookmarks/commerce/test_shopping_service_api_proxy.ts
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BrowserProxy} from 'chrome://resources/cr_components/commerce/browser_proxy.js';
-import {BookmarkProductInfo, PageCallbackRouter, PageRemote, PriceInsightsInfo, PriceInsightsInfo_PriceBucket, ProductInfo} from 'chrome://resources/cr_components/commerce/shopping_service.mojom-webui.js';
+import type {BrowserProxy} from 'chrome://resources/cr_components/commerce/browser_proxy.js';
+import type {BookmarkProductInfo, PageRemote, PriceInsightsInfo, ProductInfo} from 'chrome://resources/cr_components/commerce/shopping_service.mojom-webui.js';
+import {PageCallbackRouter, PriceInsightsInfo_PriceBucket} from 'chrome://resources/cr_components/commerce/shopping_service.mojom-webui.js';
 import {TestBrowserProxy as BaseTestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export class TestBrowserProxy extends BaseTestBrowserProxy implements
diff --git a/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_context_menu_test.ts b/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_context_menu_test.ts
index 59ac263..7897d4e 100644
--- a/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_context_menu_test.ts
+++ b/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_context_menu_test.ts
@@ -5,7 +5,7 @@
 import 'chrome://bookmarks-side-panel.top-chrome/power_bookmarks_context_menu.js';
 
 import {BookmarksApiProxyImpl} from 'chrome://bookmarks-side-panel.top-chrome/bookmarks_api_proxy.js';
-import {PowerBookmarksContextMenuElement} from 'chrome://bookmarks-side-panel.top-chrome/power_bookmarks_context_menu.js';
+import type {PowerBookmarksContextMenuElement} from 'chrome://bookmarks-side-panel.top-chrome/power_bookmarks_context_menu.js';
 import {PowerBookmarksService} from 'chrome://bookmarks-side-panel.top-chrome/power_bookmarks_service.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_edit_dialog_test.ts b/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_edit_dialog_test.ts
index fc80f721..a4f7dd71 100644
--- a/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_edit_dialog_test.ts
+++ b/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_edit_dialog_test.ts
@@ -5,9 +5,9 @@
 import 'chrome://bookmarks-side-panel.top-chrome/power_bookmarks_edit_dialog.js';
 
 import {BookmarksApiProxyImpl} from 'chrome://bookmarks-side-panel.top-chrome/bookmarks_api_proxy.js';
-import {PowerBookmarksEditDialogElement} from 'chrome://bookmarks-side-panel.top-chrome/power_bookmarks_edit_dialog.js';
+import type {PowerBookmarksEditDialogElement} from 'chrome://bookmarks-side-panel.top-chrome/power_bookmarks_edit_dialog.js';
 import {PowerBookmarksService} from 'chrome://bookmarks-side-panel.top-chrome/power_bookmarks_service.js';
-import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
+import type {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_labels_test.ts b/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_labels_test.ts
index 610c9e67..948b259 100644
--- a/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_labels_test.ts
+++ b/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_labels_test.ts
@@ -4,9 +4,9 @@
 
 import 'chrome://bookmarks-side-panel.top-chrome/power_bookmarks_labels.js';
 
-import {IronIconElement} from '//resources/polymer/v3_0/iron-icon/iron-icon.js';
-import {PowerBookmarksLabelsElement} from 'chrome://bookmarks-side-panel.top-chrome/power_bookmarks_labels.js';
-import {BookmarkProductInfo} from 'chrome://resources/cr_components/commerce/shopping_service.mojom-webui.js';
+import type {IronIconElement} from '//resources/polymer/v3_0/iron-icon/iron-icon.js';
+import type {PowerBookmarksLabelsElement} from 'chrome://bookmarks-side-panel.top-chrome/power_bookmarks_labels.js';
+import type {BookmarkProductInfo} from 'chrome://resources/cr_components/commerce/shopping_service.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_list_test.ts b/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_list_test.ts
index a026926..dc1a2d8 100644
--- a/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_list_test.ts
+++ b/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_list_test.ts
@@ -6,15 +6,15 @@
 
 import {SortOrder, ViewType} from 'chrome://bookmarks-side-panel.top-chrome/bookmarks.mojom-webui.js';
 import {BookmarksApiProxyImpl} from 'chrome://bookmarks-side-panel.top-chrome/bookmarks_api_proxy.js';
-import {PowerBookmarkRowElement} from 'chrome://bookmarks-side-panel.top-chrome/power_bookmark_row.js';
-import {PowerBookmarksListElement} from 'chrome://bookmarks-side-panel.top-chrome/power_bookmarks_list.js';
+import type {PowerBookmarkRowElement} from 'chrome://bookmarks-side-panel.top-chrome/power_bookmark_row.js';
+import type {PowerBookmarksListElement} from 'chrome://bookmarks-side-panel.top-chrome/power_bookmarks_list.js';
 import {BrowserProxyImpl} from 'chrome://resources/cr_components/commerce/browser_proxy.js';
 import {PageImageServiceBrowserProxy} from 'chrome://resources/cr_components/page_image_service/browser_proxy.js';
 import {PageImageServiceHandlerRemote} from 'chrome://resources/cr_components/page_image_service/page_image_service.mojom-webui.js';
-import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
-import {CrUrlListItemElement} from 'chrome://resources/cr_elements/cr_url_list_item/cr_url_list_item.js';
+import type {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
+import type {CrUrlListItemElement} from 'chrome://resources/cr_elements/cr_url_list_item/cr_url_list_item.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {IronListElement} from 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
+import type {IronListElement} from 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/side_panel/bookmarks/test_bookmarks_api_proxy.ts b/chrome/test/data/webui/side_panel/bookmarks/test_bookmarks_api_proxy.ts
index b7b2a95..b8e50c65 100644
--- a/chrome/test/data/webui/side_panel/bookmarks/test_bookmarks_api_proxy.ts
+++ b/chrome/test/data/webui/side_panel/bookmarks/test_bookmarks_api_proxy.ts
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ActionSource, SortOrder, ViewType} from 'chrome://bookmarks-side-panel.top-chrome/bookmarks.mojom-webui.js';
-import {BookmarksApiProxy} from 'chrome://bookmarks-side-panel.top-chrome/bookmarks_api_proxy.js';
-import {ClickModifiers} from 'chrome://resources/mojo/ui/base/mojom/window_open_disposition.mojom-webui.js';
+import type {ActionSource, SortOrder, ViewType} from 'chrome://bookmarks-side-panel.top-chrome/bookmarks.mojom-webui.js';
+import type {BookmarksApiProxy} from 'chrome://bookmarks-side-panel.top-chrome/bookmarks_api_proxy.js';
+import type {ClickModifiers} from 'chrome://resources/mojo/ui/base/mojom/window_open_disposition.mojom-webui.js';
 import {FakeChromeEvent} from 'chrome://webui-test/fake_chrome_event.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/side_panel/commerce/price_tracking_section_test.ts b/chrome/test/data/webui/side_panel/commerce/price_tracking_section_test.ts
index 9d6236ee..0700f5de 100644
--- a/chrome/test/data/webui/side_panel/commerce/price_tracking_section_test.ts
+++ b/chrome/test/data/webui/side_panel/commerce/price_tracking_section_test.ts
@@ -5,12 +5,14 @@
 import 'chrome://shopping-insights-side-panel.top-chrome/app.js';
 
 import {BrowserProxyImpl} from 'chrome://resources/cr_components/commerce/browser_proxy.js';
-import {BookmarkProductInfo, PageCallbackRouter, PageRemote, PriceInsightsInfo, PriceInsightsInfo_PriceBucket, ProductInfo} from 'chrome://resources/cr_components/commerce/shopping_service.mojom-webui.js';
+import type {BookmarkProductInfo, PageRemote, PriceInsightsInfo, ProductInfo} from 'chrome://resources/cr_components/commerce/shopping_service.mojom-webui.js';
+import {PageCallbackRouter, PriceInsightsInfo_PriceBucket} from 'chrome://resources/cr_components/commerce/shopping_service.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {stringToMojoString16} from 'chrome://resources/js/mojo_type_util.js';
-import {PriceTrackingSection} from 'chrome://shopping-insights-side-panel.top-chrome/price_tracking_section.js';
+import type {PriceTrackingSection} from 'chrome://shopping-insights-side-panel.top-chrome/price_tracking_section.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {TestMock} from 'chrome://webui-test/test_mock.js';
 
diff --git a/chrome/test/data/webui/side_panel/commerce/shopping_insights_app_test.ts b/chrome/test/data/webui/side_panel/commerce/shopping_insights_app_test.ts
index 2227ca5..e3adf4c 100644
--- a/chrome/test/data/webui/side_panel/commerce/shopping_insights_app_test.ts
+++ b/chrome/test/data/webui/side_panel/commerce/shopping_insights_app_test.ts
@@ -5,13 +5,15 @@
 import 'chrome://shopping-insights-side-panel.top-chrome/app.js';
 
 import {BrowserProxyImpl} from 'chrome://resources/cr_components/commerce/browser_proxy.js';
-import {PageCallbackRouter, PriceInsightsInfo, PriceInsightsInfo_PriceBucket, ProductInfo} from 'chrome://resources/cr_components/commerce/shopping_service.mojom-webui.js';
+import type {PriceInsightsInfo, ProductInfo} from 'chrome://resources/cr_components/commerce/shopping_service.mojom-webui.js';
+import {PageCallbackRouter, PriceInsightsInfo_PriceBucket} from 'chrome://resources/cr_components/commerce/shopping_service.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {stringToMojoString16} from 'chrome://resources/js/mojo_type_util.js';
-import {ShoppingInsightsAppElement} from 'chrome://shopping-insights-side-panel.top-chrome/app.js';
-import {PriceTrackingSection} from 'chrome://shopping-insights-side-panel.top-chrome/price_tracking_section.js';
+import type {ShoppingInsightsAppElement} from 'chrome://shopping-insights-side-panel.top-chrome/app.js';
+import type {PriceTrackingSection} from 'chrome://shopping-insights-side-panel.top-chrome/price_tracking_section.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {TestMock} from 'chrome://webui-test/test_mock.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/app_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/app_test.ts
index 832af9b..09f1013 100644
--- a/chrome/test/data/webui/side_panel/customize_chrome/app_test.ts
+++ b/chrome/test/data/webui/side_panel/customize_chrome/app_test.ts
@@ -4,12 +4,13 @@
 
 import 'chrome://customize-chrome-side-panel.top-chrome/app.js';
 
-import {AppElement} from 'chrome://customize-chrome-side-panel.top-chrome/app.js';
-import {BackgroundCollection, CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote, CustomizeChromePageRemote, CustomizeChromeSection} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
+import type {AppElement} from 'chrome://customize-chrome-side-panel.top-chrome/app.js';
+import type {BackgroundCollection, CustomizeChromePageRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
+import {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote, CustomizeChromeSection} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
 import {CustomizeChromeApiProxy} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome_api_proxy.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertGE, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 
 import {installMock} from './test_support.js';
 
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/appearance_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/appearance_test.ts
index 8ba2c9f2..52af419c 100644
--- a/chrome/test/data/webui/side_panel/customize_chrome/appearance_test.ts
+++ b/chrome/test/data/webui/side_panel/customize_chrome/appearance_test.ts
@@ -4,16 +4,18 @@
 
 import 'chrome://customize-chrome-side-panel.top-chrome/appearance.js';
 
-import {AppearanceElement} from 'chrome://customize-chrome-side-panel.top-chrome/appearance.js';
+import type {AppearanceElement} from 'chrome://customize-chrome-side-panel.top-chrome/appearance.js';
 import {CustomizeChromeAction} from 'chrome://customize-chrome-side-panel.top-chrome/common.js';
-import {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote, CustomizeChromePageRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
+import type {CustomizeChromePageRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
+import {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
 import {CustomizeChromeApiProxy} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome_api_proxy.js';
-import {ManagedDialogElement} from 'chrome://resources/cr_components/managed_dialog/managed_dialog.js';
+import type {ManagedDialogElement} from 'chrome://resources/cr_components/managed_dialog/managed_dialog.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {$$, assertNotStyle, assertStyle, createBackgroundImage, createTheme, createThirdPartyThemeInfo, installMock} from './test_support.js';
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/button_label_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/button_label_test.ts
index 93e7500d..f29b669 100644
--- a/chrome/test/data/webui/side_panel/customize_chrome/button_label_test.ts
+++ b/chrome/test/data/webui/side_panel/customize_chrome/button_label_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://customize-chrome-side-panel.top-chrome/button_label.js';
 
-import {ButtonLabelElement} from 'chrome://customize-chrome-side-panel.top-chrome/button_label.js';
+import type {ButtonLabelElement} from 'chrome://customize-chrome-side-panel.top-chrome/button_label.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 
 import {assertNotStyle, assertStyle} from './test_support.js';
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/cards_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/cards_test.ts
index 097d56f..1d1d95b 100644
--- a/chrome/test/data/webui/side_panel/customize_chrome/cards_test.ts
+++ b/chrome/test/data/webui/side_panel/customize_chrome/cards_test.ts
@@ -4,19 +4,21 @@
 
 import 'chrome://customize-chrome-side-panel.top-chrome/cards.js';
 
-import {CardsElement} from 'chrome://customize-chrome-side-panel.top-chrome/cards.js';
+import type {CardsElement} from 'chrome://customize-chrome-side-panel.top-chrome/cards.js';
 import {CartHandlerRemote} from 'chrome://customize-chrome-side-panel.top-chrome/chrome_cart.mojom-webui.js';
 import {ChromeCartProxy} from 'chrome://customize-chrome-side-panel.top-chrome/chrome_cart_proxy.js';
-import {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote, CustomizeChromePageRemote, ModuleSettings} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
+import type {CustomizeChromePageRemote, ModuleSettings} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
+import {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
 import {CustomizeChromeApiProxy} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome_api_proxy.js';
-import {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
-import {CrToggleElement} from 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
+import type {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
+import type {CrToggleElement} from 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {IronCollapseElement} from 'chrome://resources/polymer/v3_0/iron-collapse/iron-collapse.js';
+import type {IronCollapseElement} from 'chrome://resources/polymer/v3_0/iron-collapse/iron-collapse.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
 
 import {assertNotStyle, assertStyle, installMock} from './test_support.js';
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 ea10cc0..f63c3ea3 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
@@ -4,16 +4,19 @@
 
 import 'chrome://customize-chrome-side-panel.top-chrome/categories.js';
 
-import {CategoriesElement, CHANGE_CHROME_THEME_CLASSIC_ELEMENT_ID, CHROME_THEME_COLLECTION_ELEMENT_ID} from 'chrome://customize-chrome-side-panel.top-chrome/categories.js';
+import type {CategoriesElement} from 'chrome://customize-chrome-side-panel.top-chrome/categories.js';
+import {CHANGE_CHROME_THEME_CLASSIC_ELEMENT_ID, CHROME_THEME_COLLECTION_ELEMENT_ID} from 'chrome://customize-chrome-side-panel.top-chrome/categories.js';
 import {CustomizeChromeAction} from 'chrome://customize-chrome-side-panel.top-chrome/common.js';
-import {BackgroundCollection, CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote, CustomizeChromePageRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
+import type {BackgroundCollection, CustomizeChromePageRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
+import {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
 import {CustomizeChromeApiProxy} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome_api_proxy.js';
 import {WindowProxy} from 'chrome://customize-chrome-side-panel.top-chrome/window_proxy.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertDeepEquals, assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {$$, createBackgroundImage, createTheme, installMock} from './test_support.js';
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/chrome_colors_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/chrome_colors_test.ts
index 3be1d31..89e4edbd 100644
--- a/chrome/test/data/webui/side_panel/customize_chrome/chrome_colors_test.ts
+++ b/chrome/test/data/webui/side_panel/customize_chrome/chrome_colors_test.ts
@@ -4,14 +4,15 @@
 
 import 'chrome://customize-chrome-side-panel.top-chrome/chrome_colors.js';
 
-import {ChromeColorsElement} from 'chrome://customize-chrome-side-panel.top-chrome/chrome_colors.js';
+import type {ChromeColorsElement} from 'chrome://customize-chrome-side-panel.top-chrome/chrome_colors.js';
 import {ThemeColorPickerBrowserProxy} from 'chrome://resources/cr_components/theme_color_picker/browser_proxy.js';
-import {ThemeColorElement} from 'chrome://resources/cr_components/theme_color_picker/theme_color.js';
-import {ChromeColor, Theme, ThemeColorPickerClientCallbackRouter, ThemeColorPickerClientRemote, ThemeColorPickerHandlerRemote} from 'chrome://resources/cr_components/theme_color_picker/theme_color_picker.mojom-webui.js';
+import type {ThemeColorElement} from 'chrome://resources/cr_components/theme_color_picker/theme_color.js';
+import type {ChromeColor, Theme, ThemeColorPickerClientRemote} from 'chrome://resources/cr_components/theme_color_picker/theme_color_picker.mojom-webui.js';
+import {ThemeColorPickerClientCallbackRouter, ThemeColorPickerHandlerRemote} from 'chrome://resources/cr_components/theme_color_picker/theme_color_picker.mojom-webui.js';
 import {BrowserColorVariant} from 'chrome://resources/mojo/ui/base/mojom/themes.mojom-webui.js';
 import {assertDeepEquals, assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {installMock} from './test_support.js';
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/hover_button_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/hover_button_test.ts
index 212e3f5..c79ba2d 100644
--- a/chrome/test/data/webui/side_panel/customize_chrome/hover_button_test.ts
+++ b/chrome/test/data/webui/side_panel/customize_chrome/hover_button_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://customize-chrome-side-panel.top-chrome/hover_button.js';
 
-import {HoverButtonElement} from 'chrome://customize-chrome-side-panel.top-chrome/hover_button.js';
+import type {HoverButtonElement} from 'chrome://customize-chrome-side-panel.top-chrome/hover_button.js';
 import {listenOnce} from 'chrome://resources/js/util.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/shortcuts_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/shortcuts_test.ts
index f6bce32..7dbb103 100644
--- a/chrome/test/data/webui/side_panel/customize_chrome/shortcuts_test.ts
+++ b/chrome/test/data/webui/side_panel/customize_chrome/shortcuts_test.ts
@@ -4,11 +4,12 @@
 
 import 'chrome://customize-chrome-side-panel.top-chrome/shortcuts.js';
 
-import {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote, CustomizeChromePageRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
+import type {CustomizeChromePageRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
+import {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
 import {CustomizeChromeApiProxy} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome_api_proxy.js';
-import {ShortcutsElement} from 'chrome://customize-chrome-side-panel.top-chrome/shortcuts.js';
+import type {ShortcutsElement} from 'chrome://customize-chrome-side-panel.top-chrome/shortcuts.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 
 import {installMock} from './test_support.js';
 
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/test_support.ts b/chrome/test/data/webui/side_panel/customize_chrome/test_support.ts
index cf6e74a..6f329c0 100644
--- a/chrome/test/data/webui/side_panel/customize_chrome/test_support.ts
+++ b/chrome/test/data/webui/side_panel/customize_chrome/test_support.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BackgroundImage, Theme, ThirdPartyThemeInfo} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
+import type {BackgroundImage, Theme, ThirdPartyThemeInfo} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
 import {assertEquals, assertNotEquals} from 'chrome://webui-test/chai_assert.js';
 import {TestMock} from 'chrome://webui-test/test_mock.js';
 
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 91e854b8..cc2fd79 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
@@ -4,11 +4,13 @@
 
 import 'chrome://customize-chrome-side-panel.top-chrome/theme_snapshot.js';
 
-import {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote, CustomizeChromePageRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
+import type {CustomizeChromePageRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
+import {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
 import {CustomizeChromeApiProxy} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome_api_proxy.js';
-import {CustomizeThemeType, ThemeSnapshotElement} from 'chrome://customize-chrome-side-panel.top-chrome/theme_snapshot.js';
+import type {ThemeSnapshotElement} from 'chrome://customize-chrome-side-panel.top-chrome/theme_snapshot.js';
+import {CustomizeThemeType} from 'chrome://customize-chrome-side-panel.top-chrome/theme_snapshot.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 
 import {$$, assertStyle, createBackgroundImage, createTheme, installMock} from './test_support.js';
 
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/themes_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/themes_test.ts
index 0ed8dff..72cf9c7 100644
--- a/chrome/test/data/webui/side_panel/customize_chrome/themes_test.ts
+++ b/chrome/test/data/webui/side_panel/customize_chrome/themes_test.ts
@@ -5,14 +5,17 @@
 import 'chrome://customize-chrome-side-panel.top-chrome/themes.js';
 
 import {CustomizeChromeAction} from 'chrome://customize-chrome-side-panel.top-chrome/common.js';
-import {BackgroundCollection, CollectionImage, CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote, CustomizeChromePageRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
+import type {BackgroundCollection, CollectionImage, CustomizeChromePageRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
+import {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
 import {CustomizeChromeApiProxy} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome_api_proxy.js';
-import {CHROME_THEME_BACK_ELEMENT_ID, CHROME_THEME_ELEMENT_ID, ThemesElement} from 'chrome://customize-chrome-side-panel.top-chrome/themes.js';
+import type {ThemesElement} from 'chrome://customize-chrome-side-panel.top-chrome/themes.js';
+import {CHROME_THEME_BACK_ELEMENT_ID, CHROME_THEME_ELEMENT_ID} from 'chrome://customize-chrome-side-panel.top-chrome/themes.js';
 import {WindowProxy} from 'chrome://customize-chrome-side-panel.top-chrome/window_proxy.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {createBackgroundImage, createTheme, installMock} from './test_support.js';
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/wallpaper_search/combobox_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/wallpaper_search/combobox_test.ts
index a44be42ad..a47670ce 100644
--- a/chrome/test/data/webui/side_panel/customize_chrome/wallpaper_search/combobox_test.ts
+++ b/chrome/test/data/webui/side_panel/customize_chrome/wallpaper_search/combobox_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://customize-chrome-side-panel.top-chrome/wallpaper_search/combobox/customize_chrome_combobox.js';
 
-import {CustomizeChromeCombobox} from 'chrome://customize-chrome-side-panel.top-chrome/wallpaper_search/combobox/customize_chrome_combobox.js';
+import type {CustomizeChromeCombobox} from 'chrome://customize-chrome-side-panel.top-chrome/wallpaper_search/combobox/customize_chrome_combobox.js';
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/wallpaper_search/wallpaper_search_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/wallpaper_search/wallpaper_search_test.ts
index 5ed3959..a5c52a0 100644
--- a/chrome/test/data/webui/side_panel/customize_chrome/wallpaper_search/wallpaper_search_test.ts
+++ b/chrome/test/data/webui/side_panel/customize_chrome/wallpaper_search/wallpaper_search_test.ts
@@ -6,24 +6,27 @@
 import 'chrome://customize-chrome-side-panel.top-chrome/strings.m.js';
 
 import {CustomizeChromeAction} from 'chrome://customize-chrome-side-panel.top-chrome/common.js';
-import {CustomizeChromePageRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
+import type {CustomizeChromePageRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
 import {CustomizeChromeApiProxy} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome_api_proxy.js';
-import {DescriptorDName, Descriptors, InspirationGroup, ResultDescriptors, UserFeedback, WallpaperSearchClientCallbackRouter, WallpaperSearchClientRemote, WallpaperSearchHandlerInterface, WallpaperSearchHandlerRemote, WallpaperSearchStatus} from 'chrome://customize-chrome-side-panel.top-chrome/wallpaper_search.mojom-webui.js';
-import {CustomizeChromeCombobox} from 'chrome://customize-chrome-side-panel.top-chrome/wallpaper_search/combobox/customize_chrome_combobox.js';
-import {DESCRIPTOR_D_VALUE, WallpaperSearchElement} from 'chrome://customize-chrome-side-panel.top-chrome/wallpaper_search/wallpaper_search.js';
+import type {Descriptors, InspirationGroup, ResultDescriptors, WallpaperSearchClientRemote, WallpaperSearchHandlerInterface} from 'chrome://customize-chrome-side-panel.top-chrome/wallpaper_search.mojom-webui.js';
+import {DescriptorDName, UserFeedback, WallpaperSearchClientCallbackRouter, WallpaperSearchHandlerRemote, WallpaperSearchStatus} from 'chrome://customize-chrome-side-panel.top-chrome/wallpaper_search.mojom-webui.js';
+import type {CustomizeChromeCombobox} from 'chrome://customize-chrome-side-panel.top-chrome/wallpaper_search/combobox/customize_chrome_combobox.js';
+import type {WallpaperSearchElement} from 'chrome://customize-chrome-side-panel.top-chrome/wallpaper_search/wallpaper_search.js';
+import {DESCRIPTOR_D_VALUE} from 'chrome://customize-chrome-side-panel.top-chrome/wallpaper_search/wallpaper_search.js';
 import {WallpaperSearchProxy} from 'chrome://customize-chrome-side-panel.top-chrome/wallpaper_search/wallpaper_search_proxy.js';
 import {WindowProxy} from 'chrome://customize-chrome-side-panel.top-chrome/window_proxy.js';
-import {CrAutoImgElement} from 'chrome://resources/cr_elements/cr_auto_img/cr_auto_img.js';
+import type {CrAutoImgElement} from 'chrome://resources/cr_elements/cr_auto_img/cr_auto_img.js';
 import {CrFeedbackOption} from 'chrome://resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.js';
-import {CrIconButtonElement} from 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
+import type {CrIconButtonElement} from 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
 import {hexColorToSkColor} from 'chrome://resources/js/color_utils.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
-import {IronCollapseElement} from 'chrome://resources/polymer/v3_0/iron-collapse/iron-collapse.js';
+import type {IronCollapseElement} from 'chrome://resources/polymer/v3_0/iron-collapse/iron-collapse.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertGE, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
+import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
-import {TestMock} from 'chrome://webui-test/test_mock.js';
+import type {TestMock} from 'chrome://webui-test/test_mock.js';
 import {eventToPromise, isVisible, whenCheck} from 'chrome://webui-test/test_util.js';
 
 import {$$, assertNotStyle, assertStyle, createBackgroundImage, createTheme, installMock} from '../test_support.js';
diff --git a/chrome/test/data/webui/side_panel/performance_controls/app_test.ts b/chrome/test/data/webui/side_panel/performance_controls/app_test.ts
index 58e6b32..5b9fddf2 100644
--- a/chrome/test/data/webui/side_panel/performance_controls/app_test.ts
+++ b/chrome/test/data/webui/side_panel/performance_controls/app_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://performance-side-panel.top-chrome/app.js';
 
-import {PerformanceAppElement} from 'chrome://performance-side-panel.top-chrome/app.js';
+import type {PerformanceAppElement} from 'chrome://performance-side-panel.top-chrome/app.js';
 import {PerformanceSidePanelNotification} from 'chrome://performance-side-panel.top-chrome/performance.mojom-webui.js';
 import {PerformancePageApiProxyImpl} from 'chrome://performance-side-panel.top-chrome/performance_page_api_proxy.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
diff --git a/chrome/test/data/webui/side_panel/performance_controls/battery_saver_card_api_proxy_test.ts b/chrome/test/data/webui/side_panel/performance_controls/battery_saver_card_api_proxy_test.ts
index e62780f..6bf9d76b 100644
--- a/chrome/test/data/webui/side_panel/performance_controls/battery_saver_card_api_proxy_test.ts
+++ b/chrome/test/data/webui/side_panel/performance_controls/battery_saver_card_api_proxy_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://performance-side-panel.top-chrome/app.js';
 
-import {BatterySaverCardApiProxy, BatterySaverCardApiProxyImpl} from 'chrome://performance-side-panel.top-chrome/battery_saver_card_api_proxy.js';
+import type {BatterySaverCardApiProxy} from 'chrome://performance-side-panel.top-chrome/battery_saver_card_api_proxy.js';
+import {BatterySaverCardApiProxyImpl} from 'chrome://performance-side-panel.top-chrome/battery_saver_card_api_proxy.js';
 import {BatterySaverCardCallbackRouter, BatterySaverCardHandlerRemote} from 'chrome://performance-side-panel.top-chrome/performance.mojom-webui.js';
 import {assertNotEquals} from 'chrome://webui-test/chai_assert.js';
 import {TestMock} from 'chrome://webui-test/test_mock.js';
diff --git a/chrome/test/data/webui/side_panel/performance_controls/battery_saver_card_test.ts b/chrome/test/data/webui/side_panel/performance_controls/battery_saver_card_test.ts
index 0358525..be6a2c9 100644
--- a/chrome/test/data/webui/side_panel/performance_controls/battery_saver_card_test.ts
+++ b/chrome/test/data/webui/side_panel/performance_controls/battery_saver_card_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://performance-side-panel.top-chrome/battery_saver_card.js';
 
-import {BatterySaverCardElement} from 'chrome://performance-side-panel.top-chrome/battery_saver_card.js';
+import type {BatterySaverCardElement} from 'chrome://performance-side-panel.top-chrome/battery_saver_card.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 suite('Battery saver card', () => {
diff --git a/chrome/test/data/webui/side_panel/performance_controls/browser_health_card_test.ts b/chrome/test/data/webui/side_panel/performance_controls/browser_health_card_test.ts
index 1f38f767..4296eea 100644
--- a/chrome/test/data/webui/side_panel/performance_controls/browser_health_card_test.ts
+++ b/chrome/test/data/webui/side_panel/performance_controls/browser_health_card_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://performance-side-panel.top-chrome/browser_health_card.js';
 
-import {BrowserHealthCardElement} from 'chrome://performance-side-panel.top-chrome/browser_health_card.js';
+import type {BrowserHealthCardElement} from 'chrome://performance-side-panel.top-chrome/browser_health_card.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/side_panel/performance_controls/memory_saver_card_api_proxy_test.ts b/chrome/test/data/webui/side_panel/performance_controls/memory_saver_card_api_proxy_test.ts
index a6553f4..e80fbfb 100644
--- a/chrome/test/data/webui/side_panel/performance_controls/memory_saver_card_api_proxy_test.ts
+++ b/chrome/test/data/webui/side_panel/performance_controls/memory_saver_card_api_proxy_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://performance-side-panel.top-chrome/app.js';
 
-import {MemorySaverCardApiProxy, MemorySaverCardApiProxyImpl} from 'chrome://performance-side-panel.top-chrome/memory_saver_card_api_proxy.js';
+import type {MemorySaverCardApiProxy} from 'chrome://performance-side-panel.top-chrome/memory_saver_card_api_proxy.js';
+import {MemorySaverCardApiProxyImpl} from 'chrome://performance-side-panel.top-chrome/memory_saver_card_api_proxy.js';
 import {MemorySaverCardCallbackRouter, MemorySaverCardHandlerRemote} from 'chrome://performance-side-panel.top-chrome/performance.mojom-webui.js';
 import {assertNotEquals} from 'chrome://webui-test/chai_assert.js';
 import {TestMock} from 'chrome://webui-test/test_mock.js';
diff --git a/chrome/test/data/webui/side_panel/performance_controls/memory_saver_card_test.ts b/chrome/test/data/webui/side_panel/performance_controls/memory_saver_card_test.ts
index fd06575..2ab53d6 100644
--- a/chrome/test/data/webui/side_panel/performance_controls/memory_saver_card_test.ts
+++ b/chrome/test/data/webui/side_panel/performance_controls/memory_saver_card_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://performance-side-panel.top-chrome/memory_saver_card.js';
 
-import {MemorySaverCardElement} from 'chrome://performance-side-panel.top-chrome/memory_saver_card.js';
+import type {MemorySaverCardElement} from 'chrome://performance-side-panel.top-chrome/memory_saver_card.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 suite('memory saver card', () => {
diff --git a/chrome/test/data/webui/side_panel/performance_controls/performance_page_api_proxy_test.ts b/chrome/test/data/webui/side_panel/performance_controls/performance_page_api_proxy_test.ts
index 4074d6c0..bfa2742f 100644
--- a/chrome/test/data/webui/side_panel/performance_controls/performance_page_api_proxy_test.ts
+++ b/chrome/test/data/webui/side_panel/performance_controls/performance_page_api_proxy_test.ts
@@ -5,7 +5,8 @@
 import 'chrome://performance-side-panel.top-chrome/app.js';
 
 import {PerformancePageCallbackRouter, PerformancePageHandlerRemote} from 'chrome://performance-side-panel.top-chrome/performance.mojom-webui.js';
-import {PerformancePageApiProxy, PerformancePageApiProxyImpl} from 'chrome://performance-side-panel.top-chrome/performance_page_api_proxy.js';
+import type {PerformancePageApiProxy} from 'chrome://performance-side-panel.top-chrome/performance_page_api_proxy.js';
+import {PerformancePageApiProxyImpl} from 'chrome://performance-side-panel.top-chrome/performance_page_api_proxy.js';
 import {assertEquals, assertNotEquals} from 'chrome://webui-test/chai_assert.js';
 import {TestMock} from 'chrome://webui-test/test_mock.js';
 
diff --git a/chrome/test/data/webui/side_panel/performance_controls/test_battery_saver_card_api_proxy.ts b/chrome/test/data/webui/side_panel/performance_controls/test_battery_saver_card_api_proxy.ts
index 86dabe6..15b28b5 100644
--- a/chrome/test/data/webui/side_panel/performance_controls/test_battery_saver_card_api_proxy.ts
+++ b/chrome/test/data/webui/side_panel/performance_controls/test_battery_saver_card_api_proxy.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BatterySaverCardApiProxy} from 'chrome://performance-side-panel.top-chrome/battery_saver_card_api_proxy';
+import type {BatterySaverCardApiProxy} from 'chrome://performance-side-panel.top-chrome/battery_saver_card_api_proxy';
 import {BatterySaverCardCallbackRouter} from 'chrome://performance-side-panel.top-chrome/performance.mojom-webui.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/side_panel/performance_controls/test_memory_saver_card_api_proxy.ts b/chrome/test/data/webui/side_panel/performance_controls/test_memory_saver_card_api_proxy.ts
index db3ba63..e9453b4 100644
--- a/chrome/test/data/webui/side_panel/performance_controls/test_memory_saver_card_api_proxy.ts
+++ b/chrome/test/data/webui/side_panel/performance_controls/test_memory_saver_card_api_proxy.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {MemorySaverCardApiProxy} from 'chrome://performance-side-panel.top-chrome/memory_saver_card_api_proxy';
+import type {MemorySaverCardApiProxy} from 'chrome://performance-side-panel.top-chrome/memory_saver_card_api_proxy';
 import {MemorySaverCardCallbackRouter} from 'chrome://performance-side-panel.top-chrome/performance.mojom-webui.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/side_panel/performance_controls/test_performance_page_api_proxy.ts b/chrome/test/data/webui/side_panel/performance_controls/test_performance_page_api_proxy.ts
index efc7957..75ede46f 100644
--- a/chrome/test/data/webui/side_panel/performance_controls/test_performance_page_api_proxy.ts
+++ b/chrome/test/data/webui/side_panel/performance_controls/test_performance_page_api_proxy.ts
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {PerformancePageCallbackRouter, PerformancePageHandlerRemote, PerformancePageRemote} from 'chrome://performance-side-panel.top-chrome/performance.mojom-webui.js';
-import {PerformancePageApiProxy} from 'chrome://performance-side-panel.top-chrome/performance_page_api_proxy';
+import type {PerformancePageRemote} from 'chrome://performance-side-panel.top-chrome/performance.mojom-webui.js';
+import {PerformancePageCallbackRouter, PerformancePageHandlerRemote} from 'chrome://performance-side-panel.top-chrome/performance.mojom-webui.js';
+import type {PerformancePageApiProxy} from 'chrome://performance-side-panel.top-chrome/performance_page_api_proxy';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 import {TestMock} from 'chrome://webui-test/test_mock.js';
 
diff --git a/chrome/test/data/webui/side_panel/read_anything/checkmark_visible_on_selected.ts b/chrome/test/data/webui/side_panel/read_anything/checkmark_visible_on_selected.ts
index 59ac4a4..92edaba 100644
--- a/chrome/test/data/webui/side_panel/read_anything/checkmark_visible_on_selected.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/checkmark_visible_on_selected.ts
@@ -4,7 +4,7 @@
 
 import 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything_toolbar.js';
 
-import {ReadAnythingToolbarElement} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything_toolbar.js';
+import type {ReadAnythingToolbarElement} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything_toolbar.js';
 import {assertEquals} from 'chrome-untrusted://webui-test/chai_assert.js';
 
 import {FakeReadingMode} from './fake_reading_mode.js';
diff --git a/chrome/test/data/webui/side_panel/reading_list/reading_list_app_test.ts b/chrome/test/data/webui/side_panel/reading_list/reading_list_app_test.ts
index cd44ffe3..a91e46d 100644
--- a/chrome/test/data/webui/side_panel/reading_list/reading_list_app_test.ts
+++ b/chrome/test/data/webui/side_panel/reading_list/reading_list_app_test.ts
@@ -4,10 +4,10 @@
 
 import 'chrome://read-later.top-chrome/app.js';
 
-import {ReadingListAppElement} from 'chrome://read-later.top-chrome/app.js';
-import {ReadLaterEntriesByStatus} from 'chrome://read-later.top-chrome/reading_list.mojom-webui.js';
+import type {ReadingListAppElement} from 'chrome://read-later.top-chrome/app.js';
+import type {ReadLaterEntriesByStatus} from 'chrome://read-later.top-chrome/reading_list.mojom-webui.js';
 import {ReadingListApiProxyImpl} from 'chrome://read-later.top-chrome/reading_list_api_proxy.js';
-import {ReadingListItemElement} from 'chrome://read-later.top-chrome/reading_list_item.js';
+import type {ReadingListItemElement} from 'chrome://read-later.top-chrome/reading_list_item.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/side_panel/reading_list/test_reading_list_api_proxy.ts b/chrome/test/data/webui/side_panel/reading_list/test_reading_list_api_proxy.ts
index fcc691c..5be0c6b 100644
--- a/chrome/test/data/webui/side_panel/reading_list/test_reading_list_api_proxy.ts
+++ b/chrome/test/data/webui/side_panel/reading_list/test_reading_list_api_proxy.ts
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {PageCallbackRouter, ReadLaterEntriesByStatus} from 'chrome://read-later.top-chrome/reading_list.mojom-webui.js';
-import {ReadingListApiProxy} from 'chrome://read-later.top-chrome/reading_list_api_proxy.js';
-import {ClickModifiers} from 'chrome://resources/mojo/ui/base/mojom/window_open_disposition.mojom-webui.js';
-import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
+import type {ReadLaterEntriesByStatus} from 'chrome://read-later.top-chrome/reading_list.mojom-webui.js';
+import {PageCallbackRouter} from 'chrome://read-later.top-chrome/reading_list.mojom-webui.js';
+import type {ReadingListApiProxy} from 'chrome://read-later.top-chrome/reading_list_api_proxy.js';
+import type {ClickModifiers} from 'chrome://resources/mojo/ui/base/mojom/window_open_disposition.mojom-webui.js';
+import type {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export class TestReadingListApiProxy extends TestBrowserProxy implements
diff --git a/chrome/test/data/webui/side_panel/user_notes/app_test.ts b/chrome/test/data/webui/side_panel/user_notes/app_test.ts
index 73e11e0..65c5081 100644
--- a/chrome/test/data/webui/side_panel/user_notes/app_test.ts
+++ b/chrome/test/data/webui/side_panel/user_notes/app_test.ts
@@ -5,9 +5,9 @@
 import 'chrome://user-notes-side-panel.top-chrome/app.js';
 
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {UserNotesAppElement} from 'chrome://user-notes-side-panel.top-chrome/app.js';
-import {UserNoteElement} from 'chrome://user-notes-side-panel.top-chrome/user_note.js';
-import {Note, NoteOverview} from 'chrome://user-notes-side-panel.top-chrome/user_notes.mojom-webui.js';
+import type {UserNotesAppElement} from 'chrome://user-notes-side-panel.top-chrome/app.js';
+import type {UserNoteElement} from 'chrome://user-notes-side-panel.top-chrome/user_note.js';
+import type {Note, NoteOverview} from 'chrome://user-notes-side-panel.top-chrome/user_notes.mojom-webui.js';
 import {UserNotesApiProxyImpl} from 'chrome://user-notes-side-panel.top-chrome/user_notes_api_proxy.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/side_panel/user_notes/test_user_notes_api_proxy.ts b/chrome/test/data/webui/side_panel/user_notes/test_user_notes_api_proxy.ts
index acbcc04..f1b237e 100644
--- a/chrome/test/data/webui/side_panel/user_notes/test_user_notes_api_proxy.ts
+++ b/chrome/test/data/webui/side_panel/user_notes/test_user_notes_api_proxy.ts
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ClickModifiers} from 'chrome://resources/mojo/ui/base/mojom/window_open_disposition.mojom-webui.js';
-import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
-import {Note, NoteOverview, UserNotesPageCallbackRouter} from 'chrome://user-notes-side-panel.top-chrome/user_notes.mojom-webui.js';
-import {UserNotesApiProxy} from 'chrome://user-notes-side-panel.top-chrome/user_notes_api_proxy.js';
+import type {ClickModifiers} from 'chrome://resources/mojo/ui/base/mojom/window_open_disposition.mojom-webui.js';
+import type {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
+import type {Note, NoteOverview} from 'chrome://user-notes-side-panel.top-chrome/user_notes.mojom-webui.js';
+import {UserNotesPageCallbackRouter} from 'chrome://user-notes-side-panel.top-chrome/user_notes.mojom-webui.js';
+import type {UserNotesApiProxy} from 'chrome://user-notes-side-panel.top-chrome/user_notes_api_proxy.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export class TestUserNotesApiProxy extends TestBrowserProxy implements
diff --git a/chrome/test/data/webui/side_panel/user_notes/user_note_overviews_list_test.ts b/chrome/test/data/webui/side_panel/user_notes/user_note_overviews_list_test.ts
index 386b257..02102e9 100644
--- a/chrome/test/data/webui/side_panel/user_notes/user_note_overviews_list_test.ts
+++ b/chrome/test/data/webui/side_panel/user_notes/user_note_overviews_list_test.ts
@@ -5,9 +5,9 @@
 import 'chrome://user-notes-side-panel.top-chrome/user_note_overviews_list.js';
 
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {UserNoteOverviewRowElement} from 'chrome://user-notes-side-panel.top-chrome/user_note_overview_row.js';
-import {UserNoteOverviewsListElement} from 'chrome://user-notes-side-panel.top-chrome/user_note_overviews_list.js';
-import {NoteOverview} from 'chrome://user-notes-side-panel.top-chrome/user_notes.mojom-webui.js';
+import type {UserNoteOverviewRowElement} from 'chrome://user-notes-side-panel.top-chrome/user_note_overview_row.js';
+import type {UserNoteOverviewsListElement} from 'chrome://user-notes-side-panel.top-chrome/user_note_overviews_list.js';
+import type {NoteOverview} from 'chrome://user-notes-side-panel.top-chrome/user_notes.mojom-webui.js';
 import {UserNotesApiProxyImpl} from 'chrome://user-notes-side-panel.top-chrome/user_notes_api_proxy.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/side_panel/user_notes/user_notes_list_test.ts b/chrome/test/data/webui/side_panel/user_notes/user_notes_list_test.ts
index d27aafd..d3ccccc 100644
--- a/chrome/test/data/webui/side_panel/user_notes/user_notes_list_test.ts
+++ b/chrome/test/data/webui/side_panel/user_notes/user_notes_list_test.ts
@@ -4,10 +4,10 @@
 
 import 'chrome://user-notes-side-panel.top-chrome/user_notes_list.js';
 
-import {UserNoteElement} from 'chrome://user-notes-side-panel.top-chrome/user_note.js';
-import {Note} from 'chrome://user-notes-side-panel.top-chrome/user_notes.mojom-webui.js';
+import type {UserNoteElement} from 'chrome://user-notes-side-panel.top-chrome/user_note.js';
+import type {Note} from 'chrome://user-notes-side-panel.top-chrome/user_notes.mojom-webui.js';
 import {UserNotesApiProxyImpl} from 'chrome://user-notes-side-panel.top-chrome/user_notes_api_proxy.js';
-import {UserNotesListElement} from 'chrome://user-notes-side-panel.top-chrome/user_notes_list.js';
+import type {UserNotesListElement} from 'chrome://user-notes-side-panel.top-chrome/user_notes_list.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
diff --git a/chrome/test/data/webui/signin/account_selection_lacros_test.ts b/chrome/test/data/webui/signin/account_selection_lacros_test.ts
index 8b8914e2..fefac84 100644
--- a/chrome/test/data/webui/signin/account_selection_lacros_test.ts
+++ b/chrome/test/data/webui/signin/account_selection_lacros_test.ts
@@ -4,8 +4,9 @@
 
 import 'chrome://profile-picker/lazy_load.js';
 
-import {AccountSelectionLacrosElement} from 'chrome://profile-picker/lazy_load.js';
-import {AvailableAccount, ensureLazyLoaded, ManageProfilesBrowserProxyImpl} from 'chrome://profile-picker/profile_picker.js';
+import type {AccountSelectionLacrosElement} from 'chrome://profile-picker/lazy_load.js';
+import type {AvailableAccount} from 'chrome://profile-picker/profile_picker.js';
+import {ensureLazyLoaded, ManageProfilesBrowserProxyImpl} from 'chrome://profile-picker/profile_picker.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks, waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/signin/dice_web_signin_intercept_chrome_signin_test.ts b/chrome/test/data/webui/signin/dice_web_signin_intercept_chrome_signin_test.ts
index ea81ef3c..17ebae55 100644
--- a/chrome/test/data/webui/signin/dice_web_signin_intercept_chrome_signin_test.ts
+++ b/chrome/test/data/webui/signin/dice_web_signin_intercept_chrome_signin_test.ts
@@ -4,10 +4,11 @@
 
 import 'chrome://signin-dice-web-intercept/chrome_signin/chrome_signin_app.js';
 
-import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import type {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
-import {ChromeSigninAppElement} from 'chrome://signin-dice-web-intercept/chrome_signin/chrome_signin_app.js';
-import {ChromeSigninInterceptionParameters, DiceWebSigninInterceptBrowserProxyImpl} from 'chrome://signin-dice-web-intercept/dice_web_signin_intercept_browser_proxy.js';
+import type {ChromeSigninAppElement} from 'chrome://signin-dice-web-intercept/chrome_signin/chrome_signin_app.js';
+import type {ChromeSigninInterceptionParameters} from 'chrome://signin-dice-web-intercept/dice_web_signin_intercept_browser_proxy.js';
+import {DiceWebSigninInterceptBrowserProxyImpl} from 'chrome://signin-dice-web-intercept/dice_web_signin_intercept_browser_proxy.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 import {isChildVisible} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/signin/dice_web_signin_intercept_test.ts b/chrome/test/data/webui/signin/dice_web_signin_intercept_test.ts
index 3c69dba..a127cb6 100644
--- a/chrome/test/data/webui/signin/dice_web_signin_intercept_test.ts
+++ b/chrome/test/data/webui/signin/dice_web_signin_intercept_test.ts
@@ -5,8 +5,9 @@
 import 'chrome://signin-dice-web-intercept/dice_web_signin_intercept_app.js';
 
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
-import {DiceWebSigninInterceptAppElement} from 'chrome://signin-dice-web-intercept/dice_web_signin_intercept_app.js';
-import {DiceWebSigninInterceptBrowserProxyImpl, InterceptionParameters} from 'chrome://signin-dice-web-intercept/dice_web_signin_intercept_browser_proxy.js';
+import type {DiceWebSigninInterceptAppElement} from 'chrome://signin-dice-web-intercept/dice_web_signin_intercept_app.js';
+import type {InterceptionParameters} from 'chrome://signin-dice-web-intercept/dice_web_signin_intercept_browser_proxy.js';
+import {DiceWebSigninInterceptBrowserProxyImpl} from 'chrome://signin-dice-web-intercept/dice_web_signin_intercept_browser_proxy.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 import {isChildVisible, isVisible} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/signin/managed_user_profile_notice_test.ts b/chrome/test/data/webui/signin/managed_user_profile_notice_test.ts
index 5eb88c70..400c6e9 100644
--- a/chrome/test/data/webui/signin/managed_user_profile_notice_test.ts
+++ b/chrome/test/data/webui/signin/managed_user_profile_notice_test.ts
@@ -4,9 +4,10 @@
 
 import 'chrome://managed-user-profile-notice/managed_user_profile_notice_app.js';
 
-import {ManagedUserProfileNoticeAppElement} from 'chrome://managed-user-profile-notice/managed_user_profile_notice_app.js';
-import {ManagedUserProfileInfo, ManagedUserProfileNoticeBrowserProxyImpl} from 'chrome://managed-user-profile-notice/managed_user_profile_notice_browser_proxy.js';
-import {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
+import type {ManagedUserProfileNoticeAppElement} from 'chrome://managed-user-profile-notice/managed_user_profile_notice_app.js';
+import type {ManagedUserProfileInfo} from 'chrome://managed-user-profile-notice/managed_user_profile_notice_browser_proxy.js';
+import {ManagedUserProfileNoticeBrowserProxyImpl} from 'chrome://managed-user-profile-notice/managed_user_profile_notice_browser_proxy.js';
+import type {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/signin/profile_card_menu_test.ts b/chrome/test/data/webui/signin/profile_card_menu_test.ts
index 0a199a5e..800f717 100644
--- a/chrome/test/data/webui/signin/profile_card_menu_test.ts
+++ b/chrome/test/data/webui/signin/profile_card_menu_test.ts
@@ -4,13 +4,15 @@
 
 import 'chrome://profile-picker/profile_picker.js';
 
-import {ManageProfilesBrowserProxyImpl, ProfileCardMenuElement, ProfileState, Statistics, StatisticsResult} from 'chrome://profile-picker/profile_picker.js';
+import type {ProfileCardMenuElement, ProfileState, Statistics, StatisticsResult} from 'chrome://profile-picker/profile_picker.js';
+import {ManageProfilesBrowserProxyImpl} from 'chrome://profile-picker/profile_picker.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
-// <if expr="chromeos_lacros">
-import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
-// </if>
+import {
+  // <if expr="chromeos_lacros">
+  waitAfterNextRender,
+  // </if>
+  waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
 
 import {TestManageProfilesBrowserProxy} from './test_manage_profiles_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/signin/profile_customization_test.ts b/chrome/test/data/webui/signin/profile_customization_test.ts
index 4fb127c6..1bf3cab 100644
--- a/chrome/test/data/webui/signin/profile_customization_test.ts
+++ b/chrome/test/data/webui/signin/profile_customization_test.ts
@@ -4,10 +4,10 @@
 
 import 'chrome://profile-customization/profile_customization_app.js';
 
-import {ProfileCustomizationAppElement} from 'chrome://profile-customization/profile_customization_app.js';
+import type {ProfileCustomizationAppElement} from 'chrome://profile-customization/profile_customization_app.js';
 import {ProfileCustomizationBrowserProxyImpl} from 'chrome://profile-customization/profile_customization_browser_proxy.js';
-import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
-import {CrIconButtonElement} from 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
+import type {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import type {CrIconButtonElement} from 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/signin/profile_picker_app_test.ts b/chrome/test/data/webui/signin/profile_picker_app_test.ts
index a7cd102..5e7d1f5 100644
--- a/chrome/test/data/webui/signin/profile_picker_app_test.ts
+++ b/chrome/test/data/webui/signin/profile_picker_app_test.ts
@@ -5,18 +5,20 @@
 import 'chrome://profile-picker/profile_picker.js';
 // <if expr="chromeos_lacros">
 import 'chrome://profile-picker/lazy_load.js';
+
 // </if>
 
-// <if expr="chromeos_lacros">
-import {AvailableAccount} from 'chrome://profile-picker/profile_picker.js';
-// </if>
-
-import {ensureLazyLoaded, ManageProfilesBrowserProxyImpl, navigateTo, ProfilePickerAppElement, Routes} from 'chrome://profile-picker/profile_picker.js';
+import type {
+  // <if expr="chromeos_lacros">
+  AvailableAccount,
+  // </if>
+  ProfilePickerAppElement} from 'chrome://profile-picker/profile_picker.js';
+import {ensureLazyLoaded, ManageProfilesBrowserProxyImpl, navigateTo, Routes} from 'chrome://profile-picker/profile_picker.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {whenCheck} from 'chrome://webui-test/test_util.js';
 import {flushTasks, waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
+import {whenCheck} from 'chrome://webui-test/test_util.js';
 
 import {TestManageProfilesBrowserProxy} from './test_manage_profiles_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/signin/profile_picker_main_view_test.ts b/chrome/test/data/webui/signin/profile_picker_main_view_test.ts
index 845343f..ad05fbc 100644
--- a/chrome/test/data/webui/signin/profile_picker_main_view_test.ts
+++ b/chrome/test/data/webui/signin/profile_picker_main_view_test.ts
@@ -4,7 +4,8 @@
 
 import 'chrome://profile-picker/profile_picker.js';
 
-import {loadTimeData, ManageProfilesBrowserProxyImpl, NavigationMixin, ProfileCardElement, ProfilePickerMainViewElement, ProfileState, Routes} from 'chrome://profile-picker/profile_picker.js';
+import type {ProfileCardElement, ProfilePickerMainViewElement, ProfileState} from 'chrome://profile-picker/profile_picker.js';
+import {loadTimeData, ManageProfilesBrowserProxyImpl, NavigationMixin, Routes} from 'chrome://profile-picker/profile_picker.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/signin/profile_switch_test.ts b/chrome/test/data/webui/signin/profile_switch_test.ts
index d0b0399..eee65e7 100644
--- a/chrome/test/data/webui/signin/profile_switch_test.ts
+++ b/chrome/test/data/webui/signin/profile_switch_test.ts
@@ -4,8 +4,9 @@
 
 import 'chrome://profile-picker/lazy_load.js';
 
-import {ProfileSwitchElement} from 'chrome://profile-picker/lazy_load.js';
-import {ManageProfilesBrowserProxyImpl, ProfileState} from 'chrome://profile-picker/profile_picker.js';
+import type {ProfileSwitchElement} from 'chrome://profile-picker/lazy_load.js';
+import type {ProfileState} from 'chrome://profile-picker/profile_picker.js';
+import {ManageProfilesBrowserProxyImpl} from 'chrome://profile-picker/profile_picker.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/signin/profile_type_choice_test.ts b/chrome/test/data/webui/signin/profile_type_choice_test.ts
index 995af28..cf768f7 100644
--- a/chrome/test/data/webui/signin/profile_type_choice_test.ts
+++ b/chrome/test/data/webui/signin/profile_type_choice_test.ts
@@ -4,9 +4,8 @@
 
 import 'chrome://profile-picker/lazy_load.js';
 
-import {ProfileTypeChoiceElement} from 'chrome://profile-picker/lazy_load.js';
+import type {ProfileTypeChoiceElement} from 'chrome://profile-picker/lazy_load.js';
 import {ManageProfilesBrowserProxyImpl} from 'chrome://profile-picker/profile_picker.js';
-
 import {assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {isChildVisible} from 'chrome://webui-test/test_util.js';
 
diff --git a/chrome/test/data/webui/signin/signin_reauth_test.ts b/chrome/test/data/webui/signin/signin_reauth_test.ts
index a0fb916a..31a7d21 100644
--- a/chrome/test/data/webui/signin/signin_reauth_test.ts
+++ b/chrome/test/data/webui/signin/signin_reauth_test.ts
@@ -7,7 +7,7 @@
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {SigninReauthAppElement} from 'chrome://signin-reauth/signin_reauth_app.js';
+import type {SigninReauthAppElement} from 'chrome://signin-reauth/signin_reauth_app.js';
 import {SigninReauthBrowserProxyImpl} from 'chrome://signin-reauth/signin_reauth_browser_proxy.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/signin/sync_confirmation_test.ts b/chrome/test/data/webui/signin/sync_confirmation_test.ts
index c47eee2..eac05db2 100644
--- a/chrome/test/data/webui/signin/sync_confirmation_test.ts
+++ b/chrome/test/data/webui/signin/sync_confirmation_test.ts
@@ -5,7 +5,7 @@
 import 'chrome://sync-confirmation/sync_confirmation_app.js';
 
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {SyncConfirmationAppElement} from 'chrome://sync-confirmation/sync_confirmation_app.js';
+import type {SyncConfirmationAppElement} from 'chrome://sync-confirmation/sync_confirmation_app.js';
 import {SyncConfirmationBrowserProxyImpl} from 'chrome://sync-confirmation/sync_confirmation_browser_proxy.js';
 import {assertArrayEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/signin/test_dice_web_signin_intercept_browser_proxy.ts b/chrome/test/data/webui/signin/test_dice_web_signin_intercept_browser_proxy.ts
index 55d25dcd..c950770 100644
--- a/chrome/test/data/webui/signin/test_dice_web_signin_intercept_browser_proxy.ts
+++ b/chrome/test/data/webui/signin/test_dice_web_signin_intercept_browser_proxy.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ChromeSigninInterceptionParameters, DiceWebSigninInterceptBrowserProxy, InterceptionParameters} from 'chrome://signin-dice-web-intercept/dice_web_signin_intercept_browser_proxy.js';
+import type {ChromeSigninInterceptionParameters, DiceWebSigninInterceptBrowserProxy, InterceptionParameters} from 'chrome://signin-dice-web-intercept/dice_web_signin_intercept_browser_proxy.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export class TestDiceWebSigninInterceptBrowserProxy extends TestBrowserProxy
diff --git a/chrome/test/data/webui/signin/test_manage_profiles_browser_proxy.ts b/chrome/test/data/webui/signin/test_manage_profiles_browser_proxy.ts
index 2799f94..734b0ee1 100644
--- a/chrome/test/data/webui/signin/test_manage_profiles_browser_proxy.ts
+++ b/chrome/test/data/webui/signin/test_manage_profiles_browser_proxy.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {AutogeneratedThemeColorInfo, ManageProfilesBrowserProxy, ProfileState, UserThemeChoice} from 'chrome://profile-picker/profile_picker.js';
+import type {AutogeneratedThemeColorInfo, ManageProfilesBrowserProxy, ProfileState, UserThemeChoice} from 'chrome://profile-picker/profile_picker.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export class TestManageProfilesBrowserProxy extends TestBrowserProxy implements
diff --git a/chrome/test/data/webui/signin/test_managed_user_profile_notice_browser_proxy.ts b/chrome/test/data/webui/signin/test_managed_user_profile_notice_browser_proxy.ts
index a98f6ba0..c60c56d 100644
--- a/chrome/test/data/webui/signin/test_managed_user_profile_notice_browser_proxy.ts
+++ b/chrome/test/data/webui/signin/test_managed_user_profile_notice_browser_proxy.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ManagedUserProfileInfo, ManagedUserProfileNoticeBrowserProxy} from 'chrome://managed-user-profile-notice/managed_user_profile_notice_browser_proxy.js';
+import type {ManagedUserProfileInfo, ManagedUserProfileNoticeBrowserProxy} from 'chrome://managed-user-profile-notice/managed_user_profile_notice_browser_proxy.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export class TestManagedUserProfileNoticeBrowserProxy extends TestBrowserProxy
diff --git a/chrome/test/data/webui/signin/test_profile_customization_browser_proxy.ts b/chrome/test/data/webui/signin/test_profile_customization_browser_proxy.ts
index fb98641..6aeba78 100644
--- a/chrome/test/data/webui/signin/test_profile_customization_browser_proxy.ts
+++ b/chrome/test/data/webui/signin/test_profile_customization_browser_proxy.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ProfileCustomizationBrowserProxy, ProfileInfo} from 'chrome://profile-customization/profile_customization_browser_proxy.js';
+import type {ProfileCustomizationBrowserProxy, ProfileInfo} from 'chrome://profile-customization/profile_customization_browser_proxy.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export class TestProfileCustomizationBrowserProxy extends TestBrowserProxy
diff --git a/chrome/test/data/webui/signin/test_signin_reauth_browser_proxy.ts b/chrome/test/data/webui/signin/test_signin_reauth_browser_proxy.ts
index 851511b..6d66dee 100644
--- a/chrome/test/data/webui/signin/test_signin_reauth_browser_proxy.ts
+++ b/chrome/test/data/webui/signin/test_signin_reauth_browser_proxy.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {SigninReauthBrowserProxy} from 'chrome://signin-reauth/signin_reauth_browser_proxy.js';
+import type {SigninReauthBrowserProxy} from 'chrome://signin-reauth/signin_reauth_browser_proxy.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export class TestSigninReauthBrowserProxy extends TestBrowserProxy implements
diff --git a/chrome/test/data/webui/signin/test_sync_confirmation_browser_proxy.ts b/chrome/test/data/webui/signin/test_sync_confirmation_browser_proxy.ts
index e4369c1..9c92bc77 100644
--- a/chrome/test/data/webui/signin/test_sync_confirmation_browser_proxy.ts
+++ b/chrome/test/data/webui/signin/test_sync_confirmation_browser_proxy.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {SyncConfirmationBrowserProxy} from 'chrome://sync-confirmation/sync_confirmation_browser_proxy.js';
+import type {SyncConfirmationBrowserProxy} from 'chrome://sync-confirmation/sync_confirmation_browser_proxy.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export class TestSyncConfirmationBrowserProxy extends TestBrowserProxy
diff --git a/chrome/test/data/webui/support_tool/support_tool_test.ts b/chrome/test/data/webui/support_tool/support_tool_test.ts
index 50905a1..1ebe02d4 100644
--- a/chrome/test/data/webui/support_tool/support_tool_test.ts
+++ b/chrome/test/data/webui/support_tool/support_tool_test.ts
@@ -10,16 +10,18 @@
 import 'chrome://support-tool/support_tool.js';
 import 'chrome://support-tool/url_generator.js';
 
-import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
-import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
+import type {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import type {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {track} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {BrowserProxy, BrowserProxyImpl, DataCollectorItem, IssueDetails, PiiDataItem, SupportTokenGenerationResult} from 'chrome://support-tool/browser_proxy.js';
-import {ScreenshotElement} from 'chrome://support-tool/screenshot.js';
-import {DataExportResult, SupportToolElement, SupportToolPageIndex} from 'chrome://support-tool/support_tool.js';
-import {UrlGeneratorElement} from 'chrome://support-tool/url_generator.js';
+import type {BrowserProxy, DataCollectorItem, IssueDetails, PiiDataItem, SupportTokenGenerationResult} from 'chrome://support-tool/browser_proxy.js';
+import {BrowserProxyImpl} from 'chrome://support-tool/browser_proxy.js';
+import type {ScreenshotElement} from 'chrome://support-tool/screenshot.js';
+import type {DataExportResult, SupportToolElement} from 'chrome://support-tool/support_tool.js';
+import {SupportToolPageIndex} from 'chrome://support-tool/support_tool.js';
+import type {UrlGeneratorElement} from 'chrome://support-tool/url_generator.js';
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
diff --git a/chrome/test/data/webui/sync_internals/sync_internals_test.ts b/chrome/test/data/webui/sync_internals/sync_internals_test.ts
index 42320fd9..70ba449 100644
--- a/chrome/test/data/webui/sync_internals/sync_internals_test.ts
+++ b/chrome/test/data/webui/sync_internals/sync_internals_test.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {CrTreeElement} from 'chrome://resources/cr_elements/cr_tree/cr_tree.js';
+import type {CrTreeElement} from 'chrome://resources/cr_elements/cr_tree/cr_tree.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {getRequiredElement} from 'chrome://resources/js/util.js';
 import {getAboutInfoForTest} from 'chrome://sync-internals/about.js';
diff --git a/chrome/test/data/webui/tab_search/fuzzy_search_test.ts b/chrome/test/data/webui/tab_search/fuzzy_search_test.ts
index a8d6feb..66027dd 100644
--- a/chrome/test/data/webui/tab_search/fuzzy_search_test.ts
+++ b/chrome/test/data/webui/tab_search/fuzzy_search_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {fuzzySearch, FuzzySearchOptions, TabData, TabGroup, TabItemType} from 'chrome://tab-search.top-chrome/tab_search.js';
+import type {FuzzySearchOptions, TabGroup} from 'chrome://tab-search.top-chrome/tab_search.js';
+import {fuzzySearch, TabData, TabItemType} from 'chrome://tab-search.top-chrome/tab_search.js';
 import {assertDeepEquals, assertEquals} from 'chrome://webui-test/chai_assert.js';
 
 import {createTab, sampleToken} from './tab_search_test_data.js';
diff --git a/chrome/test/data/webui/tab_search/tab_organization_page_test.ts b/chrome/test/data/webui/tab_search/tab_organization_page_test.ts
index 805045f..0299818 100644
--- a/chrome/test/data/webui/tab_search/tab_organization_page_test.ts
+++ b/chrome/test/data/webui/tab_search/tab_organization_page_test.ts
@@ -5,7 +5,8 @@
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {mojoString16ToString, stringToMojoString16} from 'chrome://resources/js/mojo_type_util.js';
-import {SyncInfo, Tab, TabOrganizationError, TabOrganizationPageElement, TabOrganizationResultsElement, TabOrganizationSession, TabOrganizationState, TabSearchApiProxyImpl, TabSearchSyncBrowserProxyImpl} from 'chrome://tab-search.top-chrome/tab_search.js';
+import type {SyncInfo, Tab, TabOrganizationPageElement, TabOrganizationResultsElement, TabOrganizationSession} from 'chrome://tab-search.top-chrome/tab_search.js';
+import {TabOrganizationError, TabOrganizationState, TabSearchApiProxyImpl, TabSearchSyncBrowserProxyImpl} from 'chrome://tab-search.top-chrome/tab_search.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/tab_search/tab_search_app_test.ts b/chrome/test/data/webui/tab_search/tab_search_app_test.ts
index ec99e70e..3289947 100644
--- a/chrome/test/data/webui/tab_search/tab_search_app_test.ts
+++ b/chrome/test/data/webui/tab_search/tab_search_app_test.ts
@@ -3,7 +3,8 @@
 // found in the LICENSE file.
 
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {TabSearchApiProxyImpl, TabSearchAppElement} from 'chrome://tab-search.top-chrome/tab_search.js';
+import type {TabSearchAppElement} from 'chrome://tab-search.top-chrome/tab_search.js';
+import {TabSearchApiProxyImpl} from 'chrome://tab-search.top-chrome/tab_search.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/tab_search/tab_search_item_test.ts b/chrome/test/data/webui/tab_search/tab_search_item_test.ts
index 7db25d17..aa34688 100644
--- a/chrome/test/data/webui/tab_search/tab_search_item_test.ts
+++ b/chrome/test/data/webui/tab_search/tab_search_item_test.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {RecentlyClosedTab, Tab, TabAlertState, TabData, TabGroup, TabGroupColor, TabItemType, TabSearchItem} from 'chrome://tab-search.top-chrome/tab_search.js';
+import type {RecentlyClosedTab, Tab, TabGroup, TabSearchItem} from 'chrome://tab-search.top-chrome/tab_search.js';
+import {TabAlertState, TabData, TabGroupColor, TabItemType} from 'chrome://tab-search.top-chrome/tab_search.js';
 import {assertDeepEquals, assertEquals, assertNotEquals} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
diff --git a/chrome/test/data/webui/tab_search/tab_search_media_tabs_test.ts b/chrome/test/data/webui/tab_search/tab_search_media_tabs_test.ts
index b77a23b..19c3579 100644
--- a/chrome/test/data/webui/tab_search/tab_search_media_tabs_test.ts
+++ b/chrome/test/data/webui/tab_search/tab_search_media_tabs_test.ts
@@ -4,7 +4,8 @@
 
 import {MetricsReporterImpl} from 'chrome://resources/js/metrics_reporter/metrics_reporter.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
-import {ProfileData, Tab, TabAlertState, TabSearchApiProxyImpl, TabSearchPageElement} from 'chrome://tab-search.top-chrome/tab_search.js';
+import type {ProfileData, Tab, TabSearchPageElement} from 'chrome://tab-search.top-chrome/tab_search.js';
+import {TabAlertState, TabSearchApiProxyImpl} from 'chrome://tab-search.top-chrome/tab_search.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 import {MockedMetricsReporter} from 'chrome://webui-test/mocked_metrics_reporter.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/tab_search/tab_search_page_focus_test.ts b/chrome/test/data/webui/tab_search/tab_search_page_focus_test.ts
index 06c8088b..1fe1935 100644
--- a/chrome/test/data/webui/tab_search/tab_search_page_focus_test.ts
+++ b/chrome/test/data/webui/tab_search/tab_search_page_focus_test.ts
@@ -4,7 +4,8 @@
 
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
-import {InfiniteList, ProfileData, TabSearchApiProxyImpl, TabSearchItem, TabSearchPageElement} from 'chrome://tab-search.top-chrome/tab_search.js';
+import type {ProfileData, TabSearchPageElement} from 'chrome://tab-search.top-chrome/tab_search.js';
+import {InfiniteList, TabSearchApiProxyImpl, TabSearchItem} from 'chrome://tab-search.top-chrome/tab_search.js';
 import {assertEquals, assertGT, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 
diff --git a/chrome/test/data/webui/tab_search/tab_search_page_test.ts b/chrome/test/data/webui/tab_search/tab_search_page_test.ts
index 336a098..3206c63 100644
--- a/chrome/test/data/webui/tab_search/tab_search_page_test.ts
+++ b/chrome/test/data/webui/tab_search/tab_search_page_test.ts
@@ -4,7 +4,8 @@
 
 import {MetricsReporterImpl} from 'chrome://resources/js/metrics_reporter/metrics_reporter.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
-import {ProfileData, RecentlyClosedTab, Tab, TabGroupColor, TabSearchApiProxyImpl, TabSearchItem, TabSearchPageElement} from 'chrome://tab-search.top-chrome/tab_search.js';
+import type {ProfileData, RecentlyClosedTab, Tab, TabSearchItem, TabSearchPageElement} from 'chrome://tab-search.top-chrome/tab_search.js';
+import {TabGroupColor, TabSearchApiProxyImpl} from 'chrome://tab-search.top-chrome/tab_search.js';
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {MockedMetricsReporter} from 'chrome://webui-test/mocked_metrics_reporter.js';
 import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/tab_search/tab_search_test_data.ts b/chrome/test/data/webui/tab_search/tab_search_test_data.ts
index 3de6b34..6fbaf79 100644
--- a/chrome/test/data/webui/tab_search/tab_search_test_data.ts
+++ b/chrome/test/data/webui/tab_search/tab_search_test_data.ts
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Token} from 'chrome://resources/mojo/mojo/public/mojom/base/token.mojom-webui.js';
-import {ProfileData, RecentlyClosedTab, Tab, TabAlertState, Window} from 'chrome://tab-search.top-chrome/tab_search.js';
+import type {Token} from 'chrome://resources/mojo/mojo/public/mojom/base/token.mojom-webui.js';
+import type {ProfileData, RecentlyClosedTab, Tab, Window} from 'chrome://tab-search.top-chrome/tab_search.js';
+import {TabAlertState} from 'chrome://tab-search.top-chrome/tab_search.js';
 
 export const SAMPLE_WINDOW_HEIGHT: number = 448;
 
diff --git a/chrome/test/data/webui/tab_search/test_tab_search_api_proxy.ts b/chrome/test/data/webui/tab_search/test_tab_search_api_proxy.ts
index d517cc33..9609b68 100644
--- a/chrome/test/data/webui/tab_search/test_tab_search_api_proxy.ts
+++ b/chrome/test/data/webui/tab_search/test_tab_search_api_proxy.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {PageCallbackRouter, PageRemote, ProfileData, SwitchToTabInfo, Tab, TabOrganizationSession, TabSearchApiProxy, UserFeedback} from 'chrome://tab-search.top-chrome/tab_search.js';
+import type {PageRemote, ProfileData, SwitchToTabInfo, Tab, TabOrganizationSession, TabSearchApiProxy, UserFeedback} from 'chrome://tab-search.top-chrome/tab_search.js';
+import {PageCallbackRouter} from 'chrome://tab-search.top-chrome/tab_search.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export class TestTabSearchApiProxy extends TestBrowserProxy implements
diff --git a/chrome/test/data/webui/tab_search/test_tab_search_sync_browser_proxy.ts b/chrome/test/data/webui/tab_search/test_tab_search_sync_browser_proxy.ts
index 45e708e..1d545bf 100644
--- a/chrome/test/data/webui/tab_search/test_tab_search_sync_browser_proxy.ts
+++ b/chrome/test/data/webui/tab_search/test_tab_search_sync_browser_proxy.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {AccountInfo, SyncInfo, TabSearchSyncBrowserProxy} from 'chrome://tab-search.top-chrome/tab_search.js';
+import type {AccountInfo, SyncInfo, TabSearchSyncBrowserProxy} from 'chrome://tab-search.top-chrome/tab_search.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export class TestTabSearchSyncBrowserProxy extends TestBrowserProxy implements
diff --git a/chrome/test/data/webui/tab_strip/alert_indicator_test.ts b/chrome/test/data/webui/tab_strip/alert_indicator_test.ts
index 6553527..2807e750 100644
--- a/chrome/test/data/webui/tab_strip/alert_indicator_test.ts
+++ b/chrome/test/data/webui/tab_strip/alert_indicator_test.ts
@@ -4,8 +4,7 @@
 
 import 'chrome://tab-strip.top-chrome/alert_indicator.js';
 
-import {AlertIndicatorElement} from 'chrome://tab-strip.top-chrome/alert_indicator.js';
-
+import type {AlertIndicatorElement} from 'chrome://tab-strip.top-chrome/alert_indicator.js';
 import {assertEquals, assertFalse, assertNotEquals} from 'chrome://webui-test/chai_assert.js';
 
 suite('AlertIndicator', () => {
diff --git a/chrome/test/data/webui/tab_strip/alert_indicators_test.ts b/chrome/test/data/webui/tab_strip/alert_indicators_test.ts
index 9e51c46..10f2a50c5 100644
--- a/chrome/test/data/webui/tab_strip/alert_indicators_test.ts
+++ b/chrome/test/data/webui/tab_strip/alert_indicators_test.ts
@@ -4,9 +4,8 @@
 
 import 'chrome://tab-strip.top-chrome/alert_indicators.js';
 
-import {AlertIndicatorsElement} from 'chrome://tab-strip.top-chrome/alert_indicators.js';
+import type {AlertIndicatorsElement} from 'chrome://tab-strip.top-chrome/alert_indicators.js';
 import {TabAlertState} from 'chrome://tab-strip.top-chrome/tabs.mojom-webui.js';
-
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 
 suite('AlertIndicators', () => {
diff --git a/chrome/test/data/webui/tab_strip/drag_manager_test.ts b/chrome/test/data/webui/tab_strip/drag_manager_test.ts
index 2f831c95..fa89afff 100644
--- a/chrome/test/data/webui/tab_strip/drag_manager_test.ts
+++ b/chrome/test/data/webui/tab_strip/drag_manager_test.ts
@@ -1,11 +1,12 @@
 // Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-import {isChromeOS} from 'chrome://resources/js/platform.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {DragManager, DragManagerDelegate, PLACEHOLDER_GROUP_ID, PLACEHOLDER_TAB_ID} from 'chrome://tab-strip.top-chrome/drag_manager.js';
-import {TabElement} from 'chrome://tab-strip.top-chrome/tab.js';
-import {TabGroupElement} from 'chrome://tab-strip.top-chrome/tab_group.js';
+import {isChromeOS} from 'chrome://resources/js/platform.js';
+import type {DragManagerDelegate} from 'chrome://tab-strip.top-chrome/drag_manager.js';
+import {DragManager, PLACEHOLDER_GROUP_ID, PLACEHOLDER_TAB_ID} from 'chrome://tab-strip.top-chrome/drag_manager.js';
+import type {TabElement} from 'chrome://tab-strip.top-chrome/tab.js';
+import type {TabGroupElement} from 'chrome://tab-strip.top-chrome/tab_group.js';
 import {TabsApiProxyImpl} from 'chrome://tab-strip.top-chrome/tabs_api_proxy.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/tab_strip/tab_group_test.ts b/chrome/test/data/webui/tab_strip/tab_group_test.ts
index 6edd314..d7b1beb 100644
--- a/chrome/test/data/webui/tab_strip/tab_group_test.ts
+++ b/chrome/test/data/webui/tab_strip/tab_group_test.ts
@@ -5,10 +5,8 @@
 import 'chrome://tab-strip.top-chrome/tab.js';
 import 'chrome://tab-strip.top-chrome/tab_group.js';
 
-import {TabGroupElement} from 'chrome://tab-strip.top-chrome/tab_group.js';
-
+import type {TabGroupElement} from 'chrome://tab-strip.top-chrome/tab_group.js';
 import {TabsApiProxyImpl} from 'chrome://tab-strip.top-chrome/tabs_api_proxy.js';
-
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 
 import {TestTabsApiProxy} from './test_tabs_api_proxy.js';
diff --git a/chrome/test/data/webui/tab_strip/tab_list_test.ts b/chrome/test/data/webui/tab_strip/tab_list_test.ts
index 9546e988..332cba71 100644
--- a/chrome/test/data/webui/tab_strip/tab_list_test.ts
+++ b/chrome/test/data/webui/tab_strip/tab_list_test.ts
@@ -5,10 +5,11 @@
 import 'chrome://tab-strip.top-chrome/tab_list.js';
 
 import {FocusOutlineManager} from 'chrome://resources/js/focus_outline_manager.js';
-import {TabElement} from 'chrome://tab-strip.top-chrome/tab.js';
-import {TabGroupElement} from 'chrome://tab-strip.top-chrome/tab_group.js';
-import {setScrollAnimationEnabledForTesting, TabListElement} from 'chrome://tab-strip.top-chrome/tab_list.js';
-import {PageRemote, Tab} from 'chrome://tab-strip.top-chrome/tab_strip.mojom-webui.js';
+import type {TabElement} from 'chrome://tab-strip.top-chrome/tab.js';
+import type {TabGroupElement} from 'chrome://tab-strip.top-chrome/tab_group.js';
+import type {TabListElement} from 'chrome://tab-strip.top-chrome/tab_list.js';
+import {setScrollAnimationEnabledForTesting} from 'chrome://tab-strip.top-chrome/tab_list.js';
+import type {PageRemote, Tab} from 'chrome://tab-strip.top-chrome/tab_strip.mojom-webui.js';
 import {TabsApiProxyImpl} from 'chrome://tab-strip.top-chrome/tabs_api_proxy.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/tab_strip/tab_test.ts b/chrome/test/data/webui/tab_strip/tab_test.ts
index cd0b90b5..9dc154d 100644
--- a/chrome/test/data/webui/tab_strip/tab_test.ts
+++ b/chrome/test/data/webui/tab_strip/tab_test.ts
@@ -6,8 +6,9 @@
 
 import {getFavicon} from 'chrome://resources/js/icon.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {TabElement} from 'chrome://tab-strip.top-chrome/tab.js';
-import {Tab, TabNetworkState} from 'chrome://tab-strip.top-chrome/tab_strip.mojom-webui.js';
+import type {TabElement} from 'chrome://tab-strip.top-chrome/tab.js';
+import type {Tab} from 'chrome://tab-strip.top-chrome/tab_strip.mojom-webui.js';
+import {TabNetworkState} from 'chrome://tab-strip.top-chrome/tab_strip.mojom-webui.js';
 import {CloseTabAction, TabsApiProxyImpl} from 'chrome://tab-strip.top-chrome/tabs_api_proxy.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.ts b/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.ts
index 5fb09ee..926bc2c 100644
--- a/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.ts
+++ b/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.ts
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {PageCallbackRouter, PageRemote} from 'chrome://tab-strip.top-chrome/tab_strip.mojom-webui.js';
-import {Tab, TabGroupVisualData, TabNetworkState} from 'chrome://tab-strip.top-chrome/tab_strip.mojom-webui.js';
-import {CloseTabAction, TabsApiProxy} from 'chrome://tab-strip.top-chrome/tabs_api_proxy';
-
+import type {PageRemote} from 'chrome://tab-strip.top-chrome/tab_strip.mojom-webui.js';
+import {PageCallbackRouter} from 'chrome://tab-strip.top-chrome/tab_strip.mojom-webui.js';
+import type {Tab, TabGroupVisualData} from 'chrome://tab-strip.top-chrome/tab_strip.mojom-webui.js';
+import {TabNetworkState} from 'chrome://tab-strip.top-chrome/tab_strip.mojom-webui.js';
+import type {CloseTabAction, TabsApiProxy} from 'chrome://tab-strip.top-chrome/tabs_api_proxy';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export function createTab(override?: Partial<Tab>): Tab {
diff --git a/chrome/test/data/webui/test_browser_proxy.ts b/chrome/test/data/webui/test_browser_proxy.ts
index ea50758d..2327f45 100644
--- a/chrome/test/data/webui/test_browser_proxy.ts
+++ b/chrome/test/data/webui/test_browser_proxy.ts
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {assert} from 'chrome://resources/js/assert.js';
-import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
+import {assert} from '//resources/js/assert.js';
+import {PromiseResolver} from '//resources/js/promise_resolver.js';
 
 /**
  * A base class for all test browser proxies to inherit from. Provides helper
diff --git a/chrome/test/data/webui/test_open_window_proxy.ts b/chrome/test/data/webui/test_open_window_proxy.ts
index 45c7a89..f2e5ddb 100644
--- a/chrome/test/data/webui/test_open_window_proxy.ts
+++ b/chrome/test/data/webui/test_open_window_proxy.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {OpenWindowProxy} from 'chrome://resources/js/open_window_proxy.js';
+import type {OpenWindowProxy} from 'chrome://resources/js/open_window_proxy.js';
 
 import {TestBrowserProxy} from './test_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/test_plural_string_proxy.ts b/chrome/test/data/webui/test_plural_string_proxy.ts
index ab1c683..649562b1 100644
--- a/chrome/test/data/webui/test_plural_string_proxy.ts
+++ b/chrome/test/data/webui/test_plural_string_proxy.ts
@@ -5,7 +5,7 @@
 /** @fileoverview Test implementation of PluralStringProxy. */
 
 // clang-format off
-import {PluralStringProxy} from 'chrome://resources/js/plural_string_proxy.js';
+import type {PluralStringProxy} from 'chrome://resources/js/plural_string_proxy.js';
 
 import {TestBrowserProxy} from './test_browser_proxy.js';
 // clang-format on
diff --git a/chrome/test/data/webui/test_store.ts b/chrome/test/data/webui/test_store.ts
index 4d982ae..4306682 100644
--- a/chrome/test/data/webui/test_store.ts
+++ b/chrome/test/data/webui/test_store.ts
@@ -2,8 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Action, Store} from 'chrome://resources/js/store.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
+import type {Action} from 'chrome://resources/js/store.js';
+import {Store} from 'chrome://resources/js/store.js';
+
 import {assertTrue} from './chai_assert.js';
 
 /**
diff --git a/chrome/test/data/webui/usb_internals/usb_internals_test.ts b/chrome/test/data/webui/usb_internals/usb_internals_test.ts
index 524ef6b9..055e1e50 100644
--- a/chrome/test/data/webui/usb_internals/usb_internals_test.ts
+++ b/chrome/test/data/webui/usb_internals/usb_internals_test.ts
@@ -4,16 +4,20 @@
 
 import 'chrome://resources/cr_elements/cr_tree/cr_tree.js';
 
-import {CrTreeItemElement} from 'chrome://resources/cr_elements/cr_tree/cr_tree_item.js';
+import type {CrTreeItemElement} from 'chrome://resources/cr_elements/cr_tree/cr_tree_item.js';
 import {stringToMojoString16} from 'chrome://resources/js/mojo_type_util.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
-import {File} from 'chrome://resources/mojo/mojo/public/mojom/base/file.mojom-webui.js';
-import {ReadOnlyBuffer} from 'chrome://resources/mojo/mojo/public/mojom/base/read_only_buffer.mojom-webui.js';
-import {setSetupFn, UsbInternalsAppElement} from 'chrome://usb-internals/app.js';
-import {UsbClaimInterfaceResult, UsbControlTransferParams, UsbControlTransferRecipient, UsbControlTransferType, UsbDeviceClientRemote, UsbDeviceInfo, UsbDeviceInterface, UsbDevicePendingReceiver, UsbDeviceReceiver, UsbIsochronousPacket, UsbOpenDeviceResult, UsbOpenDeviceSuccess, UsbTransferDirection, UsbTransferStatus} from 'chrome://usb-internals/usb_device.mojom-webui.js';
-import {UsbInternalsPageHandler, UsbInternalsPageHandlerInterface, UsbInternalsPageHandlerReceiver} from 'chrome://usb-internals/usb_internals.mojom-webui.js';
-import {UsbDeviceManagerInterface, UsbDeviceManagerPendingReceiver, UsbDeviceManagerReceiver} from 'chrome://usb-internals/usb_manager.mojom-webui.js';
-import {UsbDeviceManagerTestPendingReceiver} from 'chrome://usb-internals/usb_manager_test.mojom-webui.js';
+import type {File} from 'chrome://resources/mojo/mojo/public/mojom/base/file.mojom-webui.js';
+import type {ReadOnlyBuffer} from 'chrome://resources/mojo/mojo/public/mojom/base/read_only_buffer.mojom-webui.js';
+import type {UsbInternalsAppElement} from 'chrome://usb-internals/app.js';
+import {setSetupFn} from 'chrome://usb-internals/app.js';
+import type {UsbClaimInterfaceResult, UsbControlTransferParams, UsbDeviceClientRemote, UsbDeviceInfo, UsbDeviceInterface, UsbDevicePendingReceiver, UsbIsochronousPacket, UsbOpenDeviceResult, UsbTransferDirection} from 'chrome://usb-internals/usb_device.mojom-webui.js';
+import {UsbControlTransferRecipient, UsbControlTransferType, UsbDeviceReceiver, UsbOpenDeviceSuccess, UsbTransferStatus} from 'chrome://usb-internals/usb_device.mojom-webui.js';
+import type {UsbInternalsPageHandlerInterface} from 'chrome://usb-internals/usb_internals.mojom-webui.js';
+import {UsbInternalsPageHandler, UsbInternalsPageHandlerReceiver} from 'chrome://usb-internals/usb_internals.mojom-webui.js';
+import type {UsbDeviceManagerInterface, UsbDeviceManagerPendingReceiver} from 'chrome://usb-internals/usb_manager.mojom-webui.js';
+import {UsbDeviceManagerReceiver} from 'chrome://usb-internals/usb_manager.mojom-webui.js';
+import type {UsbDeviceManagerTestPendingReceiver} from 'chrome://usb-internals/usb_manager_test.mojom-webui.js';
 import {assertEquals, assertFalse, assertNotReached, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/welcome/app_chooser_test.ts b/chrome/test/data/webui/welcome/app_chooser_test.ts
index 3295c80..6757379 100644
--- a/chrome/test/data/webui/welcome/app_chooser_test.ts
+++ b/chrome/test/data/webui/welcome/app_chooser_test.ts
@@ -4,11 +4,11 @@
 
 import 'chrome://welcome/google_apps/nux_google_apps.js';
 
-import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import type {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {GoogleAppProxyImpl} from 'chrome://welcome/google_apps/google_app_proxy.js';
 import {GoogleAppsMetricsProxyImpl} from 'chrome://welcome/google_apps/google_apps_metrics_proxy.js';
-import {NuxGoogleAppsElement} from 'chrome://welcome/google_apps/nux_google_apps.js';
+import type {NuxGoogleAppsElement} from 'chrome://welcome/google_apps/nux_google_apps.js';
 import {BookmarkBarManager, BookmarkProxyImpl} from 'chrome://welcome/shared/bookmark_proxy.js';
 
 import {TestBookmarkProxy} from './test_bookmark_proxy.js';
diff --git a/chrome/test/data/webui/welcome/nux_ntp_background_test.ts b/chrome/test/data/webui/welcome/nux_ntp_background_test.ts
index d43d38b3..6ada994b 100644
--- a/chrome/test/data/webui/welcome/nux_ntp_background_test.ts
+++ b/chrome/test/data/webui/welcome/nux_ntp_background_test.ts
@@ -7,8 +7,9 @@
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {NtpBackgroundMetricsProxyImpl} from 'chrome://welcome/ntp_background/ntp_background_metrics_proxy.js';
-import {NtpBackgroundData, NtpBackgroundProxyImpl} from 'chrome://welcome/ntp_background/ntp_background_proxy.js';
-import {NuxNtpBackgroundElement} from 'chrome://welcome/ntp_background/nux_ntp_background.js';
+import type {NtpBackgroundData} from 'chrome://welcome/ntp_background/ntp_background_proxy.js';
+import {NtpBackgroundProxyImpl} from 'chrome://welcome/ntp_background/ntp_background_proxy.js';
+import type {NuxNtpBackgroundElement} from 'chrome://welcome/ntp_background/nux_ntp_background.js';
 
 import {TestMetricsProxy} from './test_metrics_proxy.js';
 import {TestNtpBackgroundProxy} from './test_ntp_background_proxy.js';
diff --git a/chrome/test/data/webui/welcome/nux_set_as_default_test.ts b/chrome/test/data/webui/welcome/nux_set_as_default_test.ts
index e5b2b2c9..323d9ca8 100644
--- a/chrome/test/data/webui/welcome/nux_set_as_default_test.ts
+++ b/chrome/test/data/webui/welcome/nux_set_as_default_test.ts
@@ -4,10 +4,10 @@
 
 import 'chrome://welcome/set_as_default/nux_set_as_default.js';
 
-import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import type {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
-import {NuxSetAsDefaultElement} from 'chrome://welcome/set_as_default/nux_set_as_default.js';
+import type {NuxSetAsDefaultElement} from 'chrome://welcome/set_as_default/nux_set_as_default.js';
 import {NuxSetAsDefaultProxyImpl} from 'chrome://welcome/set_as_default/nux_set_as_default_proxy.js';
 
 import {TestNuxSetAsDefaultProxy} from './test_nux_set_as_default_proxy.js';
diff --git a/chrome/test/data/webui/welcome/signin_view_test.ts b/chrome/test/data/webui/welcome/signin_view_test.ts
index 8b9ca7f5..0f976805 100644
--- a/chrome/test/data/webui/welcome/signin_view_test.ts
+++ b/chrome/test/data/webui/welcome/signin_view_test.ts
@@ -5,8 +5,7 @@
 import 'chrome://welcome/signin_view.js';
 
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-
-import {SigninViewElement} from 'chrome://welcome/signin_view.js';
+import type {SigninViewElement} from 'chrome://welcome/signin_view.js';
 import {SigninViewProxyImpl} from 'chrome://welcome/signin_view_proxy.js';
 import {WelcomeBrowserProxyImpl} from 'chrome://welcome/welcome_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/welcome/test_bookmark_proxy.ts b/chrome/test/data/webui/welcome/test_bookmark_proxy.ts
index c5499f4b..0a389522 100644
--- a/chrome/test/data/webui/welcome/test_bookmark_proxy.ts
+++ b/chrome/test/data/webui/welcome/test_bookmark_proxy.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-import {BookmarkData, BookmarkProxy} from 'chrome://welcome/shared/bookmark_proxy.js';
+import type {BookmarkData, BookmarkProxy} from 'chrome://welcome/shared/bookmark_proxy.js';
 
 export class TestBookmarkProxy extends TestBrowserProxy implements
     BookmarkProxy {
diff --git a/chrome/test/data/webui/welcome/test_google_app_proxy.ts b/chrome/test/data/webui/welcome/test_google_app_proxy.ts
index 41fa6f5b..1f7a7c7 100644
--- a/chrome/test/data/webui/welcome/test_google_app_proxy.ts
+++ b/chrome/test/data/webui/welcome/test_google_app_proxy.ts
@@ -3,9 +3,8 @@
 // found in the LICENSE file.
 
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-
-import {GoogleAppProxy} from 'chrome://welcome/google_apps/google_app_proxy.js';
-import {BookmarkListItem} from 'chrome://welcome/shared/nux_types.js';
+import type {GoogleAppProxy} from 'chrome://welcome/google_apps/google_app_proxy.js';
+import type {BookmarkListItem} from 'chrome://welcome/shared/nux_types.js';
 
 export class TestGoogleAppProxy extends TestBrowserProxy implements
     GoogleAppProxy {
diff --git a/chrome/test/data/webui/welcome/test_landing_view_proxy.ts b/chrome/test/data/webui/welcome/test_landing_view_proxy.ts
index bf3ffcf..560e6e1d 100644
--- a/chrome/test/data/webui/welcome/test_landing_view_proxy.ts
+++ b/chrome/test/data/webui/welcome/test_landing_view_proxy.ts
@@ -3,8 +3,7 @@
 // found in the LICENSE file.
 
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-
-import {LandingViewProxy} from 'chrome://welcome/landing_view_proxy.js';
+import type {LandingViewProxy} from 'chrome://welcome/landing_view_proxy.js';
 
 export class TestLandingViewProxy extends TestBrowserProxy implements
     LandingViewProxy {
diff --git a/chrome/test/data/webui/welcome/test_metrics_proxy.ts b/chrome/test/data/webui/welcome/test_metrics_proxy.ts
index 1e681351..2a9cf11 100644
--- a/chrome/test/data/webui/welcome/test_metrics_proxy.ts
+++ b/chrome/test/data/webui/welcome/test_metrics_proxy.ts
@@ -3,8 +3,7 @@
 // found in the LICENSE file.
 
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-
-import {ModuleMetricsProxy} from 'chrome://welcome/shared/module_metrics_proxy.js';
+import type {ModuleMetricsProxy} from 'chrome://welcome/shared/module_metrics_proxy.js';
 
 export class TestMetricsProxy extends TestBrowserProxy implements
     ModuleMetricsProxy {
diff --git a/chrome/test/data/webui/welcome/test_ntp_background_proxy.ts b/chrome/test/data/webui/welcome/test_ntp_background_proxy.ts
index abb6795..bc1509c 100644
--- a/chrome/test/data/webui/welcome/test_ntp_background_proxy.ts
+++ b/chrome/test/data/webui/welcome/test_ntp_background_proxy.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-import {NtpBackgroundData, NtpBackgroundProxy} from 'chrome://welcome/ntp_background/ntp_background_proxy.js';
+import type {NtpBackgroundData, NtpBackgroundProxy} from 'chrome://welcome/ntp_background/ntp_background_proxy.js';
 
 export class TestNtpBackgroundProxy extends TestBrowserProxy implements
     NtpBackgroundProxy {
diff --git a/chrome/test/data/webui/welcome/test_nux_set_as_default_proxy.ts b/chrome/test/data/webui/welcome/test_nux_set_as_default_proxy.ts
index 0df39a9..8ac6098 100644
--- a/chrome/test/data/webui/welcome/test_nux_set_as_default_proxy.ts
+++ b/chrome/test/data/webui/welcome/test_nux_set_as_default_proxy.ts
@@ -3,9 +3,8 @@
 // found in the LICENSE file.
 
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-
-import {NuxSetAsDefaultProxy} from 'chrome://welcome/set_as_default/nux_set_as_default_proxy.js';
-import {DefaultBrowserInfo} from 'chrome://welcome/shared/nux_types.js';
+import type {NuxSetAsDefaultProxy} from 'chrome://welcome/set_as_default/nux_set_as_default_proxy.js';
+import type {DefaultBrowserInfo} from 'chrome://welcome/shared/nux_types.js';
 
 export class TestNuxSetAsDefaultProxy extends TestBrowserProxy implements
     NuxSetAsDefaultProxy {
diff --git a/chrome/test/data/webui/welcome/test_signin_view_proxy.ts b/chrome/test/data/webui/welcome/test_signin_view_proxy.ts
index aa74da3..e578b268 100644
--- a/chrome/test/data/webui/welcome/test_signin_view_proxy.ts
+++ b/chrome/test/data/webui/welcome/test_signin_view_proxy.ts
@@ -3,8 +3,7 @@
 // found in the LICENSE file.
 
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-
-import {SigninViewProxy} from 'chrome://welcome/signin_view_proxy.js';
+import type {SigninViewProxy} from 'chrome://welcome/signin_view_proxy.js';
 
 export class TestSigninViewProxy extends TestBrowserProxy implements
     SigninViewProxy {
diff --git a/chrome/test/data/webui/welcome/test_welcome_browser_proxy.ts b/chrome/test/data/webui/welcome/test_welcome_browser_proxy.ts
index 4f60487..1b71ee2 100644
--- a/chrome/test/data/webui/welcome/test_welcome_browser_proxy.ts
+++ b/chrome/test/data/webui/welcome/test_welcome_browser_proxy.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-import {WelcomeBrowserProxy} from 'chrome://welcome/welcome_browser_proxy.js';
+import type {WelcomeBrowserProxy} from 'chrome://welcome/welcome_browser_proxy.js';
 
 export class TestWelcomeBrowserProxy extends TestBrowserProxy implements
     WelcomeBrowserProxy {
diff --git a/chrome/test/data/webui/welcome/welcome_app_test.ts b/chrome/test/data/webui/welcome/welcome_app_test.ts
index 5233e938..5068088 100644
--- a/chrome/test/data/webui/welcome/welcome_app_test.ts
+++ b/chrome/test/data/webui/welcome/welcome_app_test.ts
@@ -10,8 +10,8 @@
 import {navigateTo, Routes} from 'chrome://welcome/navigation_mixin.js';
 import {NuxSetAsDefaultProxyImpl} from 'chrome://welcome/set_as_default/nux_set_as_default_proxy.js';
 import {BookmarkProxyImpl} from 'chrome://welcome/shared/bookmark_proxy.js';
-import {DefaultBrowserInfo} from 'chrome://welcome/shared/nux_types.js';
-import {WelcomeAppElement} from 'chrome://welcome/welcome_app.js';
+import type {DefaultBrowserInfo} from 'chrome://welcome/shared/nux_types.js';
+import type {WelcomeAppElement} from 'chrome://welcome/welcome_app.js';
 import {WelcomeBrowserProxyImpl} from 'chrome://welcome/welcome_browser_proxy.js';
 
 import {TestBookmarkProxy} from './test_bookmark_proxy.js';
diff --git a/chrome/test/data/webui/whats_new/whats_new_app_test.ts b/chrome/test/data/webui/whats_new/whats_new_app_test.ts
index 56ba88f..c5fab3d 100644
--- a/chrome/test/data/webui/whats_new/whats_new_app_test.ts
+++ b/chrome/test/data/webui/whats_new/whats_new_app_test.ts
@@ -12,7 +12,8 @@
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 import {TestMock} from 'chrome://webui-test/test_mock.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
-import {WhatsNewProxy, WhatsNewProxyImpl} from 'chrome://whats-new/whats_new_proxy.js';
+import type {WhatsNewProxy} from 'chrome://whats-new/whats_new_proxy.js';
+import {WhatsNewProxyImpl} from 'chrome://whats-new/whats_new_proxy.js';
 
 const whatsNewURL = 'chrome://webui-test/whats_new/test.html';
 
diff --git a/chrome/updater/enum_traits.h b/chrome/updater/enum_traits.h
index 9d10cfc1..f37df04e 100644
--- a/chrome/updater/enum_traits.h
+++ b/chrome/updater/enum_traits.h
@@ -5,19 +5,22 @@
 #ifndef CHROME_UPDATER_ENUM_TRAITS_H_
 #define CHROME_UPDATER_ENUM_TRAITS_H_
 
+#include <concepts>
 #include <optional>
 #include <ostream>
 #include <type_traits>
 
+#include "base/types/cxx23_to_underlying.h"
+
 namespace updater {
 
-// Provides a way to safely convert numeric types to enumerated values.
-// To use this facility, the enum definition must be annotated with traits to
-// specify the range of the enum values. Due to how specialization of class
-// templates works in C++14, the |EnumTraits| specialization of the primary
-// template must be defined inside the |updater| namespace, where the
-// primary template is defined. Traits for enum types defined inside
-// other scopes, such as nested classes or other namespaces, may not work.
+// Provides a way to safely convert numeric types to enumerated values. To use
+// this facility, the enum definition must be annotated with traits to specify
+// the range of the enum values. Due to how specialization of class templates
+// works in C++14, the `EnumTraits` specialization of the primary template must
+// be defined inside the `updater` namespace, where the primary template is
+// defined. Traits for enum types defined inside other scopes, such as nested
+// classes or other namespaces, may not work.
 //
 // enum class MyEnum {
 //   kVal1 = -1,
@@ -34,19 +37,18 @@
 // MyEnum val = *CheckedCastToEnum<MyEnum>(-1);
 
 template <typename T>
+  requires(std::is_enum_v<T>)
 struct EnumTraits {};
 
 // Returns an optional value of an enum type T if the conversion from an
-// integral type V is safe, meaning that |v| is within the bounds of the enum.
+// integer value `v` is safe, meaning that `v` is within the bounds of the enum.
 // The enum type must be annotated with traits to specify the lower and upper
 // bounds of the enum values.
-template <typename T, typename V>
-std::optional<T> CheckedCastToEnum(V v) {
-  static_assert(std::is_enum<T>::value, "T must be an enum type.");
-  static_assert(std::is_integral<V>::value, "V must be an integral type.");
-  using Traits = EnumTraits<T>;
-  return (static_cast<V>(Traits::first_elem) <= v &&
-          v <= static_cast<V>(Traits::last_elem))
+template <typename T>
+  requires(std::is_enum_v<T>)
+constexpr std::optional<T> CheckedCastToEnum(std::underlying_type_t<T> v) {
+  return (base::to_underlying(EnumTraits<T>::first_elem) <= v &&
+          v <= base::to_underlying(EnumTraits<T>::last_elem))
              ? std::make_optional(static_cast<T>(v))
              : std::nullopt;
 }
diff --git a/chrome/updater/util/util.h b/chrome/updater/util/util.h
index 287e1fd..52d50212 100644
--- a/chrome/updater/util/util.h
+++ b/chrome/updater/util/util.h
@@ -44,7 +44,7 @@
 
 // Inserts an enum value as the underlying type.
 template <typename T>
-  requires std::is_enum_v<T>
+  requires(std::is_enum_v<T>)
 inline std::ostream& operator<<(std::ostream& os, const T& e) {
   return os << base::to_underlying(e);
 }
@@ -220,7 +220,7 @@
 
 // Returns the quotient of dividing two integer numbers (m/n) rounded up.
 template <typename T>
-  requires std::integral<T>
+  requires(std::integral<T>)
 [[nodiscard]] constexpr T CeilingDivide(T m, T n) {
   return std::ceil(static_cast<double>(m) / n);
 }
diff --git a/chromecast/browser/cast_display_configurator.cc b/chromecast/browser/cast_display_configurator.cc
index 16e2d82..2e669b4 100644
--- a/chromecast/browser/cast_display_configurator.cc
+++ b/chromecast/browser/cast_display_configurator.cc
@@ -172,14 +172,6 @@
   if (!delegate_ || !display_)
     return;
   delegate_->SetColorTemperatureAdjustment(display_->display_id(), cta);
-
-  std::vector<float> color_matrix(9);
-  for (size_t i = 0; i < 3; ++i) {
-    for (size_t j = 0; j < 3; ++j) {
-      color_matrix[3 * i + j] = cta.srgb_matrix.vals[i][j];
-    }
-  }
-  delegate_->SetColorMatrix(display_->display_id(), color_matrix);
   NotifyObservers();
 }
 
@@ -188,8 +180,6 @@
   if (!delegate_ || !display_)
     return;
   delegate_->SetGammaAdjustment(display_->display_id(), adjustment);
-
-  delegate_->SetGammaCorrection(display_->display_id(), {}, adjustment.curve);
   NotifyObservers();
 }
 
diff --git a/chromecast/browser/media/media_caps_impl.cc b/chromecast/browser/media/media_caps_impl.cc
index fdb1048..e3961e7 100644
--- a/chromecast/browser/media/media_caps_impl.cc
+++ b/chromecast/browser/media/media_caps_impl.cc
@@ -5,7 +5,6 @@
 #include "chromecast/browser/media/media_caps_impl.h"
 
 #include "base/logging.h"
-#include "chromecast/media/base/media_caps.h"
 #include "chromecast/public/media/decoder_config.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
@@ -21,16 +20,7 @@
   return result;
 }
 
-MediaCapsImpl::MediaCapsImpl()
-    : hdcp_version_(0),
-      supported_eotfs_(0),
-      dolby_vision_flags_(0),
-      screen_width_mm_(0),
-      screen_height_mm_(0),
-      current_mode_supports_hdr_(false),
-      current_mode_supports_dv_(false),
-      screen_resolution_(0, 0) {}
-
+MediaCapsImpl::MediaCapsImpl() = default;
 MediaCapsImpl::~MediaCapsImpl() = default;
 
 void MediaCapsImpl::AddReceiver(
@@ -44,35 +34,6 @@
   return pending_remote;
 }
 
-void MediaCapsImpl::ScreenResolutionChanged(uint32_t width, uint32_t height) {
-  screen_resolution_ = gfx::Size(width, height);
-  for (auto& observer : observers_)
-    observer->ScreenResolutionChanged(width, height);
-}
-
-void MediaCapsImpl::ScreenInfoChanged(int32_t hdcp_version,
-                                      int32_t supported_eotfs,
-                                      int32_t dolby_vision_flags,
-                                      int32_t screen_width_mm,
-                                      int32_t screen_height_mm,
-                                      bool current_mode_supports_hdr,
-                                      bool current_mode_supports_dv) {
-  hdcp_version_ = hdcp_version;
-  supported_eotfs_ = supported_eotfs;
-  dolby_vision_flags_ = dolby_vision_flags;
-  screen_width_mm_ = screen_width_mm;
-  screen_height_mm_ = screen_height_mm;
-  current_mode_supports_hdr_ = current_mode_supports_hdr;
-  current_mode_supports_dv_ = current_mode_supports_dv;
-
-  for (auto& observer : observers_) {
-    observer->ScreenInfoChanged(hdcp_version, supported_eotfs,
-                                dolby_vision_flags, screen_width_mm,
-                                screen_height_mm, current_mode_supports_hdr,
-                                current_mode_supports_dv);
-  }
-}
-
 void MediaCapsImpl::AddSupportedCodecProfileLevel(
     const CodecProfileLevel& codec_profile_level) {
   codec_profile_levels_.push_back(codec_profile_level);
@@ -87,12 +48,6 @@
 void MediaCapsImpl::AddObserver(
     mojo::PendingRemote<mojom::MediaCapsObserver> observer_remote) {
   mojo::Remote<mojom::MediaCapsObserver> observer(std::move(observer_remote));
-  observer->ScreenResolutionChanged(screen_resolution_.width(),
-                                    screen_resolution_.height());
-  observer->ScreenInfoChanged(hdcp_version_, supported_eotfs_,
-                              dolby_vision_flags_, screen_width_mm_,
-                              screen_height_mm_, current_mode_supports_hdr_,
-                              current_mode_supports_dv_);
   DVLOG(1) << __func__ << ": Sending " << codec_profile_levels_.size()
            << " supported codec profile levels to observer.";
   for (const auto& codec_profile_level : codec_profile_levels_) {
diff --git a/chromecast/browser/media/media_caps_impl.h b/chromecast/browser/media/media_caps_impl.h
index 8f70aad..b6b16b21 100644
--- a/chromecast/browser/media/media_caps_impl.h
+++ b/chromecast/browser/media/media_caps_impl.h
@@ -11,7 +11,6 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/remote_set.h"
-#include "ui/gfx/geometry/size.h"
 
 namespace chromecast {
 namespace media {
@@ -36,23 +35,7 @@
   // chromecast::mojom::MediaCaps implementation.
   void AddObserver(
       mojo::PendingRemote<mojom::MediaCapsObserver> observer) override;
-  void ScreenResolutionChanged(uint32_t width, uint32_t height) override;
-  void ScreenInfoChanged(int32_t hdcp_version,
-                         int32_t supported_eotfs,
-                         int32_t dolby_vision_flags,
-                         int32_t screen_width_mm,
-                         int32_t screen_height_mm,
-                         bool current_mode_supports_hdr,
-                         bool current_mode_supports_dv) override;
 
-  int32_t hdcp_version_;
-  int32_t supported_eotfs_;
-  int32_t dolby_vision_flags_;
-  int32_t screen_width_mm_;
-  int32_t screen_height_mm_;
-  bool current_mode_supports_hdr_;
-  bool current_mode_supports_dv_;
-  gfx::Size screen_resolution_;
   std::vector<CodecProfileLevel> codec_profile_levels_;
   mojo::RemoteSet<mojom::MediaCapsObserver> observers_;
   mojo::ReceiverSet<mojom::MediaCaps> receivers_;
diff --git a/chromecast/browser/test/cast_browser_test.cc b/chromecast/browser/test/cast_browser_test.cc
index 026750f..9f8371bd 100644
--- a/chromecast/browser/test/cast_browser_test.cc
+++ b/chromecast/browser/test/cast_browser_test.cc
@@ -59,6 +59,7 @@
   // MOJO_RENDERER is CMA renderer on Chromecast
   params->renderer_type = ::chromecast::mojom::RendererType::MOJO_RENDERER;
   params->enabled_for_dev = true;
+  params->log_js_console_messages = true;
   cast_web_view_ = web_service_->CreateWebViewInternal(std::move(params));
 
   return cast_web_view_->web_contents();
diff --git a/chromecast/common/mojom/media_caps.mojom b/chromecast/common/mojom/media_caps.mojom
index e9d3d9f..b419f26 100644
--- a/chromecast/common/mojom/media_caps.mojom
+++ b/chromecast/common/mojom/media_caps.mojom
@@ -14,27 +14,10 @@
 interface MediaCaps {
   // Adds an `observer` to monitor media capability changes.
   AddObserver(pending_remote<MediaCapsObserver> observer);
-
-  // Notifies the observers of the change to the screen resolution.
-  ScreenResolutionChanged(uint32 width, uint32 height);
-
-  // Notifies the observers of the change to the screen info.
-  ScreenInfoChanged(int32 hdcp_version, int32 supported_eotfs,
-                    int32 dolby_vision_flags, int32 screen_width_mm,
-                    int32 screen_height_mm, bool current_mode_supports_hdr,
-                    bool current_mode_supports_dolby_vision);
 };
 
 // Observes changes in media capabilities that apply to all applications.
 interface MediaCapsObserver {
-  ScreenResolutionChanged(uint32 width, uint32 height);
-
-  // EOTFs and Dolby Vision flags defined in avsettings.h
-  ScreenInfoChanged(int32 hdcp_version, int32 supported_eotfs,
-                    int32 dolby_vision_flags, int32 screen_width_mm,
-                    int32 screen_height_mm, bool current_mode_supports_hdr,
-                    bool current_mode_supports_dolby_vision);
-
   // Add supported codec profiles and levels. May not be called on all
   // platforms, but can be used to infer whether a video config is supported.
   AddSupportedCodecProfileLevel(CodecProfileLevel codec_profile_level);
diff --git a/chromecast/media/audio/BUILD.gn b/chromecast/media/audio/BUILD.gn
index 044cc55f..99c4c466 100644
--- a/chromecast/media/audio/BUILD.gn
+++ b/chromecast/media/audio/BUILD.gn
@@ -44,10 +44,7 @@
   # Use fastest possible float math.
   configs -= [ "//build/config/compiler:default_optimization" ]
   configs += [ "//build/config/compiler:optimize_speed" ]
-  cflags = [
-    "-ffast-math",
-    "-Wno-nan-infinity-disabled",
-  ]
+  cflags = [ "-ffast-math" ]
 }
 
 cast_source_set("playback_rate_shifter") {
diff --git a/chromecast/media/base/BUILD.gn b/chromecast/media/base/BUILD.gn
index f3a47bf..fc39a768 100644
--- a/chromecast/media/base/BUILD.gn
+++ b/chromecast/media/base/BUILD.gn
@@ -41,8 +41,6 @@
     "cast_decoder_buffer_impl.h",
     "decrypt_context_impl.cc",
     "decrypt_context_impl.h",
-    "media_caps.cc",
-    "media_caps.h",
     "supported_codec_profile_levels_memo.cc",
     "supported_codec_profile_levels_memo.h",
     "video_mode_switcher.cc",
diff --git a/chromecast/media/cdm/cast_cdm.cc b/chromecast/media/cdm/cast_cdm.cc
index c382669..3f775f2 100644
--- a/chromecast/media/cdm/cast_cdm.cc
+++ b/chromecast/media/cdm/cast_cdm.cc
@@ -11,7 +11,6 @@
 #include "base/location.h"
 #include "base/task/single_thread_task_runner.h"
 #include "chromecast/media/base/decrypt_context_impl.h"
-#include "chromecast/media/base/media_caps.h"
 #include "chromecast/media/common/media_resource_tracker.h"
 #include "media/base/cdm_key_information.h"
 #include "media/base/cdm_promise.h"
@@ -139,7 +138,9 @@
     ::media::HdcpVersion min_hdcp_version,
     std::unique_ptr<::media::KeyStatusCdmPromise> promise) {
   int min_hdcp_x10 = HdcpVersionX10(min_hdcp_version);
-  int cur_hdcp_x10 = MediaCapabilities::GetHdcpVersion();
+  // TODO(sanfin): Implement a function to get the current HDCP version in the
+  // browser process.
+  int cur_hdcp_x10 = 0;
   promise->resolve(cur_hdcp_x10 >= min_hdcp_x10 ? KeyStatus::USABLE
                                                 : KeyStatus::OUTPUT_RESTRICTED);
 }
diff --git a/chromecast/renderer/media/media_caps_observer_impl.cc b/chromecast/renderer/media/media_caps_observer_impl.cc
index 19ea46d..284e300 100644
--- a/chromecast/renderer/media/media_caps_observer_impl.cc
+++ b/chromecast/renderer/media/media_caps_observer_impl.cc
@@ -4,7 +4,6 @@
 
 #include "chromecast/renderer/media/media_caps_observer_impl.h"
 
-#include "chromecast/media/base/media_caps.h"
 #include "chromecast/media/base/supported_codec_profile_levels_memo.h"
 #include "chromecast/public/media/decoder_config.h"
 
@@ -19,23 +18,6 @@
 
 MediaCapsObserverImpl::~MediaCapsObserverImpl() = default;
 
-void MediaCapsObserverImpl::ScreenResolutionChanged(uint32_t width,
-                                                    uint32_t height) {
-  MediaCapabilities::ScreenResolutionChanged(gfx::Size(width, height));
-}
-
-void MediaCapsObserverImpl::ScreenInfoChanged(int32_t hdcp_version,
-                                              int32_t supported_eotfs,
-                                              int32_t dolby_vision_flags,
-                                              int32_t screen_width_mm,
-                                              int32_t screen_height_mm,
-                                              bool current_mode_supports_hdr,
-                                              bool current_mode_supports_dv) {
-  MediaCapabilities::ScreenInfoChanged(
-      hdcp_version, supported_eotfs, dolby_vision_flags, screen_width_mm,
-      screen_height_mm, current_mode_supports_hdr, current_mode_supports_dv);
-}
-
 void MediaCapsObserverImpl::AddSupportedCodecProfileLevel(
     mojom::CodecProfileLevelPtr codec_profile_level) {
   CodecProfileLevel converted_codec_profile_level(
diff --git a/chromecast/renderer/media/media_caps_observer_impl.h b/chromecast/renderer/media/media_caps_observer_impl.h
index ad6bd27..488dac6 100644
--- a/chromecast/renderer/media/media_caps_observer_impl.h
+++ b/chromecast/renderer/media/media_caps_observer_impl.h
@@ -9,7 +9,6 @@
 #include "chromecast/media/base/supported_codec_profile_levels_memo.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
-#include "ui/gfx/geometry/size.h"
 
 namespace chromecast {
 namespace media {
@@ -25,14 +24,6 @@
   ~MediaCapsObserverImpl() override;
 
  private:
-  void ScreenResolutionChanged(uint32_t width, uint32_t height) override;
-  void ScreenInfoChanged(int32_t hdcp_version,
-                         int32_t supported_eotfs,
-                         int32_t dolby_vision_flags,
-                         int32_t screen_width_mm,
-                         int32_t screen_height_mm,
-                         bool current_mode_supports_hdr,
-                         bool current_mode_supports_dolby_vision) override;
   void AddSupportedCodecProfileLevel(
       mojom::CodecProfileLevelPtr codec_profile_level) override;
 
diff --git a/chromeos/ash/components/BUILD.gn b/chromeos/ash/components/BUILD.gn
index 29afdd5..d15d1d7 100644
--- a/chromeos/ash/components/BUILD.gn
+++ b/chromeos/ash/components/BUILD.gn
@@ -19,6 +19,7 @@
     "//chromeos/ash/components/dbus:unit_tests",
     "//chromeos/ash/components/disks:unit_tests",
     "//chromeos/ash/components/drivefs:unit_tests",
+    "//chromeos/ash/components/emoji:unit_tests",
     "//chromeos/ash/components/feature_usage:unit_tests",
     "//chromeos/ash/components/file_manager:unit_tests",
     "//chromeos/ash/components/fwupd:unit_tests",
diff --git a/chromeos/ash/components/dbus/fwupd/fake_fwupd_client.cc b/chromeos/ash/components/dbus/fwupd/fake_fwupd_client.cc
index 9a8bca0..c0b73cd1 100644
--- a/chromeos/ash/components/dbus/fwupd/fake_fwupd_client.cc
+++ b/chromeos/ash/components/dbus/fwupd/fake_fwupd_client.cc
@@ -5,6 +5,8 @@
 #include <string>
 
 #include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/threading/thread_restrictions.h"
 #include "chromeos/ash/components/dbus/fwupd/fake_fwupd_client.h"
 #include "chromeos/ash/components/dbus/fwupd/fwupd_device.h"
 #include "chromeos/ash/components/dbus/fwupd/fwupd_update.h"
@@ -18,7 +20,11 @@
 namespace ash {
 
 FakeFwupdClient::FakeFwupdClient() = default;
-FakeFwupdClient::~FakeFwupdClient() = default;
+FakeFwupdClient::~FakeFwupdClient() {
+  if (temp_directory_.IsValid()) {
+    CHECK(temp_directory_.Delete());
+  }
+}
 void FakeFwupdClient::Init(dbus::Bus* bus) {}
 
 void FakeFwupdClient::RequestDevices() {
@@ -43,8 +49,10 @@
   // Add a fake update.
   updates.emplace_back(
       /*version=*/"1.2.3", /*description=*/"Fake update description.",
-      /*priority=*/1, /*filepath=*/base::FilePath("/fake/filepath"),
-      /*checksum=*/"fake-checksum");
+      /*priority=*/1,
+      /*filepath=*/CreateUpdateFilePath(),
+      /*checksum=*/
+      "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
 
   for (auto& observer : observers_)
     observer.OnUpdateListResponse(device_id, &updates);
@@ -58,11 +66,32 @@
   if (device_id != kFakeDeviceIdForTesting)
     return;
 
-  for (auto& observer : observers_)
-    observer.OnInstallResponse(install_success_);
+  for (auto& observer : observers_) {
+    observer.OnInstallResponse(/*success=*/true);
+  }
 }
 
 // Implement stub method to satisfy interface.
 void FakeFwupdClient::SetFwupdFeatureFlags() {}
 
+base::FilePath FakeFwupdClient::CreateUpdateFilePath() {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  if (!temp_directory_.IsValid()) {
+    CHECK(temp_directory_.CreateUniqueTempDir());
+  }
+
+  const std::string fake_update_filename =
+      base::StrCat({kFakeDeviceIdForTesting, ".cab"});
+
+  // Create the file into the temp directory.
+  base::FilePath full_path_to_fake_update =
+      temp_directory_.GetPath().Append(fake_update_filename);
+  // Write an empty file, since the contents of the cab file are not processed
+  // upstream.
+  base::WriteFile(full_path_to_fake_update, "");
+  base::FilePath fake_update_file_with_URI(
+      base::StrCat({"file://", full_path_to_fake_update.value()}));
+  return fake_update_file_with_URI;
+}
+
 }  // namespace ash
diff --git a/chromeos/ash/components/dbus/fwupd/fake_fwupd_client.h b/chromeos/ash/components/dbus/fwupd/fake_fwupd_client.h
index 181b39c..22bba2f 100644
--- a/chromeos/ash/components/dbus/fwupd/fake_fwupd_client.h
+++ b/chromeos/ash/components/dbus/fwupd/fake_fwupd_client.h
@@ -7,6 +7,7 @@
 
 #include "base/component_export.h"
 #include "base/files/scoped_file.h"
+#include "base/files/scoped_temp_dir.h"
 #include "chromeos/ash/components/dbus/fwupd/fwupd_client.h"
 
 namespace ash {
@@ -28,7 +29,12 @@
 
  private:
   void SetFwupdFeatureFlags() override;
-  bool install_success_ = false;
+
+  // The temporary directory where fake update files are created.
+  base::ScopedTempDir temp_directory_;
+
+  // Creates a fake update file (with .cab extension) in a temporary directory.
+  base::FilePath CreateUpdateFilePath();
 };
 
 }  // namespace ash
diff --git a/chromeos/ash/components/drivefs/drivefs_host.h b/chromeos/ash/components/drivefs/drivefs_host.h
index 1b3fdab..a7d5a616 100644
--- a/chromeos/ash/components/drivefs/drivefs_host.h
+++ b/chromeos/ash/components/drivefs/drivefs_host.h
@@ -131,7 +131,7 @@
     virtual void OnUnmounted() {}
     virtual void OnSyncingStatusUpdate(const mojom::SyncingStatus& status) {}
     virtual void OnIndividualSyncingStatusesDelta(
-        const std::vector<const SyncState>& sync_states) {}
+        const std::vector<SyncState>& sync_states) {}
     virtual void OnMirrorSyncingStatusUpdate(
         const mojom::SyncingStatus& status) {}
     virtual void OnFilesChanged(const std::vector<mojom::FileChange>& changes) {
diff --git a/chromeos/ash/components/emoji/BUILD.gn b/chromeos/ash/components/emoji/BUILD.gn
index 8e10538..f801be5b 100644
--- a/chromeos/ash/components/emoji/BUILD.gn
+++ b/chromeos/ash/components/emoji/BUILD.gn
@@ -8,6 +8,24 @@
 
 assert(is_chromeos_ash, "Non-Chrome-OS builds must not depend on //ash")
 
+mojom("mojo_bindings") {
+  sources = [ "emoji_search.mojom" ]
+  public_deps = [ "//mojo/public/mojom/base" ]
+  webui_module_path = "/"
+}
+
+source_set("unit_tests") {
+  testonly = true
+  deps = [
+    ":emoji",
+    ":resources",
+    "//base/test:test_support",
+    "//testing/gtest",
+    "//ui/base:test_support",
+  ]
+  sources = [ "emoji_search_unittest.cc" ]
+}
+
 action_foreach("emoji_data") {
   script = "tools/emoji_data.py"
 
@@ -130,3 +148,16 @@
     "emoji.pak",
   ]
 }
+
+source_set("emoji") {
+  deps = [
+    ":mojo_bindings",
+    ":resources",
+    "//base",
+    "//ui/base",
+  ]
+  sources = [
+    "emoji_search.cc",
+    "emoji_search.h",
+  ]
+}
diff --git a/chromeos/ash/components/emoji/DEPS b/chromeos/ash/components/emoji/DEPS
new file mode 100644
index 0000000..b2a2f53
--- /dev/null
+++ b/chromeos/ash/components/emoji/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+ui/base",
+]
\ No newline at end of file
diff --git a/chromeos/ash/components/emoji/emoji_search.cc b/chromeos/ash/components/emoji/emoji_search.cc
new file mode 100644
index 0000000..58db92fd
--- /dev/null
+++ b/chromeos/ash/components/emoji/emoji_search.cc
@@ -0,0 +1,208 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/ash/components/emoji/emoji_search.h"
+
+#include <memory>
+#include <string_view>
+#include <unordered_map>
+
+#include "base/containers/cxx20_erase.h"
+#include "base/containers/span.h"
+#include "base/i18n/case_conversion.h"
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/notreached.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chromeos/ash/components/emoji/emoji_search.mojom.h"
+#include "chromeos/ash/components/emoji/grit/emoji.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+
+namespace emoji {
+
+namespace {
+// Map from keyword -> sum of position weightings
+std::map<std::string, double, std::less<>> CombineSearchTerms(
+    base::span<const std::string> long_search_terms) {
+  std::map<std::string, std::vector<int>, std::less<>> position_map;
+  for (const std::string& long_string : long_search_terms) {
+    std::vector<std::string_view> words = base::SplitStringPieceUsingSubstr(
+        long_string, " ", base::WhitespaceHandling::TRIM_WHITESPACE,
+        base::SplitResult::SPLIT_WANT_NONEMPTY);
+    for (size_t i = 0; i < words.size(); ++i) {
+      position_map[base::UTF16ToUTF8(
+                       base::i18n::ToLower(base::UTF8ToUTF16(words[i])))]
+          .push_back(i);
+    }
+  }
+
+  std::map<std::string, double, std::less<>> ret;
+  for (auto& map_entry : position_map) {
+    double weight = 0;
+    for (int p : map_entry.second) {
+      weight += 1.0 / (1.0 + p);
+    }
+    ret[map_entry.first] = weight;
+  }
+  return ret;
+}
+
+// Convert a JSON file to a map from search term to emoji weighted by position
+// in keyword / name.
+void AddDataFromFileToMap(
+    const int file_id_in_resources,
+    std::map<std::string, std::vector<EmojiSearchEntry>, std::less<>>& map) {
+  std::string json_string =
+      ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
+          file_id_in_resources);
+  // TODO(b/309343774): switch to JSON reading service
+  absl::optional<base::Value> json = base::JSONReader::Read(json_string);
+  CHECK(json) << "parse failed for " << file_id_in_resources << ":"
+              << json_string << "EOF";
+  base::Value::List groups = std::move(*json).TakeList();
+  // TODO(b/309343774): Consider using json_value_converter
+  for (auto& group : groups) {
+    for (const auto& emoji : *group.GetDict().FindList("emoji")) {
+      const base::Value::Dict* base = emoji.GetDict().FindDict("base");
+      const std::string* emoji_string = base->FindString("string");
+      CHECK(emoji_string) << "All emoji should have names";
+      // Gather possible search terms for the emoji
+      std::vector<std::string> search_terms;
+      const base::Value::List* keywords = base->FindList("keywords");
+      if (keywords) {
+        search_terms.reserve(keywords->size());
+        for (const auto& keyword : *keywords) {
+          search_terms.push_back(keyword.GetString());
+        }
+      }
+      for (const auto& search_term : CombineSearchTerms(search_terms)) {
+        // Keywords have less weighting (0.25)
+        map[search_term.first].push_back(
+            EmojiSearchEntry{.weighting = 0.25 * search_term.second,
+                             .emoji_string = *emoji_string});
+      }
+      const std::string* name = base->FindString("name");
+      if (name) {
+        for (const auto& search_term : CombineSearchTerms({{*name}})) {
+          map[search_term.first].push_back(
+              // Name has full weighting (1.0)
+              EmojiSearchEntry{.weighting = 1 * search_term.second,
+                               .emoji_string = *emoji_string});
+        }
+      }
+    }
+  }
+}
+
+std::unordered_map<std::string, double> GetResultsFromASingleWordQuery(
+    const std::map<std::string, std::vector<EmojiSearchEntry>, std::less<>>&
+        map,
+    const std::string_view query) {
+  if (query.empty()) {
+    return {};
+  }
+  std::unordered_map<std::string, double> scored_emoji;
+  // Make search case insensitive.
+  std::string lower_bound =
+      base::UTF16ToUTF8(base::i18n::ToLower(base::UTF8ToUTF16(query)));
+  std::string upper_bound = lower_bound;
+  // will break if someone searches for some very specific char, but should be
+  // fine.
+  upper_bound.back() = upper_bound.back() + 1;
+  // This should ensure we get everything that is a substring match
+  auto upper_bound_iterator = map.upper_bound(upper_bound);
+  for (auto matches = map.lower_bound(lower_bound);
+       matches != upper_bound_iterator; ++matches) {
+    for (const auto& match : matches->second) {
+      // Will zero initialize if entry missing
+      scored_emoji[match.emoji_string] +=
+          match.weighting / matches->first.size();
+    }
+  }
+  return scored_emoji;
+}
+
+std::vector<std::string> GetResultsFromMap(
+    const std::map<std::string, std::vector<EmojiSearchEntry>, std::less<>>&
+        map,
+    const std::string_view query) {
+  std::vector<std::string> words = base::SplitStringUsingSubstr(
+      query, " ", base::WhitespaceHandling::TRIM_WHITESPACE,
+      base::SplitResult::SPLIT_WANT_NONEMPTY);
+  if (words.empty()) {
+    return {};
+  }
+  std::unordered_map<std::string, double> scored_emoji =
+      GetResultsFromASingleWordQuery(map, words.back());
+  words.pop_back();
+  for (const std::string& word : words) {
+    std::unordered_map<std::string, double> newly_scored_emoji =
+        GetResultsFromASingleWordQuery(map, word);
+    for (const auto& already_scored_emoji : scored_emoji) {
+      auto it = newly_scored_emoji.find(already_scored_emoji.first);
+      if (it != newly_scored_emoji.end()) {
+        scored_emoji[already_scored_emoji.first] *= it->second;
+      } else {
+        scored_emoji[already_scored_emoji.first] = 0;
+      }
+    }
+  }
+  std::erase_if(scored_emoji, [](auto elem) { return elem.second == 0.0; });
+  std::vector<std::string> ret;
+  for (const auto& emoji : scored_emoji) {
+    ret.push_back(emoji.first);
+  }
+  // TODO(b/309343774): Sort first then project rather than project then sort
+  // which should be faster.
+  std::sort(ret.begin(), ret.end(),
+            [&scored_emoji](const std::string& a, const std::string& b) {
+              return scored_emoji.at(a) > scored_emoji.at(b);
+            });
+  return ret;
+}
+
+}  // namespace
+
+EmojiSearch::EmojiSearch() {
+  AddDataFromFileToMap(IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_REMAINING,
+                       emojis_);
+  AddDataFromFileToMap(IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_START,
+                       emojis_);
+  AddDataFromFileToMap(IDR_EMOJI_PICKER_SYMBOL_ORDERING_JSON, symbols_);
+  AddDataFromFileToMap(IDR_EMOJI_PICKER_EMOTICON_ORDERING_JSON, symbols_);
+}
+
+EmojiSearch::~EmojiSearch() = default;
+
+void EmojiSearch::SearchEmoji(
+    const std::string_view query,
+    emoji_search::mojom::EmojiSearch::SearchEmojiCallback callback) {
+  std::move(callback).Run(emoji_search::mojom::SearchResults::New(
+                              GetResultsFromMap(emojis_, query)),
+                          emoji_search::mojom::SearchResults::New(
+                              GetResultsFromMap(symbols_, query)),
+                          emoji_search::mojom::SearchResults::New(
+                              GetResultsFromMap(emoticons_, query)));
+}
+
+std::vector<std::string> EmojiSearch::AllResultsForTesting(
+    const std::string& query) {
+  std::vector<std::string> ret;
+  for (std::string& r : GetResultsFromMap(emojis_, query)) {
+    ret.push_back(std::move(r));
+  }
+  for (std::string& r : GetResultsFromMap(emoticons_, query)) {
+    ret.push_back(std::move(r));
+  }
+  for (std::string& r : GetResultsFromMap(symbols_, query)) {
+    ret.push_back(std::move(r));
+  }
+  return ret;
+}
+
+}  // namespace emoji
diff --git a/chromeos/ash/components/emoji/emoji_search.h b/chromeos/ash/components/emoji/emoji_search.h
new file mode 100644
index 0000000..c3e1ebc
--- /dev/null
+++ b/chromeos/ash/components/emoji/emoji_search.h
@@ -0,0 +1,43 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_ASH_COMPONENTS_EMOJI_EMOJI_SEARCH_H_
+#define CHROMEOS_ASH_COMPONENTS_EMOJI_EMOJI_SEARCH_H_
+
+#include <map>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include "chromeos/ash/components/emoji/emoji_search.mojom.h"
+
+namespace emoji {
+
+// Simple struct for storing a search weighting for a particular emoji.
+struct EmojiSearchEntry {
+  double weighting;
+  std::string emoji_string;
+};
+
+class EmojiSearch {
+ public:
+  EmojiSearch();
+  ~EmojiSearch();
+  EmojiSearch(const EmojiSearch&) = delete;
+  EmojiSearch& operator=(const EmojiSearch&) = delete;
+
+  void SearchEmoji(
+      std::string_view query,
+      emoji_search::mojom::EmojiSearch::SearchEmojiCallback callback);
+
+  std::vector<std::string> AllResultsForTesting(const std::string& query);
+
+ private:
+  std::map<std::string, std::vector<EmojiSearchEntry>, std::less<>> emojis_;
+  std::map<std::string, std::vector<EmojiSearchEntry>, std::less<>> emoticons_;
+  std::map<std::string, std::vector<EmojiSearchEntry>, std::less<>> symbols_;
+};
+}  // namespace emoji
+
+#endif  // CHROMEOS_ASH_COMPONENTS_EMOJI_EMOJI_SEARCH_H_
diff --git a/chromeos/ash/components/emoji/emoji_search.mojom b/chromeos/ash/components/emoji/emoji_search.mojom
new file mode 100644
index 0000000..fe8e1de
--- /dev/null
+++ b/chromeos/ash/components/emoji/emoji_search.mojom
@@ -0,0 +1,20 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module emoji_search.mojom;
+
+struct SearchResults {
+  array<string> results;
+};
+
+// Interface to allow users to search for emoji whose metadata match a query
+// string
+interface EmojiSearch {
+  // Lookup metadata to find emoji/symbol/emoticon matches for a given query.
+  SearchEmoji(string query) => (
+    SearchResults emoji_results,
+    SearchResults symbol_results,
+    SearchResults emoticon_results
+  );
+};
\ No newline at end of file
diff --git a/chromeos/ash/components/emoji/emoji_search_unittest.cc b/chromeos/ash/components/emoji/emoji_search_unittest.cc
new file mode 100644
index 0000000..295b9fdf
--- /dev/null
+++ b/chromeos/ash/components/emoji/emoji_search_unittest.cc
@@ -0,0 +1,244 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/ash/components/emoji/emoji_search.h"
+
+#include "base/containers/span.h"
+#include "chromeos/ash/components/emoji/grit/emoji.h"
+#include "ui/base/resource/mock_resource_bundle_delegate.h"
+
+namespace emoji {
+namespace {
+
+using ::testing::ElementsAre;
+using ::testing::Return;
+
+struct FakeResource {
+  int resource;
+  std::string data;
+};
+
+class ScopedFakeResourceBundleDelegate {
+ public:
+  explicit ScopedFakeResourceBundleDelegate(
+      base::span<const FakeResource> resources) {
+    original_resource_bundle_ =
+        ui::ResourceBundle::SwapSharedInstanceForTesting(nullptr);
+    ui::ResourceBundle::InitSharedInstanceWithLocale(
+        "en-US", &delegate_, ui::ResourceBundle::DO_NOT_LOAD_COMMON_RESOURCES);
+
+    for (const auto& [resource, data] : resources) {
+      EXPECT_CALL(delegate_, LoadDataResourceString(resource))
+          .WillOnce(Return(data));
+    }
+  }
+
+  ~ScopedFakeResourceBundleDelegate() {
+    ui::ResourceBundle::CleanupSharedInstance();
+    ui::ResourceBundle::SwapSharedInstanceForTesting(original_resource_bundle_);
+  }
+
+ private:
+  ui::MockResourceBundleDelegate delegate_;
+  raw_ptr<ui::ResourceBundle> original_resource_bundle_;
+};
+
+using EmojiSearchTest = testing::Test;
+
+TEST_F(EmojiSearchTest, FindsSmilingEmoji) {
+  ScopedFakeResourceBundleDelegate mock_resource_delegate(
+      {{FakeResource{
+            IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_START,
+            R"([{"emoji":[{"base":{"string":"😀","name":"grinning face",
+            "keywords":["face","grin","grinning face",":D",":smile:"]}}]}])"},
+        FakeResource{
+            IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_REMAINING,
+            R"([{"emoji":[{"base":{"string":"😀","name":"grinning face",
+            "keywords":["face","grin","grinning face",":D",":smile:"]}}]}])"},
+        FakeResource{IDR_EMOJI_PICKER_SYMBOL_ORDERING_JSON,
+                     R"([{"group":"Arrows","emoji":[{"base":
+            {"string":"←","name":"leftwards arrow"}}]}])"},
+        FakeResource{IDR_EMOJI_PICKER_EMOTICON_ORDERING_JSON,
+                     R"-([{"group":"Classic","emoji":[
+              {"base":{"string":":-)","name":"smiley face "}}]}])-"}}});
+
+  EmojiSearch search;
+
+  std::vector<std::string> results = search.AllResultsForTesting("face");
+
+  EXPECT_THAT(results, ElementsAre("😀", ":-)"));
+}
+
+TEST_F(EmojiSearchTest, MultiKeywordPartialMatch) {
+  ScopedFakeResourceBundleDelegate mock_resource_delegate(
+      {{FakeResource{
+            IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_START,
+            R"([{"emoji":[{"base":{"string":"😀","name":"grinning face",
+            "keywords":["face","grin","grinning face",":D",":smile:"]}}]}])"},
+        FakeResource{
+            IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_REMAINING,
+            R"([{"emoji":[{"base":{"string":"😀","name":"grinning face",
+            "keywords":["face","grin","grinning face",":D",":smile:"]}}]}])"},
+        FakeResource{IDR_EMOJI_PICKER_SYMBOL_ORDERING_JSON,
+                     R"([{"group":"Arrows","emoji":[{"base":
+            {"string":"←","name":"leftwards arrow"}}]}])"},
+        FakeResource{IDR_EMOJI_PICKER_EMOTICON_ORDERING_JSON,
+                     R"-([{"group":"Classic","emoji":[{"base":
+            {"string":":-)","name":"smiley face "}}]}])-"}}});
+
+  EmojiSearch search;
+
+  std::vector<std::string> results = search.AllResultsForTesting("gr fa");
+
+  EXPECT_THAT(results, ElementsAre("😀"));
+}
+
+TEST_F(EmojiSearchTest, FindsSmilingEmoticon) {
+  ScopedFakeResourceBundleDelegate mock_resource_delegate(
+      {{FakeResource{
+            IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_START,
+            R"([{"emoji":[{"base":{"string":"😀","name":"grinning face",
+            "keywords":["face","grin","grinning face",":D",":smile:"]}}]}])"},
+        FakeResource{
+            IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_REMAINING,
+            R"([{"emoji":[{"base":{"string":"😀","name":"grinning face",
+            "keywords":["face","grin","grinning face",":D",":smile:"]}}]}])"},
+        FakeResource{IDR_EMOJI_PICKER_SYMBOL_ORDERING_JSON,
+                     R"([{"group":"Arrows","emoji":[{"base":
+            {"string":"←","name":"leftwards arrow"}}]}])"},
+        FakeResource{IDR_EMOJI_PICKER_EMOTICON_ORDERING_JSON,
+                     R"-([{"group":"Classic","emoji":[{"base":
+            {"string":":-)","name":"smiley face "}}]}])-"}}});
+
+  EmojiSearch search;
+
+  std::vector<std::string> results = search.AllResultsForTesting("smiley");
+
+  EXPECT_THAT(results, ElementsAre(":-)"));
+}
+
+TEST_F(EmojiSearchTest, FindsSymbol) {
+  ScopedFakeResourceBundleDelegate mock_resource_delegate(
+      {{FakeResource{
+            IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_START,
+            R"([{"emoji":[{"base":{"string":"😀","name":"grinning face",
+            "keywords":["face","grin","grinning face",":D",":smile:"]}}]}])"},
+        FakeResource{
+            IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_REMAINING,
+            R"([{"emoji":[{"base":{"string":"😀","name":"grinning face",
+            "keywords":["face","grin","grinning face",":D",":smile:"]}}]}])"},
+        FakeResource{IDR_EMOJI_PICKER_SYMBOL_ORDERING_JSON,
+                     R"([{"group":"Arrows","emoji":[{"base":
+            {"string":"←","name":"leftwards arrow"}}]}])"},
+        FakeResource{IDR_EMOJI_PICKER_EMOTICON_ORDERING_JSON,
+                     R"-([{"group":"Classic","emoji":[{"base":
+            {"string":":-)","name":"smiley face "}}]}])-"}}});
+  EmojiSearch search;
+
+  std::vector<std::string> results = search.AllResultsForTesting("left");
+
+  EXPECT_THAT(results, ElementsAre("←"));
+}
+
+TEST_F(EmojiSearchTest, IgnoresCase) {
+  ScopedFakeResourceBundleDelegate mock_resource_delegate(
+      {{FakeResource{
+            IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_START,
+            R"([{"emoji":[{"base":{"string":"😀","name":"grinning face",
+            "keywords":["face","grin","grinning face",":D",":smile:"]}}]}])"},
+        FakeResource{
+            IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_REMAINING,
+            R"([{"emoji":[{"base":{"string":"😀","name":"grinning face",
+            "keywords":["face","grin","grinning face",":D",":smile:"]}}]}])"},
+        FakeResource{IDR_EMOJI_PICKER_SYMBOL_ORDERING_JSON,
+                     R"([{"group":"Arrows","emoji":[{"base":
+            {"string":"←","name":"leftwards arrow"}}]}])"},
+        FakeResource{IDR_EMOJI_PICKER_EMOTICON_ORDERING_JSON,
+                     R"-([{"group":"Classic","emoji":[{"base":
+            {"string":":-)","name":"smiley face "}}]}])-"}}});
+
+  EmojiSearch search;
+
+  std::vector<std::string> results = search.AllResultsForTesting("LEFT");
+
+  EXPECT_THAT(results, ElementsAre("←"));
+}
+
+TEST_F(EmojiSearchTest, WholeNameScoresHigherThanPartialMatch) {
+  ScopedFakeResourceBundleDelegate mock_resource_delegate(
+      {{FakeResource{
+            IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_START,
+            R"([{"emoji":[{"base":{"string":"😀","name":"grinning faceandmore",
+            "keywords":["face","grin","grinning face",":D",":smile:"]}}]}])"},
+        FakeResource{
+            IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_REMAINING,
+            R"([{"emoji":[{"base":{"string":"😀a","name":"grinning face",
+            "keywords":["face","grin","grinning face",":D",":smile:"]}}]}])"},
+        FakeResource{IDR_EMOJI_PICKER_SYMBOL_ORDERING_JSON,
+                     R"([{"group":"Arrows","emoji":[{"base":
+            {"string":"←","name":"leftwards arrow"}}]}])"},
+        FakeResource{IDR_EMOJI_PICKER_EMOTICON_ORDERING_JSON,
+                     R"-([{"group":"Classic","emoji":[{"base":
+            {"string":":-)","name":"smiley face "}}]}])-"}}});
+
+  EmojiSearch search;
+
+  std::vector<std::string> results =
+      search.AllResultsForTesting("grinning face");
+
+  EXPECT_THAT(results, ElementsAre("😀a", "😀"));
+}
+
+TEST_F(EmojiSearchTest, NameMatchScoresHigherThanKeyword) {
+  ScopedFakeResourceBundleDelegate mock_resource_delegate(
+      {{FakeResource{
+            IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_START,
+            R"([{"emoji":[{"base":{"string":"😀","name":"something else",
+            "keywords":["face","grin","grinning face",":D",":smile:"]}}]}])"},
+        FakeResource{
+            IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_REMAINING,
+            R"([{"emoji":[{"base":{"string":"😀a","name":"grinning face",
+            "keywords":["face","grin","grinning face",":D",":smile:"]}}]}])"},
+        FakeResource{IDR_EMOJI_PICKER_SYMBOL_ORDERING_JSON,
+                     R"([{"group":"Arrows","emoji":[{"base":
+            {"string":"←","name":"leftwards arrow"}}]}])"},
+        FakeResource{IDR_EMOJI_PICKER_EMOTICON_ORDERING_JSON,
+                     R"-([{"group":"Classic","emoji":[{"base":
+            {"string":":-)","name":"smiley face "}}]}])-"}}});
+
+  EmojiSearch search;
+
+  std::vector<std::string> results =
+      search.AllResultsForTesting("grinning face");
+
+  EXPECT_THAT(results, ElementsAre("😀a", "😀"));
+}
+
+TEST_F(EmojiSearchTest, KeywordPartialScoresHigherThanFullKeywordMatch) {
+  ScopedFakeResourceBundleDelegate mock_resource_delegate(
+      {{FakeResource{
+            IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_START,
+            R"([{"emoji":[{"base":{"string":"😀","name":"something else",
+            "keywords":["face","grin","grinning face",":D",":smile:"]}}]}])"},
+        FakeResource{
+            IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_REMAINING,
+            R"([{"emoji":[{"base":{"string":"😀a","name":"something else",
+            "keywords":["face","grin","grinning face with something else",":D",":smile:"]}}]}])"},
+        FakeResource{IDR_EMOJI_PICKER_SYMBOL_ORDERING_JSON,
+                     R"([{"group":"Arrows","emoji":[{"base":
+            {"string":"←","name":"leftwards arrow"}}]}])"},
+        FakeResource{IDR_EMOJI_PICKER_EMOTICON_ORDERING_JSON,
+                     R"-([{"group":"Classic","emoji":[{"base":
+            {"string":":-)","name":"smiley face "}}]}])-"}}});
+
+  EmojiSearch search;
+
+  std::vector<std::string> results =
+      search.AllResultsForTesting("grinning face");
+
+  EXPECT_THAT(results, ElementsAre("😀", "😀a"));
+}
+
+}  // namespace
+}  // namespace emoji
diff --git a/chromeos/ash/components/emoji/tools/emoji_data.py b/chromeos/ash/components/emoji/tools/emoji_data.py
index f276e49..519f3f3 100755
--- a/chromeos/ash/components/emoji/tools/emoji_data.py
+++ b/chromeos/ash/components/emoji/tools/emoji_data.py
@@ -196,7 +196,6 @@
         out.append({'emoji': newGroup})
     return out
 
-
 def main(args):
     parser = argparse.ArgumentParser()
     parser.add_argument('--metadata',
diff --git a/chromeos/ash/components/language_packs/language_pack_manager.cc b/chromeos/ash/components/language_packs/language_pack_manager.cc
index 98bbba78..c8222feb 100644
--- a/chromeos/ash/components/language_packs/language_pack_manager.cc
+++ b/chromeos/ash/components/language_packs/language_pack_manager.cc
@@ -303,6 +303,9 @@
           {{kTtsFeatureId, "uk"}, "tts-uk-ua-c"},
           {{kTtsFeatureId, "vi"}, "tts-vi-vn-c"},
           {{kTtsFeatureId, "yue"}, "tts-yue-hk-c"},
+
+          // Fonts.
+          {{kFontsFeatureId, "ja"}, "extrafonts-ja"},
       });
 
   return *all_dlc_ids;
diff --git a/chromeos/ash/components/language_packs/language_pack_manager.h b/chromeos/ash/components/language_packs/language_pack_manager.h
index 5bad545..a22a615 100644
--- a/chromeos/ash/components/language_packs/language_pack_manager.h
+++ b/chromeos/ash/components/language_packs/language_pack_manager.h
@@ -27,6 +27,7 @@
 // All Language Pack IDs are listed here.
 inline constexpr char kHandwritingFeatureId[] = "LP_ID_HANDWRITING";
 inline constexpr char kTtsFeatureId[] = "LP_ID_TTS";
+inline constexpr char kFontsFeatureId[] = "LP_ID_FONT";
 
 // Feature IDs.
 // These values are persisted to logs. Entries should not be renumbered and
diff --git a/chromeos/ash/components/language_packs/language_packs_util.cc b/chromeos/ash/components/language_packs/language_packs_util.cc
index 6ad2154465..f3fd58b 100644
--- a/chromeos/ash/components/language_packs/language_packs_util.cc
+++ b/chromeos/ash/components/language_packs/language_packs_util.cc
@@ -14,6 +14,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "chromeos/ash/components/dbus/dlcservice/dlcservice_client.h"
+#include "chromeos/ash/components/language_packs/language_pack_manager.h"
 #include "components/language/core/common/locale_util.h"
 #include "components/prefs/pref_service.h"
 #include "components/session_manager/core/session_manager.h"
@@ -178,6 +179,9 @@
     return ResolveLocaleForHandwriting(locale);
   } else if (feature_id == kTtsFeatureId) {
     return ResolveLocaleForTts(locale);
+  } else if (feature_id == kFontsFeatureId) {
+    // Language pack resolution is handled by the client.
+    return locale;
   } else {
     DLOG(ERROR) << "ResolveLocale called with wrong feature_id";
     return "";
diff --git a/chromeos/ash/components/language_packs/language_packs_util_unittest.cc b/chromeos/ash/components/language_packs/language_packs_util_unittest.cc
index d567ff3..24838b1 100644
--- a/chromeos/ash/components/language_packs/language_packs_util_unittest.cc
+++ b/chromeos/ash/components/language_packs/language_packs_util_unittest.cc
@@ -181,6 +181,12 @@
   EXPECT_EQ(ResolveLocale(kTtsFeatureId, "ja-jp"), "ja");
 }
 
+TEST(LanguagePacksUtil, ResolveLocaleFonts) {
+  // Language pack resolution is handled by the client.
+  EXPECT_EQ(ResolveLocale(kFontsFeatureId, "ja"), "ja");
+  EXPECT_EQ(ResolveLocale(kFontsFeatureId, "ko"), "ko");
+}
+
 TEST(LanguagePacksUtil, MapThenFilterStringsNoInput) {
   EXPECT_THAT(MapThenFilterStrings(
                   {}, base::BindRepeating(
diff --git a/chromeos/ash/components/network/metrics/BUILD.gn b/chromeos/ash/components/network/metrics/BUILD.gn
index 0da88b4..b4e26e8 100644
--- a/chromeos/ash/components/network/metrics/BUILD.gn
+++ b/chromeos/ash/components/network/metrics/BUILD.gn
@@ -33,6 +33,8 @@
     "connection_info_metrics_logger.h",
     "connection_results.cc",
     "connection_results.h",
+    "default_network_metrics_logger.cc",
+    "default_network_metrics_logger.h",
     "esim_policy_login_metrics_logger.cc",
     "esim_policy_login_metrics_logger.h",
     "hotspot_feature_usage_metrics.cc",
@@ -70,6 +72,7 @@
   sources = [
     "cellular_network_metrics_logger_unittest.cc",
     "connection_info_metrics_logger_unittest.cc",
+    "default_network_metrics_logger_unittest.cc",
     "esim_policy_login_metrics_logger_unittest.cc",
     "hotspot_feature_usage_metrics_unittest.cc",
     "hotspot_metrics_helper_unittest.cc",
diff --git a/chromeos/ash/components/network/metrics/default_network_metrics_logger.cc b/chromeos/ash/components/network/metrics/default_network_metrics_logger.cc
new file mode 100644
index 0000000..f6f0a993
--- /dev/null
+++ b/chromeos/ash/components/network/metrics/default_network_metrics_logger.cc
@@ -0,0 +1,99 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/ash/components/network/metrics/default_network_metrics_logger.h"
+
+#include "base/logging.h"
+#include "base/metrics/histogram_functions.h"
+#include "chromeos/ash/components/network/network_state.h"
+#include "chromeos/ash/components/network/network_state_handler.h"
+
+namespace ash {
+
+// static
+const char DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram[] =
+    "Network.Ash.DefaultNetwork.MeterSubtype";
+
+DefaultNetworkMetricsLogger::DefaultNetworkMetricsLogger() = default;
+
+DefaultNetworkMetricsLogger::~DefaultNetworkMetricsLogger() = default;
+
+void DefaultNetworkMetricsLogger::Init(
+    NetworkStateHandler* network_state_handler) {
+  DCHECK(network_state_handler);
+  network_state_handler_ = network_state_handler;
+  network_state_handler_observer_.Observe(network_state_handler_.get());
+  UpdateAndRecordTechnologyMeterSubtype();
+}
+
+void DefaultNetworkMetricsLogger::OnShuttingDown() {
+  network_state_handler_observer_.Reset();
+}
+
+void DefaultNetworkMetricsLogger::DefaultNetworkChanged(
+    const NetworkState* network) {
+  UpdateAndRecordTechnologyMeterSubtype();
+}
+
+void DefaultNetworkMetricsLogger::UpdateAndRecordTechnologyMeterSubtype() {
+  NetworkStateHandler::NetworkStateList active_networks;
+
+  // Ignore VPN since it is always on top of a physical network.
+  const NetworkState* default_non_vpn_network =
+      network_state_handler_->ActiveNetworkByType(
+          NetworkTypePattern::NonVirtual());
+
+  if (!default_non_vpn_network) {
+    return;
+  }
+
+  const std::string guid = default_non_vpn_network->guid();
+  bool is_metered = default_non_vpn_network->metered();
+
+  if (!guid.empty() && guid_ == guid && is_metered_ == is_metered) {
+    return;
+  }
+
+  guid_ = guid;
+  is_metered_ = is_metered;
+
+  if (auto subtype = GetNetworkTechnologyMeterSubtype(default_non_vpn_network);
+      subtype) {
+    base::UmaHistogramEnumeration(kDefaultNetworkMeterSubtypeHistogram,
+                                  *subtype);
+  }
+}
+
+std::optional<DefaultNetworkMetricsLogger::NetworkTechnologyMeterSubtype>
+DefaultNetworkMetricsLogger::GetNetworkTechnologyMeterSubtype(
+    const NetworkState* network) {
+  if (!network) {
+    return absl::nullopt;
+  }
+
+  switch (network->GetNetworkTechnologyType()) {
+    case NetworkState::NetworkTechnologyType::kCellular:
+      return network->metered()
+                 ? NetworkTechnologyMeterSubtype::kCellularMetered
+                 : NetworkTechnologyMeterSubtype::kCellular;
+    case NetworkState::NetworkTechnologyType::kEthernetEap:
+      [[fallthrough]];
+    case NetworkState::NetworkTechnologyType::kEthernet:
+      return NetworkTechnologyMeterSubtype::kEthernet;
+    case NetworkState::NetworkTechnologyType::kWiFi:
+      return network->metered() ? NetworkTechnologyMeterSubtype::kWifiMetered
+                                : NetworkTechnologyMeterSubtype::kWifi;
+    case NetworkState::NetworkTechnologyType::kTether:
+      return network->metered() ? NetworkTechnologyMeterSubtype::kTetherMetered
+                                : NetworkTechnologyMeterSubtype::kTether;
+    case NetworkState::NetworkTechnologyType::kVPN:
+      [[fallthrough]];
+    case NetworkState::NetworkTechnologyType::kUnknown:
+      [[fallthrough]];
+    default:
+      return absl::nullopt;
+  }
+}
+
+}  // namespace ash
diff --git a/chromeos/ash/components/network/metrics/default_network_metrics_logger.h b/chromeos/ash/components/network/metrics/default_network_metrics_logger.h
new file mode 100644
index 0000000..50690cf
--- /dev/null
+++ b/chromeos/ash/components/network/metrics/default_network_metrics_logger.h
@@ -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.
+
+#ifndef CHROMEOS_ASH_COMPONENTS_NETWORK_METRICS_DEFAULT_NETWORK_METRICS_LOGGER_H_
+#define CHROMEOS_ASH_COMPONENTS_NETWORK_METRICS_DEFAULT_NETWORK_METRICS_LOGGER_H_
+
+#include "base/component_export.h"
+#include "base/memory/raw_ptr.h"
+#include "chromeos/ash/components/network/network_state_handler_observer.h"
+
+namespace ash {
+
+class NetworkState;
+class NetworkStateHandler;
+
+// Class for tracking info about the default network.
+//
+// This class adds observers on network state and currently makes the
+// following measurements on the default networks:
+//
+// 1. The possible metered and non-metered technology type of the first
+//    active/default network that is non-virtual when the active/default
+//    network changes or when the metered property changes. Note that if
+//    the default/active network drops and then reconnects without another
+//    network taking its place, this metric will not be emitted.
+//
+// Note: This class does not start logging metrics until Init() is
+// invoked.
+class COMPONENT_EXPORT(CHROMEOS_NETWORK) DefaultNetworkMetricsLogger
+    : public NetworkStateHandlerObserver {
+ public:
+  DefaultNetworkMetricsLogger();
+  DefaultNetworkMetricsLogger(const DefaultNetworkMetricsLogger&) = delete;
+  DefaultNetworkMetricsLogger& operator=(const DefaultNetworkMetricsLogger&) =
+      delete;
+  ~DefaultNetworkMetricsLogger() override;
+
+  void Init(NetworkStateHandler* network_state_handler);
+
+ private:
+  friend class DefaultNetworkMetricsLoggerTest;
+  FRIEND_TEST_ALL_PREFIXES(DefaultNetworkMetricsLoggerTest,
+                           NetworkTechnologyMeterSubtypeChanges);
+
+  // Histograms associated with SIM Pin operations.
+  static const char kDefaultNetworkMeterSubtypeHistogram[];
+
+  // Corresponds to NetworkTechnologyMeterSubtype in network/enums.xml. These
+  // values are persisted to logs. Entries should not be renumbered and numeric
+  // values should never be reused.
+  enum class NetworkTechnologyMeterSubtype {
+    kEthernet = 0,
+    kWifi = 1,
+    kWifiMetered = 2,
+    kCellular = 3,
+    kCellularMetered = 4,
+    kTether = 5,
+    kTetherMetered = 6,
+    kMaxValue = kTetherMetered
+  };
+
+  // NetworkStateHandlerObserver::
+  void DefaultNetworkChanged(const NetworkState* network) override;
+  void OnShuttingDown() override;
+
+  void UpdateAndRecordTechnologyMeterSubtype();
+
+  std::optional<NetworkTechnologyMeterSubtype> GetNetworkTechnologyMeterSubtype(
+      const NetworkState* network);
+
+  std::string guid_;
+  bool is_metered_;
+  raw_ptr<NetworkStateHandler> network_state_handler_ = nullptr;
+
+  NetworkStateHandlerScopedObservation network_state_handler_observer_{this};
+};
+
+}  // namespace ash
+
+#endif  // CHROMEOS_ASH_COMPONENTS_NETWORK_METRICS_DEFAULT_NETWORK_METRICS_LOGGER_H_
diff --git a/chromeos/ash/components/network/metrics/default_network_metrics_logger_unittest.cc b/chromeos/ash/components/network/metrics/default_network_metrics_logger_unittest.cc
new file mode 100644
index 0000000..c83234e
--- /dev/null
+++ b/chromeos/ash/components/network/metrics/default_network_metrics_logger_unittest.cc
@@ -0,0 +1,210 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/ash/components/network/metrics/default_network_metrics_logger.h"
+
+#include <memory>
+
+#include "base/memory/raw_ptr.h"
+#include "base/run_loop.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/task_environment.h"
+#include "chromeos/ash/components/dbus/shill/shill_service_client.h"
+#include "chromeos/ash/components/network/network_handler_test_helper.h"
+#include "chromeos/ash/components/network/network_state_handler.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace ash {
+
+namespace {
+
+const char kCellularGuid[] = "cellular_guid";
+const char kCellularServicePath[] = "/service/network";
+const char kCellularName[] = "cellular_name";
+
+const char kWifiGuid[] = "wifi_guid";
+const char kWifiServicePath[] = "/service/network2";
+const char kWifiName[] = "wifi_name";
+
+const char kEthernetGuid[] = "eth_guid";
+const char kEthernetServicePath[] = "/service/network3";
+const char kEthernetName[] = "eth_name";
+
+const char kVpnGuid[] = "vpn_guid";
+const char kVpnServicePath[] = "/service/network4";
+const char kVpnName[] = "vpn_name";
+
+}  // namespace
+
+class DefaultNetworkMetricsLoggerTest : public testing::Test {
+ public:
+  DefaultNetworkMetricsLoggerTest() = default;
+  DefaultNetworkMetricsLoggerTest(const DefaultNetworkMetricsLoggerTest&) =
+      delete;
+  DefaultNetworkMetricsLoggerTest& operator=(
+      const DefaultNetworkMetricsLoggerTest&) = delete;
+  ~DefaultNetworkMetricsLoggerTest() override = default;
+
+  void SetUp() override {
+    network_handler_test_helper_ = std::make_unique<NetworkHandlerTestHelper>();
+    histogram_tester_ = std::make_unique<base::HistogramTester>();
+
+    GetShillServiceClient()->ClearServices();
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void TearDown() override {
+    GetShillServiceClient()->ClearServices();
+    network_handler_test_helper_.reset();
+  }
+
+  void SetUpGenericCellularNetwork() {
+    GetShillServiceClient()->AddService(kCellularServicePath, kCellularGuid,
+                                        kCellularName, shill::kTypeCellular,
+                                        shill::kStateIdle,
+                                        /*visible=*/true);
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void SetUpGenericWifiNetwork() {
+    GetShillServiceClient()->AddService(kWifiServicePath, kWifiGuid, kWifiName,
+                                        shill::kTypeWifi, shill::kStateIdle,
+                                        /*visible=*/true);
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void SetUpGenericEthernetNetwork() {
+    GetShillServiceClient()->AddService(kEthernetServicePath, kEthernetGuid,
+                                        kEthernetName, shill::kTypeEthernet,
+                                        shill::kStateIdle,
+                                        /*visible=*/true);
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void SetUpGenericVpnNetwork() {
+    GetShillServiceClient()->AddService(kVpnServicePath, kVpnGuid, kVpnName,
+                                        shill::kTypeVPN, shill::kStateIdle,
+                                        /*visible=*/true);
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void SetShillState(const std::string& service_path,
+                     const std::string& shill_state) {
+    GetShillServiceClient()->SetServiceProperty(
+        service_path, shill::kStateProperty, base::Value(shill_state));
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void SetShillMetered(const std::string& service_path, bool is_metered) {
+    GetShillServiceClient()->SetServiceProperty(
+        service_path, shill::kMeteredProperty, base::Value(is_metered));
+    base::RunLoop().RunUntilIdle();
+  }
+
+ protected:
+  ShillServiceClient::TestInterface* GetShillServiceClient() {
+    return ShillServiceClient::Get()->GetTestInterface();
+  }
+
+  base::test::TaskEnvironment task_environment_;
+  std::unique_ptr<base::HistogramTester> histogram_tester_;
+  std::unique_ptr<NetworkHandlerTestHelper> network_handler_test_helper_;
+};
+
+TEST_F(DefaultNetworkMetricsLoggerTest, NetworkTechnologyMeterSubtypeChanges) {
+  using NetworkTechnologyMeterSubtype =
+      DefaultNetworkMetricsLogger::NetworkTechnologyMeterSubtype;
+
+  SetUpGenericVpnNetwork();
+  SetUpGenericCellularNetwork();
+  SetUpGenericWifiNetwork();
+  SetUpGenericEthernetNetwork();
+
+  histogram_tester_->ExpectTotalCount(
+      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram, 0);
+
+  SetShillState(kEthernetServicePath, shill::kStateOnline);
+  histogram_tester_->ExpectTotalCount(
+      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram, 1);
+  histogram_tester_->ExpectBucketCount(
+      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram,
+      NetworkTechnologyMeterSubtype::kEthernet, 1);
+
+  // Cellular becomes connected. Note that cellular is metered by default.
+  // No impact on metrics because connected ethernet is higher in priority.
+  SetShillState(kCellularServicePath, shill::kStateOnline);
+  histogram_tester_->ExpectTotalCount(
+      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram, 1);
+
+  // Ethernet goes offline, so connected metered cellular becomes default.
+  SetShillState(kEthernetServicePath, shill::kStateIdle);
+  histogram_tester_->ExpectTotalCount(
+      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram, 2);
+  histogram_tester_->ExpectBucketCount(
+      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram,
+      NetworkTechnologyMeterSubtype::kCellularMetered, 1);
+
+  // Cellular becomes non-metered.
+  SetShillMetered(kCellularServicePath, false);
+  histogram_tester_->ExpectTotalCount(
+      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram, 3);
+  histogram_tester_->ExpectBucketCount(
+      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram,
+      NetworkTechnologyMeterSubtype::kCellular, 1);
+
+  // WiFi becomes online, which has higher priority than cellular.
+  SetShillState(kWifiServicePath, shill::kStateOnline);
+  histogram_tester_->ExpectTotalCount(
+      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram, 4);
+  histogram_tester_->ExpectBucketCount(
+      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram,
+      NetworkTechnologyMeterSubtype::kWifi, 1);
+
+  // WiFi becomes metered, which has higher priority than cellular.
+  SetShillMetered(kWifiServicePath, true);
+  histogram_tester_->ExpectTotalCount(
+      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram, 5);
+  histogram_tester_->ExpectBucketCount(
+      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram,
+      NetworkTechnologyMeterSubtype::kWifiMetered, 1);
+
+  // Ethernet becomes online, which has highest priority.
+  SetShillState(kEthernetServicePath, shill::kStateOnline);
+  // Connected WiFi becomes nonmetered. No effect on metrics since it's lower in
+  // priority than connected Ethernet.
+  SetShillMetered(kWifiServicePath, false);
+  histogram_tester_->ExpectTotalCount(
+      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram, 6);
+  histogram_tester_->ExpectBucketCount(
+      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram,
+      NetworkTechnologyMeterSubtype::kEthernet, 2);
+
+  // Ethernet goes offline, so connected nonmetered WiFi becomes default.
+  SetShillState(kEthernetServicePath, shill::kStateIdle);
+  // Cellular becomes metered. No effect on metrics since it's lower in
+  // priority than connected WiFi.
+  SetShillMetered(kCellularServicePath, true);
+  histogram_tester_->ExpectTotalCount(
+      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram, 7);
+  histogram_tester_->ExpectBucketCount(
+      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram,
+      NetworkTechnologyMeterSubtype::kWifi, 2);
+
+  // WiFi goes offline, so connected metered cellular becomes default.
+  SetShillState(kWifiServicePath, shill::kStateIdle);
+  histogram_tester_->ExpectTotalCount(
+      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram, 8);
+  histogram_tester_->ExpectBucketCount(
+      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram,
+      NetworkTechnologyMeterSubtype::kCellularMetered, 2);
+
+  // VPN is ignored.
+  SetShillState(kVpnServicePath, shill::kStateIdle);
+  SetShillState(kVpnServicePath, shill::kStateOnline);
+  histogram_tester_->ExpectTotalCount(
+      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram, 8);
+}
+
+}  // namespace ash
diff --git a/chromeos/ash/components/network/metrics/network_metrics_helper_unittest.cc b/chromeos/ash/components/network/metrics/network_metrics_helper_unittest.cc
index 4f76f21..18203b9 100644
--- a/chromeos/ash/components/network/metrics/network_metrics_helper_unittest.cc
+++ b/chromeos/ash/components/network/metrics/network_metrics_helper_unittest.cc
@@ -701,7 +701,7 @@
 }
 
 TEST_F(NetworkMetricsHelperTest, VPN) {
-  const std::vector<const std::string> kProviders{{
+  const std::vector<std::string> kProviders{{
       shill::kProviderIKEv2,
       shill::kProviderL2tpIpsec,
       shill::kProviderArcVpn,
diff --git a/chromeos/ash/components/network/network_handler.cc b/chromeos/ash/components/network/network_handler.cc
index bb698b4..c48e7537 100644
--- a/chromeos/ash/components/network/network_handler.cc
+++ b/chromeos/ash/components/network/network_handler.cc
@@ -33,6 +33,7 @@
 #include "chromeos/ash/components/network/managed_network_configuration_handler_impl.h"
 #include "chromeos/ash/components/network/metrics/cellular_network_metrics_logger.h"
 #include "chromeos/ash/components/network/metrics/connection_info_metrics_logger.h"
+#include "chromeos/ash/components/network/metrics/default_network_metrics_logger.h"
 #include "chromeos/ash/components/network/metrics/esim_policy_login_metrics_logger.h"
 #include "chromeos/ash/components/network/metrics/hotspot_feature_usage_metrics.h"
 #include "chromeos/ash/components/network/metrics/hotspot_metrics_helper.h"
@@ -85,6 +86,7 @@
   managed_cellular_pref_handler_.reset(new ManagedCellularPrefHandler());
   cellular_metrics_logger_.reset(new CellularMetricsLogger());
   connection_info_metrics_logger_.reset(new ConnectionInfoMetricsLogger());
+  default_network_metrics_logger_.reset(new DefaultNetworkMetricsLogger());
   hotspot_allowed_flag_handler_.reset(new HotspotAllowedFlagHandler());
   vpn_network_metrics_helper_.reset(new VpnNetworkMetricsHelper());
   hidden_network_handler_.reset(new HiddenNetworkHandler());
@@ -207,6 +209,7 @@
                                  managed_network_configuration_handler_.get());
   connection_info_metrics_logger_->Init(network_state_handler_.get(),
                                         network_connection_handler_.get());
+  default_network_metrics_logger_->Init(network_state_handler_.get());
   vpn_network_metrics_helper_->Init(network_configuration_handler_.get());
   if (client_cert_resolver_) {
     client_cert_resolver_->Init(network_state_handler_.get(),
diff --git a/chromeos/ash/components/network/network_handler.h b/chromeos/ash/components/network/network_handler.h
index a8a8cbe..9e11cbb8 100644
--- a/chromeos/ash/components/network/network_handler.h
+++ b/chromeos/ash/components/network/network_handler.h
@@ -26,6 +26,7 @@
 class CellularPolicyHandler;
 class ClientCertResolver;
 class ConnectionInfoMetricsLogger;
+class DefaultNetworkMetricsLogger;
 class EnterpriseManagedMetadataStore;
 class EphemeralNetworkConfigurationHandler;
 class EphemeralNetworkPoliciesEnablementHandler;
@@ -179,6 +180,7 @@
   std::unique_ptr<ManagedCellularPrefHandler> managed_cellular_pref_handler_;
   std::unique_ptr<CellularMetricsLogger> cellular_metrics_logger_;
   std::unique_ptr<ConnectionInfoMetricsLogger> connection_info_metrics_logger_;
+  std::unique_ptr<DefaultNetworkMetricsLogger> default_network_metrics_logger_;
   std::unique_ptr<HiddenNetworkHandler> hidden_network_handler_;
   std::unique_ptr<EnterpriseManagedMetadataStore>
       enterprise_managed_metadata_store_;
diff --git a/chromeos/ash/components/network/network_state.cc b/chromeos/ash/components/network/network_state.cc
index d849f14..e1a2603 100644
--- a/chromeos/ash/components/network/network_state.cc
+++ b/chromeos/ash/components/network/network_state.cc
@@ -178,6 +178,8 @@
     return GetBooleanValue(key, value, &hidden_ssid_);
   } else if (key == shill::kPasspointIDProperty) {
     return GetStringValue(key, value, &passpoint_id_);
+  } else if (key == shill::kMeteredProperty) {
+    return GetBooleanValue(key, value, &metered_);
   } else if (key == shill::kOutOfCreditsProperty) {
     return GetBooleanValue(key, value, &cellular_out_of_credits_);
   } else if (key == shill::kIccidProperty) {
diff --git a/chromeos/ash/components/network/network_state.h b/chromeos/ash/components/network/network_state.h
index 72c66e5..55524d6 100644
--- a/chromeos/ash/components/network/network_state.h
+++ b/chromeos/ash/components/network/network_state.h
@@ -156,7 +156,7 @@
   }
   bool hidden_ssid() const { return hidden_ssid_; }
   const std::string& passpoint_id() const { return passpoint_id_; }
-
+  bool metered() const { return metered_; }
   // Wifi property accessors
   const std::string& eap_method() const { return eap_method_; }
   const std::vector<uint8_t>& raw_ssid() const { return raw_ssid_; }
@@ -372,6 +372,7 @@
   bool blocked_by_policy_ = false;
   bool hidden_ssid_ = false;
   std::string passpoint_id_;
+  bool metered_ = false;
 
   // Cellular properties, used for icons, Connect, and Activation.
   std::string eid_;
diff --git a/chromeos/ash/components/report/device_metrics/actives/twenty_eight_day_impl_unittest.cc b/chromeos/ash/components/report/device_metrics/actives/twenty_eight_day_impl_unittest.cc
index 7311345..bf090623 100644
--- a/chromeos/ash/components/report/device_metrics/actives/twenty_eight_day_impl_unittest.cc
+++ b/chromeos/ash/components/report/device_metrics/actives/twenty_eight_day_impl_unittest.cc
@@ -499,6 +499,8 @@
         prefs::kDeviceActiveChurnObservationMonthlyPingTimestamp);
     GetLocalState()->ClearPref(prefs::kDeviceActiveLastKnownChurnActiveStatus);
     GetLocalState()->ClearPref(
+        prefs::kDeviceActiveChurnObservationFirstObservedNewChurnMetadata);
+    GetLocalState()->ClearPref(
         prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus0);
     GetLocalState()->ClearPref(
         prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus1);
diff --git a/chromeos/ash/services/bluetooth_config/README.md b/chromeos/ash/services/bluetooth_config/README.md
new file mode 100644
index 0000000..829aeda
--- /dev/null
+++ b/chromeos/ash/services/bluetooth_config/README.md
@@ -0,0 +1,253 @@
+# CrosBluetoothConfig
+
+[`CrosBluetoothConfig`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom;drc=321047b607bc69f5d6dce6e47319d0c198d0616e)
+is a high-level Bluetooth API implemented using [Mojo](https://source.chromium.org/chromium/chromium/src/+/main:mojo/README.md;drc=d5888c9f83d076bfba4e3bb5d749b182d35610ec)
+and is the primary Bluetooth API used by clients within Chrome.
+
+This document describes various details of the API such as what it does, how
+the API can be used, what the primary clients of the API are, and what its
+dependencies are.
+
+## ChromeOS Bluetooth Design
+
+Within Chrome, Bluetooth functionality is exposed via
+[//device/bluetooth](https://source.chromium.org/chromium/chromium/src/+/main:device/bluetooth/;drc=fad13429ad5c09a01d5ee5a58f4575f2affc4abd)
+(see [README](https://source.chromium.org/chromium/chromium/src/+/main:device/bluetooth/README.md;drc=cf64a67e4ef4684287ca64db4948cb2e6f25b492)).
+The entrypoint to this library is [BluetoothAdapterFactory](https://source.chromium.org/chromium/chromium/src/+/main:device/bluetooth/bluetooth_adapter_factory.h;drc=bceeb1f38c2dad765b51d012fdf6c32ca36108ae),
+which exposes an API for interacting with the [BluetoothAdapter](https://source.chromium.org/chromium/chromium/src/+/main:device/bluetooth/bluetooth_adapter.h;drc=84d29de4c159e89404aceafbfecaeecba6443e15).
+On ChromeOS, `BluetoothAdapter` utilizes [`FlossDBusManager`](https://source.chromium.org/chromium/chromium/src/+/main:device/bluetooth/floss/floss_dbus_manager.h;drc=3ed9cf1119782c06147dac3bf548d5a1f7ee9336)
+or [`BluezDBusManager`](https://source.chromium.org/chromium/chromium/src/+/main:device/bluetooth/dbus/bluez_dbus_manager.h;drc=bb9cd02c17a2e8a8bdeca3712d357ca3d15a95bf),
+depending on whether Floss or BlueZ is being used, to communicate with the
+Bluetooth stack library via D-Bus. //device/bluetooth is not specific to
+ChromeOS, and a lot of it is shared with other platforms and products (e.g.,
+Chrome Browser).
+
+`BluetoothAdapter` exposes APIs for powering Bluetooth on and off, discovering,
+pairing and connecting to devices, and sending/receiving information from
+these devices.
+
+Because Chromium supports other operating systems, the `BluetoothAdapter` class
+and the interfaces it exposes are intentionally generic and use different
+implementations depending on the platform. Thus, in order to implement
+Bluetooth UI in ChromeOS, there is additional logic wrapping.
+
+There are four top-level UI surfaces used for ChromeOS Bluetooth:
+
+* Quick Settings: Native UI written in C++
+* Settings: WebUI written in Polymer
+* Pairing Dialog: WebUI written in Polymer
+* OOBE UI: WebUI written in Polymer, implemented using WebUI message handlers
+
+![CrosBluetoothConfig](https://screenshot.googleplex.com/8iiMoNmUFXFGSfw.png)
+
+The diagram above shows a simplified view of these relationships, in which
+these all use `CrosBluetoothConfig`.
+
+The `CrosBluetoothConfig` API does not contain all Bluetooth functionality in
+ChromeOS. Specifically, some ChromeOS features (e.g., Nearby Share, Phone Hub,
+Instant Tethering, Smart Lock) utilize lower-level Bluetooth APIs which are not
+directly exposed by the UI. However, most Chrome clients should default to
+using `CrosBluetoothConfig` unless specific, lower-level functionality is
+required.
+
+### Bluetooth Stack
+
+Currently in ChromeOS, there are two Bluetooth stacks: BlueZ and Floss. BlueZ
+is the legacy stack that is being replaced by Floss (as of Q4 2023). For more
+information see [here](https://sites.google.com/corp/google.com/flossproject/home).
+
+## API
+
+`CrosBluetoothConfig` functionality resides in
+[//chromeos/ash/services/bluetooth_config/](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/services/bluetooth_config/;drc=a906730dd23299a558eb3783dafcd6f24186bbe8).
+Mojo interfaces are defined within a
+[public/mojom](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/services/bluetooth_config/public/mojom/;drc=321047b607bc69f5d6dce6e47319d0c198d0616e)
+subdirectory. The primary interface `CrosBluetoothConfig` has a concrete
+implementation within the top-level directory.
+
+`CrosBluetoothConfig`'s [dependencies](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/services/bluetooth_config/cros_bluetooth_config.h;l=41-43;drc=d8468bb60e224d8797b843ee9d0258862bcbe87f)
+are various lower-level Bluetooth classes such as `BluetoothAdapter` and
+`BluetoothDevice`, `UserManager`, system prefs (both device-wide
+"local" prefs, and per-profile prefs belonging to the primary login) and
+`FastPairDelegate`.
+
+### Bluetooth State
+
+The following APIs can be used by clients for observing and modifying the
+Bluetooth state:
+```
+// Notifies observer with initial set of Bluetooth properties when observer
+// is first added, then again whenever properties are updated.
+ObserveSystemProperties(pending_remote<SystemPropertiesObserver> observer);
+
+// Informs observer when a device is newly paired, connected or
+// disconnected.
+ObserveDeviceStatusChanges(
+  pending_remote<BluetoothDeviceStatusObserver> observer);
+
+// Turns Bluetooth on or off.
+SetBluetoothEnabledState(bool enabled);
+```
+Clients that wish to fetch the Bluetooth adapter's power state or the paired
+device list should use
+[`ObserveSystemProperties()`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom;l=316;drc=321047b607bc69f5d6dce6e47319d0c198d0616e).
+To update the adapter's power state clients should use
+[`SetBluetoothEnabledState()`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom;l=331;drc=321047b607bc69f5d6dce6e47319d0c198d0616e).
+
+### Bluetooth Device Properties
+
+Unpaired Bluetooth devices are represented as `BluetoothDeviceProperties`, and
+paired Bluetooth devices are represented as `PairedBluetoothDeviceProperties`
+which is composed of `BluetoothDeviceProperties` and additional paired-specific
+properties. Device information such as address, type, and battery level can be
+accessed through these properties.
+```
+// Properties belonging to a Bluetooth device.
+struct BluetoothDeviceProperties {
+  // Unique identifier for this device, which is stable across device reboots.
+  string id;
+
+  // The Bluetooth address of this device.
+  string address;
+
+  // Publicly-visible name provided by the device. If no name is provided by
+  // the device, the device address is used as a name.
+  mojo_base.mojom.String16 public_name;
+
+  // Device type, derived from the ClassOfDevice attribute for the device.
+  DeviceType device_type;
+
+  ...
+};
+
+// Properties belonging to a Bluetooth device which has been paired to this
+// Chrome OS device.
+struct PairedBluetoothDeviceProperties {
+  BluetoothDeviceProperties device_properties;
+
+  // Nickname for this device as provided by the user. Local to the device
+  // (i.e., other devices do not have access to this name). Null if the
+  // device has not been nicknamed by the user.
+  string? nickname;
+
+  ...
+};
+```
+
+### Pairing
+
+To pair with a device, clients should first start a discovery session to scan
+for Bluetooth devices:
+```
+// Starts a discovery session, during which time it will be possible
+// to find new devices and pair them.
+StartDiscovery(pending_remote<BluetoothDiscoveryDelegate> delegate);
+```
+Once discovery has started, clients should use the
+[`DevicePairingHandler`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom;l=260;drc=321047b607bc69f5d6dce6e47319d0c198d0616e)
+to initiate pairing with a device:
+```
+interface BluetoothDiscoveryDelegate {
+  // Invoked when discovery has started.
+  // |handler| can be used to initiate pairing to a discovered device.
+  OnBluetoothDiscoveryStarted(
+      pending_remote<DevicePairingHandler> handler);
+};
+
+// Handles requests to pair to a device.
+interface DevicePairingHandler {
+  // Attempts to pair to the device with ID |device_id|. Pairing often
+  // requires additional interaction from the user, so callers must
+  // provide a |delegate| which handles requests for these interactions.
+  // For example, pairing a Bluetooth keyboard usually requires that
+  // users type in a PIN.
+  //
+  // |result| is returned when the pairing attempt completes. It is
+  // possible that |result| is returned before any delegate function
+  // is invoked.
+  PairDevice(
+      string device_id,
+      pending_remote<DevicePairingDelegate> delegate) =>
+          (PairingResult result);
+};
+```
+Pairing may require additional authentication. Clients who call pair device
+should provide a `DevicePairingDelegate` which implements the handling of
+different authentication scenarios that could possibly be required:
+```
+// Provided by the pairing UI to handle pairing requests of
+// different types.
+interface DevicePairingDelegate {
+  // Requests that a PIN be provided to complete pairing.
+  RequestPinCode() => (string pin_code);
+
+  // Requests that a passkey be provided to complete pairing.
+  RequestPasskey() => (string passkey);
+
+  // Requests that |pin_code| be displayed to the user, who should
+  // enter the PIN via a Bluetooth keyboard.
+  DisplayPinCode(string pin_code,
+                 pending_receiver<KeyEnteredHandler> handler);
+
+  // Requests that |passkey| be displayed to the user, who should
+  // enter the passkey via a Bluetooth keyboard.
+  DisplayPasskey(string passkey,
+                 pending_receiver<KeyEnteredHandler> handler);
+
+  // Requests that |passkey| be displayed to the user, who should
+  // confirm or reject a pairing request. Returns whether or not the
+  // user confirmed the passkey.
+  ConfirmPasskey(string passkey) => (bool confirmed);
+
+  // Requests that the user is asked to confirm or reject a pairing
+  // request. Returns whether or not the user confirmed the pairing.
+  AuthorizePairing() => (bool confirmed);
+};
+```
+
+### Connecting/Disconnecting/Forgetting
+
+`CrosBluetoothConfig` exposes APIs for operations on paired Bluetooth devices,
+such as connecting or forgetting, with the following APIs:
+
+```
+// Initiates a connection to the device with ID |device_id|.
+Connect(string device_id) => (bool success);
+
+// Initiates a disconnection from the device with ID |device_id|.
+Disconnect(string device_id) => (bool success);
+
+// Forgets the device with ID |device_id|, which in practice means
+// un-pairing from the device.
+Forget(string device_id) => (bool success);
+```
+
+### Testing
+
+Clients that use `CrosBluetoothConfig` can utilize a fake implementation of the
+API by initializing `CrosBluetoothConfig` with
+[`ScopedBluetoothConfigTestHelper`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/services/bluetooth_config/scoped_bluetooth_config_test_helper.h;drc=e8286e2f4c1e24abdc6a0633073b4973f240a450)
+rather than
+[`InitializerImpl`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/services/bluetooth_config/initializer_impl.h;drc=e4714ce987b39d3207473e0cd5cc77fbbbf37fda).
+
+## Implementation
+
+`CrosBluetoothConfig` is composed of many internal classes which implement
+specific functionality of the API. Some notable ones are:
+
+* [`AdapterStateController`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/services/bluetooth_config/adapter_state_controller.h;drc=e4714ce987b39d3207473e0cd5cc77fbbbf37fda):
+Handles retrieving and modifying the Bluetooth adapter powered state
+* [`DeviceCache`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/services/bluetooth_config/device_cache.h;drc=d8468bb60e224d8797b843ee9d0258862bcbe87f):
+Maintains the list of paired and unpaired Bluetooth devices
+* [`DevicePairingHandler`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/services/bluetooth_config/device_pairing_handler.h;drc=d8468bb60e224d8797b843ee9d0258862bcbe87f):
+Executes pairing attempts and manages pairing authentication if required
+* [`DeviceOperationHandler`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/services/bluetooth_config/device_operation_handler.h;drc=d8468bb60e224d8797b843ee9d0258862bcbe87f):
+Executes operations on paired devices
+* [`DiscoverySessionManager`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/services/bluetooth_config/discovery_session_manager.h;drc=d8468bb60e224d8797b843ee9d0258862bcbe87f):
+Starts and stops Bluetooth device discovery sessions
+
+Each of these classes provides a fake implementation that can be used standalone
+or with `CrosBluetoothConfig`. New functionality should either
+be implemented in one of the internal classes, or if an entirely new class must
+be created, a fake implementation should also be created for it so it can be
+used for unit testing.
\ No newline at end of file
diff --git a/chromeos/ash/services/cellular_setup/euicc.cc b/chromeos/ash/services/cellular_setup/euicc.cc
index fdd6338..0b99c57 100644
--- a/chromeos/ash/services/cellular_setup/euicc.cc
+++ b/chromeos/ash/services/cellular_setup/euicc.cc
@@ -212,16 +212,15 @@
   // Format EID to string that should be encoded in the QRCode.
   std::string qr_code_string =
       base::StrCat({kEidQrCodePrefix, properties_->eid});
-  qr_code_generator::QRCodeGenerator qr_generator;
-  std::optional<qr_code_generator::QRCodeGenerator::GeneratedCode> qr_data =
-      qr_generator.Generate(base::as_byte_span(qr_code_string));
+  std::optional<qr_code_generator::GeneratedCode> qr_data =
+      qr_code_generator::Generate(base::as_byte_span(qr_code_string));
   if (!qr_data || qr_data->data.data() == nullptr ||
       qr_data->data.size() == 0) {
     std::move(callback).Run(nullptr);
     return;
   }
 
-  // Data returned from QRCodeGenerator consist of bytes that represents
+  // Data returned from QR code generator consist of bytes that represents
   // tiles. Least significant bit of each byte is set if the tile should be
   // filled. Other bit positions indicate QR Code structure and are not required
   // for rendering. Convert this data to 0 or 1 values for simpler UI side
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 844473b..5210619 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -3108,11 +3108,23 @@
           Delete
         </message>
         <message name="IDS_SEA_PEN_CREATE_MORE" desc="Menu item that, when clicked, will allow users to create more wallpapers like the selected wallpaper.">
-          Create more in this style
+          Create more
         </message>
         <message name="IDS_SEA_PEN_ABOUT" desc="Menu item that, when clicked, will show users more about the selected wallpaper.">
           About
         </message>
+        <message name="IDS_SEA_PEN_ABOUT_DIALOG_TITLE" desc="Title for dialog that shows more information about the selected wallpaper.">
+          About wallpaper
+        </message>
+        <message name="IDS_SEA_PEN_ABOUT_DIALOG_PROMPT" desc="About dialog's description of the prompt that created the selected wallpaper.">
+          This wallpaper was created with AI using the following text: "<ph name="PROMPT">$1<ex>A radiant pink garden rose in bloom</ex></ph>."
+        </message>
+        <message name="IDS_SEA_PEN_ABOUT_DIALOG_DATE" desc="About dialog's description of the date that the selected wallpaper was created.">
+          Created on <ph name="DATE">$1<ex>Aug 25, 2023</ex></ph>
+        </message>
+        <message name="IDS_SEA_PEN_ABOUT_DIALOG_CLOSE" desc="Button that closes the about dialog.">
+          Close
+        </message>
         <message name="IDS_SEA_PEN_EXPERIMENT_LABEL" desc="Label for experiment item indicating the feature is experimental.">
           Experiment
         </message>
diff --git a/chromeos/chromeos_strings_grd/IDS_SEA_PEN_ABOUT_DIALOG_CLOSE.png.sha1 b/chromeos/chromeos_strings_grd/IDS_SEA_PEN_ABOUT_DIALOG_CLOSE.png.sha1
new file mode 100644
index 0000000..2c6490d
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_SEA_PEN_ABOUT_DIALOG_CLOSE.png.sha1
@@ -0,0 +1 @@
+7fc74324ec8a1e4c07aec4f3c19bcb38ffe16551
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_SEA_PEN_ABOUT_DIALOG_DATE.png.sha1 b/chromeos/chromeos_strings_grd/IDS_SEA_PEN_ABOUT_DIALOG_DATE.png.sha1
new file mode 100644
index 0000000..a9f496b
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_SEA_PEN_ABOUT_DIALOG_DATE.png.sha1
@@ -0,0 +1 @@
+1fdcfb32ee33398e656ee1115c6a26c57bcef18f
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_SEA_PEN_ABOUT_DIALOG_PROMPT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_SEA_PEN_ABOUT_DIALOG_PROMPT.png.sha1
new file mode 100644
index 0000000..caf3a0e7
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_SEA_PEN_ABOUT_DIALOG_PROMPT.png.sha1
@@ -0,0 +1 @@
+b202b1c1e6bd8b208be4df6122752d42436b4f60
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_SEA_PEN_ABOUT_DIALOG_TITLE.png.sha1 b/chromeos/chromeos_strings_grd/IDS_SEA_PEN_ABOUT_DIALOG_TITLE.png.sha1
new file mode 100644
index 0000000..b6bc183
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_SEA_PEN_ABOUT_DIALOG_TITLE.png.sha1
@@ -0,0 +1 @@
+ed13a0d5d52e09694fa887516f90d90657dfa9f4
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_SEA_PEN_CREATE_MORE.png.sha1 b/chromeos/chromeos_strings_grd/IDS_SEA_PEN_CREATE_MORE.png.sha1
index bc1b2c9b..bfcbefd6 100644
--- a/chromeos/chromeos_strings_grd/IDS_SEA_PEN_CREATE_MORE.png.sha1
+++ b/chromeos/chromeos_strings_grd/IDS_SEA_PEN_CREATE_MORE.png.sha1
@@ -1 +1 @@
-80a96c25ca8ab0d776cc97c314795c153eee90cf
\ No newline at end of file
+b71767bf428e219aa662859a9fac43566e724480
\ No newline at end of file
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index e831af5..84bcb1c 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -138,7 +138,7 @@
 // Enables Essential Search in Omnibox for both launcher and browser.
 BASE_FEATURE(kEssentialSearch,
              "EssentialSearch",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Enable experimental goldfish web app isolation.
 BASE_FEATURE(kExperimentalWebAppStoragePartitionIsolation,
diff --git a/clank b/clank
index 87be368..cbd13ba 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 87be368d5db43790e9d4b22620fe04228e70df17
+Subproject commit cbd13ba3774974cb20cd7dbaf2acc4860fe6d0f4
diff --git a/components/autofill/content/browser/BUILD.gn b/components/autofill/content/browser/BUILD.gn
index f604d813..7981254 100644
--- a/components/autofill/content/browser/BUILD.gn
+++ b/components/autofill/content/browser/BUILD.gn
@@ -4,6 +4,10 @@
 
 import("//third_party/protobuf/proto_library.gni")
 
+proto_library("autofill_shared_storage_proto") {
+  sources = [ "autofill_shared_storage.proto" ]
+}
+
 static_library("browser") {
   sources = [
     "autofill_log_router_factory.cc",
@@ -16,6 +20,8 @@
     "content_autofill_driver.h",
     "content_autofill_driver_factory.cc",
     "content_autofill_driver_factory.h",
+    "content_autofill_shared_storage_handler.cc",
+    "content_autofill_shared_storage_handler.h",
     "renderer_forms_with_server_predictions.cc",
     "renderer_forms_with_server_predictions.h",
     "risk/fingerprint.cc",
@@ -31,13 +37,16 @@
     "//components/autofill/core/browser",
     "//components/autofill/core/common",
     "//components/keyed_service/content",
+    "//components/services/storage",
     "//content/public/common",
     "//mojo/public/cpp/bindings",
     "//skia",
     "//third_party/abseil-cpp:absl",
   ]
   deps = [
+    ":autofill_shared_storage_proto",
     "//base:i18n",
+    "//components/autofill/content/common:features",
     "//components/os_crypt/sync",
     "//components/prefs",
     "//components/profile_metrics",
diff --git a/components/autofill/content/browser/DEPS b/components/autofill/content/browser/DEPS
index 513381b..5ea3dd74 100644
--- a/components/autofill/content/browser/DEPS
+++ b/components/autofill/content/browser/DEPS
@@ -3,6 +3,7 @@
   "+components/keyed_service/content",
   "+components/version_info",
   "+components/profile_metrics",
+  "+components/services/storage",
   "+components/webauthn",
   "+crypto/random.h",
   "+gpu/config/gpu_info.h",
diff --git a/components/autofill/content/browser/autofill_shared_storage.proto b/components/autofill/content/browser/autofill_shared_storage.proto
new file mode 100644
index 0000000..1d66cf2
--- /dev/null
+++ b/components/autofill/content/browser/autofill_shared_storage.proto
@@ -0,0 +1,20 @@
+// 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package autofill;
+
+message AutofillCreditCardData {
+  optional string last_four = 1;
+  optional string network = 2;
+  optional uint32 expiration_month = 3;
+  optional uint32 expiration_year = 4;
+}
+
+message AutofillCreditCardList {
+  repeated AutofillCreditCardData server_cards = 1;
+}
diff --git a/components/autofill/content/browser/content_autofill_shared_storage_handler.cc b/components/autofill/content/browser/content_autofill_shared_storage_handler.cc
new file mode 100644
index 0000000..ae50b11
--- /dev/null
+++ b/components/autofill/content/browser/content_autofill_shared_storage_handler.cc
@@ -0,0 +1,67 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/content/browser/content_autofill_shared_storage_handler.h"
+
+#include "base/functional/bind.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/content/browser/autofill_shared_storage.pb.h"
+#include "components/autofill/content/common/content_autofill_features.h"
+#include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/autofill/core/browser/payments/payments_service_url.h"
+
+namespace autofill {
+namespace {
+
+constexpr char16_t kAutofillServerCardDataSharedStorageKey[] =
+    u"browser_autofill_card_data";
+
+std::u16string EncodeServerCardDataForSharedStorage(
+    const std::vector<std::unique_ptr<CreditCard>>& server_card_data) {
+  AutofillCreditCardList card_list_proto;
+  for (auto& card : server_card_data) {
+    AutofillCreditCardData* card_data_proto =
+        card_list_proto.add_server_cards();
+    card_data_proto->set_last_four(base::UTF16ToUTF8(card->LastFourDigits()));
+    card_data_proto->set_network(card->network());
+    card_data_proto->set_expiration_month(card->expiration_month());
+    card_data_proto->set_expiration_year(card->expiration_year());
+  }
+
+  return base::UTF8ToUTF16(card_list_proto.SerializeAsString());
+}
+
+}  // namespace
+
+ContentAutofillSharedStorageHandler::ContentAutofillSharedStorageHandler(
+    storage::SharedStorageManager& shared_storage_manager)
+    : shared_storage_manager_(shared_storage_manager) {}
+
+ContentAutofillSharedStorageHandler::~ContentAutofillSharedStorageHandler() =
+    default;
+
+void ContentAutofillSharedStorageHandler::OnServerCardDataRefreshed(
+    const std::vector<std::unique_ptr<CreditCard>>& server_card_data) {
+  if (!base::FeatureList::IsEnabled(
+          features::kAutofillSharedStorageServerCardData)) {
+    return;
+  }
+
+  shared_storage_manager_->Set(
+      url::Origin::Create(payments::GetBaseSecureUrl()),
+      kAutofillServerCardDataSharedStorageKey,
+      EncodeServerCardDataForSharedStorage(server_card_data),
+      base::BindOnce(&ContentAutofillSharedStorageHandler::
+                         OnSharedStorageSetAutofillDataComplete,
+                     weak_factory_.GetWeakPtr()),
+      storage::SharedStorageDatabase::SetBehavior::kDefault);
+}
+
+void ContentAutofillSharedStorageHandler::
+    OnSharedStorageSetAutofillDataComplete(
+        storage::SharedStorageManager::OperationResult result) {
+  // TODO(crbug/1519929): Record metrics.
+}
+
+}  // namespace autofill
diff --git a/components/autofill/content/browser/content_autofill_shared_storage_handler.h b/components/autofill/content/browser/content_autofill_shared_storage_handler.h
new file mode 100644
index 0000000..53ff91c
--- /dev/null
+++ b/components/autofill/content/browser/content_autofill_shared_storage_handler.h
@@ -0,0 +1,47 @@
+// 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_AUTOFILL_CONTENT_BROWSER_CONTENT_AUTOFILL_SHARED_STORAGE_HANDLER_H_
+#define COMPONENTS_AUTOFILL_CONTENT_BROWSER_CONTENT_AUTOFILL_SHARED_STORAGE_HANDLER_H_
+
+#include <memory>
+#include <vector>
+
+#include "components/autofill/core/browser/autofill_shared_storage_handler.h"
+#include "components/services/storage/shared_storage/shared_storage_manager.h"
+
+namespace autofill {
+
+class CreditCard;
+
+// Implements the AutofillSharedStorageHandler for the content layer.
+class ContentAutofillSharedStorageHandler
+    : public AutofillSharedStorageHandler {
+ public:
+  explicit ContentAutofillSharedStorageHandler(
+      storage::SharedStorageManager& shared_storage_manager);
+  ContentAutofillSharedStorageHandler(
+      const ContentAutofillSharedStorageHandler&) = delete;
+  ContentAutofillSharedStorageHandler& operator=(
+      const ContentAutofillSharedStorageHandler&) = delete;
+  ~ContentAutofillSharedStorageHandler() override;
+
+  void OnServerCardDataRefreshed(const std::vector<std::unique_ptr<CreditCard>>&
+                                     server_card_data) override;
+
+ private:
+  // Callback for shared storage results.
+  void OnSharedStorageSetAutofillDataComplete(
+      storage::SharedStorageManager::OperationResult result);
+
+  // The shared storage manager that this instance uses. Must outlive this
+  // instance.
+  raw_ref<storage::SharedStorageManager> shared_storage_manager_;
+
+  base::WeakPtrFactory<ContentAutofillSharedStorageHandler> weak_factory_{this};
+};
+
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CONTENT_BROWSER_CONTENT_AUTOFILL_SHARED_STORAGE_HANDLER_H_
diff --git a/components/autofill/content/common/BUILD.gn b/components/autofill/content/common/BUILD.gn
new file mode 100644
index 0000000..150f442
--- /dev/null
+++ b/components/autofill/content/common/BUILD.gn
@@ -0,0 +1,14 @@
+# Copyright 2024 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+component("features") {
+  output_name = "autofill_content_common_features"
+  defines = [ "IS_AUTOFILL_IMPL" ]
+  sources = [
+    "content_autofill_features.cc",
+    "content_autofill_features.h",
+  ]
+
+  deps = [ "//base" ]
+}
diff --git a/components/autofill/content/common/content_autofill_features.cc b/components/autofill/content/common/content_autofill_features.cc
new file mode 100644
index 0000000..36e92987
--- /dev/null
+++ b/components/autofill/content/common/content_autofill_features.cc
@@ -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.
+
+#include "components/autofill/content/common/content_autofill_features.h"
+
+namespace autofill::features {
+
+// If enabled, we will store autofill server card data in shared storage.
+BASE_FEATURE(kAutofillSharedStorageServerCardData,
+             "AutofillSharedStorageServerCardData",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+}  // namespace autofill::features
diff --git a/components/autofill/content/common/content_autofill_features.h b/components/autofill/content/common/content_autofill_features.h
new file mode 100644
index 0000000..4069444
--- /dev/null
+++ b/components/autofill/content/common/content_autofill_features.h
@@ -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.
+
+#ifndef COMPONENTS_AUTOFILL_CONTENT_COMMON_CONTENT_AUTOFILL_FEATURES_H_
+#define COMPONENTS_AUTOFILL_CONTENT_COMMON_CONTENT_AUTOFILL_FEATURES_H_
+
+#include "base/component_export.h"
+#include "base/feature_list.h"
+
+namespace autofill::features {
+
+COMPONENT_EXPORT(AUTOFILL)
+BASE_DECLARE_FEATURE(kAutofillSharedStorageServerCardData);
+
+}  // namespace autofill::features
+
+#endif  // COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_FEATURES_H_
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index dba48f0..73a4d9d 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -107,6 +107,7 @@
     "autofill_plus_address_delegate.h",
     "autofill_profile_import_process.cc",
     "autofill_profile_import_process.h",
+    "autofill_shared_storage_handler.h",
     "autofill_suggestion_generator.cc",
     "autofill_suggestion_generator.h",
     "autofill_trigger_details.h",
diff --git a/components/autofill/core/browser/autofill_optimization_guide_unittest.cc b/components/autofill/core/browser/autofill_optimization_guide_unittest.cc
index 6c1dd6d..38716de 100644
--- a/components/autofill/core/browser/autofill_optimization_guide_unittest.cc
+++ b/components/autofill/core/browser/autofill_optimization_guide_unittest.cc
@@ -89,7 +89,7 @@
         /*history_service=*/nullptr,
         /*sync_service=*/&sync_service_,
         /*strike_database=*/nullptr,
-        /*image_fetcher=*/nullptr);
+        /*image_fetcher=*/nullptr, /*shared_storage_handler=*/nullptr);
     personal_data_manager_->AddServerCreditCard(card);
   }
 
diff --git a/components/autofill/core/browser/autofill_shared_storage_handler.h b/components/autofill/core/browser/autofill_shared_storage_handler.h
new file mode 100644
index 0000000..ffd233c
--- /dev/null
+++ b/components/autofill/core/browser/autofill_shared_storage_handler.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_AUTOFILL_CORE_BROWSER_AUTOFILL_SHARED_STORAGE_HANDLER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_SHARED_STORAGE_HANDLER_H_
+
+#include <memory>
+#include <vector>
+
+namespace autofill {
+
+class CreditCard;
+
+// Abstract class that handles interacting with the SharedStorageManager on
+// supported platforms.
+//
+// On Desktop and Android, ContentAutofillSharedStorageHandler (in
+// components/autofill/content/) extends this class, and handles interactions
+// with the SharedStorageManager.
+//
+// On iOS, SharedStorage is not supported, so the behavior is not implemented.
+class AutofillSharedStorageHandler {
+ public:
+  virtual ~AutofillSharedStorageHandler() = default;
+
+  virtual void OnServerCardDataRefreshed(
+      const std::vector<std::unique_ptr<CreditCard>>& server_card_data) = 0;
+};
+
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_SHARED_STORAGE_HANDLER_H_
diff --git a/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc b/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc
index a652101..8dd136a4 100644
--- a/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc
+++ b/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc
@@ -200,7 +200,8 @@
                          /*history_service=*/nullptr,
                          /*sync_service=*/&sync_service_,
                          /*strike_database=*/nullptr,
-                         /*image_fetcher=*/nullptr);
+                         /*image_fetcher=*/nullptr,
+                         /*shared_storage_handler=*/nullptr);
     suggestion_generator_ = std::make_unique<TestAutofillSuggestionGenerator>(
         autofill_client_, personal_data());
     autofill_client_.set_autofill_offer_manager(
diff --git a/components/autofill/core/browser/form_data_importer.cc b/components/autofill/core/browser/form_data_importer.cc
index 178fca5c..ad75321c 100644
--- a/components/autofill/core/browser/form_data_importer.cc
+++ b/components/autofill/core/browser/form_data_importer.cc
@@ -733,8 +733,8 @@
     return false;
   }
 
-  // If a flow where there was no interactive authentication was completed, we
-  // might need to initiate the re-auth opt-in flow.
+  // If a flow where there was no interactive authentication was completed,
+  // re-auth opt-in flow might be offered.
   if (auto* mandatory_reauth_manager =
           client_->GetOrCreatePaymentsMandatoryReauthManager();
       mandatory_reauth_manager &&
@@ -786,6 +786,19 @@
 }
 
 bool FormDataImporter::ProcessIbanImportCandidate(Iban& extracted_iban) {
+  // If a flow where there was no interactive authentication was completed,
+  // re-auth opt-in flow might be offered.
+  if (auto* mandatory_reauth_manager =
+          client_->GetOrCreatePaymentsMandatoryReauthManager();
+      mandatory_reauth_manager &&
+      mandatory_reauth_manager->ShouldOfferOptin(
+          payment_method_type_if_non_interactive_authentication_flow_completed_)) {
+    payment_method_type_if_non_interactive_authentication_flow_completed_
+        .reset();
+    mandatory_reauth_manager->StartOptInFlow();
+    return true;
+  }
+
   return iban_save_manager_->AttemptToOfferSave(extracted_iban);
 }
 
diff --git a/components/autofill/core/browser/form_data_importer_unittest.cc b/components/autofill/core/browser/form_data_importer_unittest.cc
index 8fcbe6a..c005bc30 100644
--- a/components/autofill/core/browser/form_data_importer_unittest.cc
+++ b/components/autofill/core/browser/form_data_importer_unittest.cc
@@ -554,7 +554,7 @@
         /*history_service=*/nullptr,
         /*sync_service=*/&sync_service_,
         /*strike_database=*/nullptr,
-        /*image_fetcher=*/nullptr);
+        /*image_fetcher=*/nullptr, /*shared_storage_handler=*/nullptr);
 
     // Init the `form_data_importer()` with `personal_data_manager_`.
     autofill_client_->set_test_form_data_importer(
@@ -4126,7 +4126,7 @@
 }
 
 // Test that in the case where the MandatoryReauthManager denotes we should
-// offer re-auth opt-in, we start the opt-in flow.
+// offer re-auth opt-in, we start the opt-in in credit card processing flow.
 TEST_F(FormDataImporterTest,
        ProcessExtractedCreditCard_MandatoryReauthOffered) {
   CreditCard extracted_credit_card = test::GetCreditCard2();
@@ -4163,6 +4163,62 @@
           .GetPaymentMethodTypeIfNonInteractiveAuthenticationFlowCompleted()
           .has_value());
 }
+
+// This test is disabled for Android because the implementation for IBAN on
+// Clank, will remove the flag once the IBAN on Clank is ready.
+#if !BUILDFLAG(IS_ANDROID)
+// Test that in the case where the MandatoryReauthManager denotes we should
+// offer re-auth opt-in, we start the opt-in in IBAN processing flow.
+TEST_F(FormDataImporterTest, ProcessExtractedIban_MandatoryReauthOffered) {
+  FormStructure form_structure(CreateTestIbanFormData());
+  form_structure.DetermineHeuristicTypes(GeoIpCountryCode(""), nullptr,
+                                         nullptr);
+  form_data_importer()
+      .SetPaymentMethodTypeIfNonInteractiveAuthenticationFlowCompleted(
+          NonInteractivePaymentMethodType::kLocalIban);
+
+  EXPECT_CALL(*autofill_client_->GetOrCreatePaymentsMandatoryReauthManager(),
+              ShouldOfferOptin)
+      .WillOnce(testing::Return(true));
+  EXPECT_CALL(*autofill_client_->GetOrCreatePaymentsMandatoryReauthManager(),
+              StartOptInFlow);
+
+  EXPECT_TRUE(ExtractFormDataAndProcessIbanCandidates(
+      form_structure, /*profile_autofill_enabled=*/true,
+      /*payment_methods_autofill_enabled=*/true));
+
+  // Ensure that we reset the record type at the end of the flow.
+  EXPECT_FALSE(
+      form_data_importer()
+          .GetPaymentMethodTypeIfNonInteractiveAuthenticationFlowCompleted()
+          .has_value());
+}
+
+// Test that in the case where the MandatoryReauthManager denotes we should not
+// offer re-auth opt-in, we do not start the opt-in in IBAN processing flow.
+TEST_F(FormDataImporterTest, ProcessExtractedIban_MandatoryReauthNotOffered) {
+  FormStructure form_structure(CreateTestIbanFormData());
+  form_structure.DetermineHeuristicTypes(GeoIpCountryCode(""), nullptr,
+                                         nullptr);
+
+  EXPECT_CALL(*autofill_client_->GetOrCreatePaymentsMandatoryReauthManager(),
+              ShouldOfferOptin)
+      .WillOnce(testing::Return(false));
+  EXPECT_CALL(*autofill_client_->GetOrCreatePaymentsMandatoryReauthManager(),
+              StartOptInFlow)
+      .Times(0);
+
+  EXPECT_TRUE(ExtractFormDataAndProcessIbanCandidates(
+      form_structure, /*profile_autofill_enabled=*/true,
+      /*payment_methods_autofill_enabled=*/true));
+
+  // Ensure that we reset the record type at the end of the flow.
+  EXPECT_FALSE(
+      form_data_importer()
+          .GetPaymentMethodTypeIfNonInteractiveAuthenticationFlowCompleted()
+          .has_value());
+}
+#endif  // !BUILDFLAG(IS_ANDROID)
 #endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
 
 // Test that ProceedWithSavingIfApplicable gets called for server cards with the
diff --git a/components/autofill/core/browser/geo/alternative_state_name_map_updater_unittest.cc b/components/autofill/core/browser/geo/alternative_state_name_map_updater_unittest.cc
index 942ddeb1..ccf8743c 100644
--- a/components/autofill/core/browser/geo/alternative_state_name_map_updater_unittest.cc
+++ b/components/autofill/core/browser/geo/alternative_state_name_map_updater_unittest.cc
@@ -53,7 +53,8 @@
                                 /*history_service=*/nullptr,
                                 /*sync_service=*/nullptr,
                                 /*strike_database=*/nullptr,
-                                /*image_fetcher=*/nullptr);
+                                /*image_fetcher=*/nullptr,
+                                /*shared_storage_handler=*/nullptr);
     alternative_state_name_map_updater_ =
         std::make_unique<AlternativeStateNameMapUpdater>(
             autofill_client_.GetPrefs(), &personal_data_manager_);
diff --git a/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc b/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
index 83ba88c..60847d6 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
@@ -1642,7 +1642,8 @@
                        /*history_service=*/nullptr,
                        /*sync_service=*/nullptr,
                        /*strike_database=*/nullptr,
-                       /*image_fetcher=*/nullptr);
+                       /*image_fetcher=*/nullptr,
+                       /*shared_storage_handler=*/nullptr);
   histogram_tester.ExpectUniqueSample("Autofill.Address.IsEnabled.Startup",
                                       true, 1);
 }
@@ -1660,7 +1661,8 @@
                        /*history_service=*/nullptr,
                        /*sync_service=*/nullptr,
                        /*strike_database=*/nullptr,
-                       /*image_fetcher=*/nullptr);
+                       /*image_fetcher=*/nullptr,
+                       /*shared_storage_handler=*/nullptr);
   histogram_tester.ExpectUniqueSample("Autofill.Address.IsEnabled.Startup",
                                       false, 1);
 }
@@ -1678,7 +1680,8 @@
                        /*history_service=*/nullptr,
                        /*sync_service=*/nullptr,
                        /*strike_database=*/nullptr,
-                       /*image_fetcher=*/nullptr);
+                       /*image_fetcher=*/nullptr,
+                       /*shared_storage_handler=*/nullptr);
   histogram_tester.ExpectUniqueSample("Autofill.CreditCard.IsEnabled.Startup",
                                       true, 1);
 }
@@ -1696,7 +1699,8 @@
                        /*history_service=*/nullptr,
                        /*sync_service=*/nullptr,
                        /*strike_database=*/nullptr,
-                       /*image_fetcher=*/nullptr);
+                       /*image_fetcher=*/nullptr,
+                       /*shared_storage_handler=*/nullptr);
   histogram_tester.ExpectUniqueSample("Autofill.CreditCard.IsEnabled.Startup",
                                       false, 1);
 }
diff --git a/components/autofill/core/browser/metrics/payments/cvc_storage_metrics_unittest.cc b/components/autofill/core/browser/metrics/payments/cvc_storage_metrics_unittest.cc
index 5149ace4..057739a 100644
--- a/components/autofill/core/browser/metrics/payments/cvc_storage_metrics_unittest.cc
+++ b/components/autofill/core/browser/metrics/payments/cvc_storage_metrics_unittest.cc
@@ -58,7 +58,8 @@
                        /*history_service=*/nullptr,
                        /*sync_service=*/nullptr,
                        /*strike_database=*/nullptr,
-                       /*image_fetcher=*/nullptr);
+                       /*image_fetcher=*/nullptr,
+                       /*shared_storage_handler=*/nullptr);
 
   histogram_tester.ExpectUniqueSample(
       "Autofill.PaymentMethods.CvcStorageIsEnabled.Startup", true, 1);
@@ -79,7 +80,8 @@
                        /*history_service=*/nullptr,
                        /*sync_service=*/nullptr,
                        /*strike_database=*/nullptr,
-                       /*image_fetcher=*/nullptr);
+                       /*image_fetcher=*/nullptr,
+                       /*shared_storage_handler=*/nullptr);
 
   histogram_tester.ExpectUniqueSample(
       "Autofill.PaymentMethods.CvcStorageIsEnabled.Startup", false, 1);
diff --git a/components/autofill/core/browser/metrics/payments/mandatory_reauth_metrics.cc b/components/autofill/core/browser/metrics/payments/mandatory_reauth_metrics.cc
index d529448a..886ca0dd 100644
--- a/components/autofill/core/browser/metrics/payments/mandatory_reauth_metrics.cc
+++ b/components/autofill/core/browser/metrics/payments/mandatory_reauth_metrics.cc
@@ -25,6 +25,10 @@
       return "CheckoutFullServerCard";
     case MandatoryReauthOptInOrOutSource::kCheckoutMaskedServerCard:
       return "CheckoutMaskedServerCard";
+    case MandatoryReauthOptInOrOutSource::kCheckoutLocalIban:
+      return "CheckoutLocalIban";
+    case MandatoryReauthOptInOrOutSource::kCheckoutServerIban:
+      return "CheckoutServerIban";
     case MandatoryReauthOptInOrOutSource::kUnknown:
       return "Unknown";
   }
diff --git a/components/autofill/core/browser/metrics/payments/mandatory_reauth_metrics.h b/components/autofill/core/browser/metrics/payments/mandatory_reauth_metrics.h
index 664abcab..6da3561 100644
--- a/components/autofill/core/browser/metrics/payments/mandatory_reauth_metrics.h
+++ b/components/autofill/core/browser/metrics/payments/mandatory_reauth_metrics.h
@@ -117,7 +117,11 @@
   // The OptIn is triggered after using a green-pathed masked server card during
   // checkout.
   kCheckoutMaskedServerCard = 5,
-  kMaxValue = kCheckoutMaskedServerCard,
+  // The OptIn is triggered after using a local IBAN during checkout.
+  kCheckoutLocalIban = 6,
+  // The OptIn is triggered after using a server IBAN during checkout.
+  kCheckoutServerIban = 7,
+  kMaxValue = kCheckoutServerIban,
 };
 
 void LogMandatoryReauthOfferOptInDecision(
diff --git a/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc b/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc
index 5b3d13f3..f6538432e 100644
--- a/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc
@@ -67,7 +67,8 @@
                                 /*history_service=*/nullptr,
                                 /*sync_service=*/&sync_service_,
                                 /*strike_database=*/nullptr,
-                                /*image_fetcher=*/nullptr);
+                                /*image_fetcher=*/nullptr,
+                                /*shared_storage_handler=*/nullptr);
     personal_data_manager_.SetPrefService(autofill_client_.GetPrefs());
     auto mock_shopping_service_delegate =
         std::make_unique<MockShoppingServiceDelegate>();
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
index 58a90eb..8e006711 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
@@ -167,7 +167,8 @@
                          /*history_service=*/nullptr,
                          /*sync_service=*/&sync_service_,
                          /*strike_database=*/nullptr,
-                         /*image_fetcher=*/nullptr);
+                         /*image_fetcher=*/nullptr,
+                         /*shared_storage_handler=*/nullptr);
     personal_data().SetPrefService(autofill_client_.GetPrefs());
 
     accessor_ = std::make_unique<TestAccessor>();
diff --git a/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc b/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc
index 7af8bfd..63b3d27 100644
--- a/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc
@@ -85,7 +85,8 @@
                                 /*history_service=*/nullptr,
                                 /*sync_service=*/nullptr,
                                 /*strike_database=*/nullptr,
-                                /*image_fetcher=*/nullptr);
+                                /*image_fetcher=*/nullptr,
+                                /*shared_storage_handler=*/nullptr);
     personal_data_manager_.SetPrefService(autofill_client_.GetPrefs());
 
     requester_ = std::make_unique<TestAuthenticationRequester>();
diff --git a/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc b/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc
index bcbf5809..16fec6d 100644
--- a/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc
@@ -105,7 +105,8 @@
                                  /*history_service=*/nullptr,
                                  /*sync_service=*/nullptr,
                                  /*strike_database=*/nullptr,
-                                 /*image_fetcher=*/nullptr);
+                                 /*image_fetcher=*/nullptr,
+                                 /*shared_storage_handler=*/nullptr);
     personal_data_manager().SetPrefService(autofill_client_.GetPrefs());
 
     autofill_driver_.SetAuthenticator(new TestInternalAuthenticator());
diff --git a/components/autofill/core/browser/payments/credit_card_otp_authenticator_unittest.cc b/components/autofill/core/browser/payments/credit_card_otp_authenticator_unittest.cc
index e164431..ab0fb09a 100644
--- a/components/autofill/core/browser/payments/credit_card_otp_authenticator_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_otp_authenticator_unittest.cc
@@ -46,7 +46,8 @@
                                 /*history_service=*/nullptr,
                                 /*sync_service=*/nullptr,
                                 /*strike_database=*/nullptr,
-                                /*image_fetcher=*/nullptr);
+                                /*image_fetcher=*/nullptr,
+                                /*shared_storage_handler=*/nullptr);
     personal_data_manager_.SetPrefService(autofill_client_.GetPrefs());
 
     requester_ = std::make_unique<TestAuthenticationRequester>();
diff --git a/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc b/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
index f9347fc..fa353e1c 100644
--- a/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
@@ -199,7 +199,8 @@
                          /*identity_manager=*/nullptr,
                          /*history_service=*/nullptr, &sync_service_,
                          /*strike_database=*/nullptr,
-                         /*image_fetcher=*/nullptr);
+                         /*image_fetcher=*/nullptr,
+                         /*shared_storage_handler=*/nullptr);
     autofill_driver_ = std::make_unique<TestAutofillDriver>();
     autofill_client_.set_test_payments_network_interface(
         std::make_unique<payments::TestPaymentsNetworkInterface>(
diff --git a/components/autofill/core/browser/payments/iban_access_manager.cc b/components/autofill/core/browser/payments/iban_access_manager.cc
index a2d9616..d6aed3c 100644
--- a/components/autofill/core/browser/payments/iban_access_manager.cc
+++ b/components/autofill/core/browser/payments/iban_access_manager.cc
@@ -22,6 +22,15 @@
 
 void IbanAccessManager::FetchValue(const Suggestion& suggestion,
                                    OnIbanFetchedCallback on_iban_fetched) {
+  if (auto* form_data_importer = client_->GetFormDataImporter()) {
+    // Reset the variable in FormDataImporter that denotes if non-interactive
+    // authentication happened. This variable will be set to a value if a
+    // payments autofill non-interactive flow successfully completes.
+    form_data_importer
+        ->SetPaymentMethodTypeIfNonInteractiveAuthenticationFlowCompleted(
+            std::nullopt);
+  }
+
   // If `Guid` has a value then that means that it's a local IBAN suggestion.
   // In this case, retrieving the complete IBAN value requires accessing the
   // saved IBAN from the PersonalDataManager.
@@ -39,6 +48,13 @@
                                             copy_iban.value());
       } else {
         std::move(on_iban_fetched).Run(copy_iban.value());
+        if (auto* form_data_importer = client_->GetFormDataImporter()) {
+          form_data_importer
+              ->SetPaymentMethodTypeIfNonInteractiveAuthenticationFlowCompleted(
+                  payments::MandatoryReauthManager::
+                      GetNonInteractivePaymentMethodType(
+                          Iban::RecordType::kLocalIban));
+        }
       }
     }
     return;
@@ -114,6 +130,13 @@
       client_->CloseAutofillProgressDialog(
           /*show_confirmation_before_closing=*/false);
       std::move(on_iban_fetched).Run(value);
+      if (auto* form_data_importer = client_->GetFormDataImporter()) {
+        form_data_importer
+            ->SetPaymentMethodTypeIfNonInteractiveAuthenticationFlowCompleted(
+                payments::MandatoryReauthManager::
+                    GetNonInteractivePaymentMethodType(
+                        Iban::RecordType::kServerIban));
+      }
     }
     return;
   }
diff --git a/components/autofill/core/browser/payments/iban_access_manager_unittest.cc b/components/autofill/core/browser/payments/iban_access_manager_unittest.cc
index e140cea..c22feef 100644
--- a/components/autofill/core/browser/payments/iban_access_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/iban_access_manager_unittest.cc
@@ -48,7 +48,8 @@
                          /*history_service=*/nullptr,
                          /*sync_service=*/nullptr,
                          /*strike_database=*/nullptr,
-                         /*image_fetcher=*/nullptr);
+                         /*image_fetcher=*/nullptr,
+                         /*shared_storage_handler=*/nullptr);
     iban_access_manager_ =
         std::make_unique<IbanAccessManager>(&autofill_client_);
   }
@@ -449,6 +450,50 @@
   iban_access_manager_->FetchValue(suggestion, callback.Get());
 }
 
+// Tests that `NonInteractivePaymentMethodType` is set to `kLocalIban` on
+// local IBAN retrieval flow.
+TEST_F(IbanAccessManagerMandatoryReauthTest,
+       NonInteractivePaymentMethodType_Local) {
+  autofill_client_.GetPrefs()->SetBoolean(
+      prefs::kAutofillPaymentMethodsMandatoryReauth, false);
+  SetUpDeviceAuthenticatorResponseMock(/*success=*/true);
+
+  Iban local_iban = test::GetLocalIban();
+  personal_data().AddIbanForTest(std::make_unique<Iban>(local_iban));
+  Suggestion suggestion(PopupItemId::kIbanEntry);
+  suggestion.payload =
+      Suggestion::BackendId(Suggestion::Guid(local_iban.guid()));
+
+  iban_access_manager_->FetchValue(suggestion, base::DoNothing());
+
+  EXPECT_EQ(
+      autofill_client_.GetFormDataImporter()
+          ->GetPaymentMethodTypeIfNonInteractiveAuthenticationFlowCompleted(),
+      NonInteractivePaymentMethodType::kLocalIban);
+}
+
+// Tests that `NonInteractivePaymentMethodType` is set to `kServerIban` on
+// server IBAN retrieval flow.
+TEST_F(IbanAccessManagerMandatoryReauthTest,
+       NonInteractivePaymentMethodType_Server) {
+  autofill_client_.GetPrefs()->SetBoolean(
+      prefs::kAutofillPaymentMethodsMandatoryReauth, false);
+  SetUpDeviceAuthenticatorResponseMock(/*success=*/true);
+  SetUpUnmaskIbanCall(/*is_successful=*/true, /*value=*/kFullIbanValue);
+
+  Iban server_iban = test::GetServerIban();
+  personal_data().AddServerIban(server_iban);
+  Suggestion suggestion(PopupItemId::kIbanEntry);
+  suggestion.payload = Suggestion::InstrumentId(server_iban.instrument_id());
+
+  iban_access_manager_->FetchValue(suggestion, base::DoNothing());
+
+  EXPECT_EQ(
+      autofill_client_.GetFormDataImporter()
+          ->GetPaymentMethodTypeIfNonInteractiveAuthenticationFlowCompleted(),
+      NonInteractivePaymentMethodType::kServerIban);
+}
+
 #endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/payments/iban_save_manager_unittest.cc b/components/autofill/core/browser/payments/iban_save_manager_unittest.cc
index 159bf08..05fef72f 100644
--- a/components/autofill/core/browser/payments/iban_save_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/iban_save_manager_unittest.cc
@@ -75,7 +75,8 @@
                          /*history_service=*/nullptr,
                          /*sync_service=*/nullptr,
                          /*strike_database=*/nullptr,
-                         /*image_fetcher=*/nullptr);
+                         /*image_fetcher=*/nullptr,
+                         /*shared_storage_handler=*/nullptr);
     iban_save_manager_ =
         std::make_unique<IbanSaveManager>(&personal_data(), &autofill_client_);
   }
diff --git a/components/autofill/core/browser/payments/mandatory_reauth_manager.cc b/components/autofill/core/browser/payments/mandatory_reauth_manager.cc
index 44d5bc1c..90698f0 100644
--- a/components/autofill/core/browser/payments/mandatory_reauth_manager.cc
+++ b/components/autofill/core/browser/payments/mandatory_reauth_manager.cc
@@ -29,16 +29,28 @@
 // static
 NonInteractivePaymentMethodType
 MandatoryReauthManager::GetNonInteractivePaymentMethodType(
-    CreditCard::RecordType record_type) {
-  switch (record_type) {
-    case CreditCard::RecordType::kLocalCard:
-      return NonInteractivePaymentMethodType::kLocalCard;
-    case CreditCard::RecordType::kFullServerCard:
-      return NonInteractivePaymentMethodType::kFullServerCard;
-    case CreditCard::RecordType::kVirtualCard:
-      return NonInteractivePaymentMethodType::kVirtualCard;
-    case CreditCard::RecordType::kMaskedServerCard:
-      return NonInteractivePaymentMethodType::kMaskedServerCard;
+    absl::variant<CreditCard::RecordType, Iban::RecordType> record_type) {
+  if (CreditCard::RecordType* type =
+          absl::get_if<CreditCard::RecordType>(&record_type)) {
+    switch (*type) {
+      case CreditCard::RecordType::kLocalCard:
+        return NonInteractivePaymentMethodType::kLocalCard;
+      case CreditCard::RecordType::kFullServerCard:
+        return NonInteractivePaymentMethodType::kFullServerCard;
+      case CreditCard::RecordType::kVirtualCard:
+        return NonInteractivePaymentMethodType::kVirtualCard;
+      case CreditCard::RecordType::kMaskedServerCard:
+        return NonInteractivePaymentMethodType::kMaskedServerCard;
+    }
+  } else {
+    if (absl::get<Iban::RecordType>(record_type) ==
+        Iban::RecordType::kLocalIban) {
+      return NonInteractivePaymentMethodType::kLocalIban;
+    } else {
+      CHECK_NE(absl::get<Iban::RecordType>(record_type),
+               Iban::RecordType::kUnknown);
+      return NonInteractivePaymentMethodType::kServerIban;
+    }
   }
 }
 
@@ -166,6 +178,14 @@
       opt_in_source_ = autofill_metrics::MandatoryReauthOptInOrOutSource::
           kCheckoutMaskedServerCard;
       break;
+    case NonInteractivePaymentMethodType::kLocalIban:
+      opt_in_source_ =
+          autofill_metrics::MandatoryReauthOptInOrOutSource::kCheckoutLocalIban;
+      break;
+    case NonInteractivePaymentMethodType::kServerIban:
+      opt_in_source_ = autofill_metrics::MandatoryReauthOptInOrOutSource::
+          kCheckoutServerIban;
+      break;
   }
   LogMandatoryReauthOfferOptInDecision(
       MandatoryReauthOfferOptInDecision::kOffered);
diff --git a/components/autofill/core/browser/payments/mandatory_reauth_manager.h b/components/autofill/core/browser/payments/mandatory_reauth_manager.h
index 5a0a19d1..828f0869 100644
--- a/components/autofill/core/browser/payments/mandatory_reauth_manager.h
+++ b/components/autofill/core/browser/payments/mandatory_reauth_manager.h
@@ -7,6 +7,7 @@
 
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/autofill/core/browser/data_model/iban.h"
 #include "components/autofill/core/browser/form_data_importer.h"
 #include "components/autofill/core/browser/metrics/payments/mandatory_reauth_metrics.h"
 #include "components/device_reauth/device_authenticator.h"
@@ -20,7 +21,9 @@
   kFullServerCard = 1,
   kVirtualCard = 2,
   kMaskedServerCard = 3,
-  kMaxValue = kMaskedServerCard,
+  kLocalIban = 4,
+  kServerIban = 5,
+  kMaxValue = kServerIban,
 };
 
 namespace payments {
@@ -46,7 +49,7 @@
   virtual ~MandatoryReauthManager();
 
   static NonInteractivePaymentMethodType GetNonInteractivePaymentMethodType(
-      CreditCard::RecordType card_record_type);
+      absl::variant<CreditCard::RecordType, Iban::RecordType> record_type);
 
   // Helper method to get all NonInteractivePaymentMethodType for testing
   // purpose.
diff --git a/components/autofill/core/browser/payments/mandatory_reauth_manager_unittest.cc b/components/autofill/core/browser/payments/mandatory_reauth_manager_unittest.cc
index f1f8f41..edbf5af 100644
--- a/components/autofill/core/browser/payments/mandatory_reauth_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/mandatory_reauth_manager_unittest.cc
@@ -51,7 +51,8 @@
         /*history_service=*/nullptr,
         /*sync_service=*/nullptr,
         /*strike_database=*/nullptr,
-        /*image_fetcher=*/nullptr);
+        /*image_fetcher=*/nullptr,
+        /*shared_storage_manager=*/nullptr);
     test::SetCreditCardInfo(&server_card_, "Test User", "1111" /* Visa */,
                             test::NextMonth().c_str(), test::NextYear().c_str(),
                             "1");
@@ -477,6 +478,10 @@
         return "CheckoutVirtualCard";
       case NonInteractivePaymentMethodType::kMaskedServerCard:
         return "CheckoutMaskedServerCard";
+      case NonInteractivePaymentMethodType::kLocalIban:
+        return "CheckoutLocalIban";
+      case NonInteractivePaymentMethodType::kServerIban:
+        return "CheckoutServerIban";
     }
   }
 
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc b/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
index dc63a79..3d414a8 100644
--- a/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
@@ -64,7 +64,7 @@
         /*history_service=*/nullptr,
         /*sync_service=*/&sync_service_,
         /*strike_database=*/nullptr,
-        /*image_fetcher=*/nullptr);
+        /*image_fetcher=*/nullptr, /*shared_storage_handler=*/nullptr);
     autofill_client_->set_test_payments_network_interface(
         std::make_unique<payments::TestPaymentsNetworkInterface>(
             autofill_client_->GetURLLoaderFactory(),
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc
index 93a04fd..c5c0049 100644
--- a/components/autofill/core/browser/personal_data_manager.cc
+++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -362,7 +362,8 @@
     history::HistoryService* history_service,
     syncer::SyncService* sync_service,
     StrikeDatabaseBase* strike_database,
-    AutofillImageFetcherBase* image_fetcher) {
+    AutofillImageFetcherBase* image_fetcher,
+    std::unique_ptr<AutofillSharedStorageHandler> shared_storage_handler) {
   address_data_manager_ = std::make_unique<AddressDataManager>(
       profile_database,
       base::BindRepeating(&PersonalDataManager::NotifyPersonalDataObserver,
@@ -392,6 +393,8 @@
 
   image_fetcher_ = image_fetcher;
 
+  shared_storage_handler_ = std::move(shared_storage_handler);
+
   AutofillMetrics::LogIsAutofillEnabledAtStartup(IsAutofillEnabled());
   AutofillMetrics::LogIsAutofillProfileEnabledAtStartup(
       IsAutofillProfileEnabled());
@@ -2383,6 +2386,9 @@
 
 void PersonalDataManager::OnServerCreditCardsRefreshed() {
   ProcessCardArtUrlChanges();
+  if (shared_storage_handler_) {
+    shared_storage_handler_->OnServerCardDataRefreshed(server_credit_cards_);
+  }
 }
 
 void PersonalDataManager::ProcessCardArtUrlChanges() {
diff --git a/components/autofill/core/browser/personal_data_manager.h b/components/autofill/core/browser/personal_data_manager.h
index c1b44aaf..dbd161a 100644
--- a/components/autofill/core/browser/personal_data_manager.h
+++ b/components/autofill/core/browser/personal_data_manager.h
@@ -24,6 +24,7 @@
 #include "build/build_config.h"
 #include "components/autofill/core/browser/address_data_cleaner.h"
 #include "components/autofill/core/browser/address_data_manager.h"
+#include "components/autofill/core/browser/autofill_shared_storage_handler.h"
 #include "components/autofill/core/browser/data_model/autofill_offer_data.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/autofill_wallet_usage_data.h"
@@ -162,15 +163,17 @@
   // (sync disabled by CLI) or outlives this object, it may not have started yet
   // but its preferences can already be queried. |image_fetcher| is to fetch the
   // customized images for autofill data.
-  void Init(scoped_refptr<AutofillWebDataService> profile_database,
-            scoped_refptr<AutofillWebDataService> account_database,
-            PrefService* pref_service,
-            PrefService* local_state,
-            signin::IdentityManager* identity_manager,
-            history::HistoryService* history_service,
-            syncer::SyncService* sync_service,
-            StrikeDatabaseBase* strike_database,
-            AutofillImageFetcherBase* image_fetcher);
+  void Init(
+      scoped_refptr<AutofillWebDataService> profile_database,
+      scoped_refptr<AutofillWebDataService> account_database,
+      PrefService* pref_service,
+      PrefService* local_state,
+      signin::IdentityManager* identity_manager,
+      history::HistoryService* history_service,
+      syncer::SyncService* sync_service,
+      StrikeDatabaseBase* strike_database,
+      AutofillImageFetcherBase* image_fetcher,
+      std::unique_ptr<AutofillSharedStorageHandler> shared_storage_handler);
 
   // KeyedService:
   void Shutdown() override;
@@ -919,6 +922,9 @@
   // necessary to ensure it always has a value.
   mutable std::string experiment_country_code_;
 
+  // The shared storage handler this instance uses.
+  std::unique_ptr<AutofillSharedStorageHandler> shared_storage_handler_;
+
   // The PrefService that this instance uses. Must outlive this instance.
   raw_ptr<PrefService> pref_service_ = nullptr;
 
diff --git a/components/autofill/core/browser/personal_data_manager_test_base.cc b/components/autofill/core/browser/personal_data_manager_test_base.cc
index 8517575..760f14cf 100644
--- a/components/autofill/core/browser/personal_data_manager_test_base.cc
+++ b/components/autofill/core/browser/personal_data_manager_test_base.cc
@@ -100,7 +100,7 @@
       profile_database_service_, account_database_service_, prefs_.get(),
       prefs_.get(), identity_test_env_.identity_manager(),
       /*history_service=*/nullptr, &sync_service_, strike_database_.get(),
-      /*image_fetcher=*/nullptr);
+      /*image_fetcher=*/nullptr, /*shared_storage_handler=*/nullptr);
   personal_data->AddObserver(&personal_data_observer_);
   personal_data->OnStateChanged(&sync_service_);
   std::move(waiter).Wait();
diff --git a/components/component_updater/installer_policies/tpcd_metadata_component_installer_policy.cc b/components/component_updater/installer_policies/tpcd_metadata_component_installer_policy.cc
index b454b11b0..e1a0593 100644
--- a/components/component_updater/installer_policies/tpcd_metadata_component_installer_policy.cc
+++ b/components/component_updater/installer_policies/tpcd_metadata_component_installer_policy.cc
@@ -147,6 +147,11 @@
       WriteMetrics(TpcdMetadataInstallationResult::kErroneousSpec);
       return false;
     }
+
+    if (!me.has_source()) {
+      WriteMetrics(TpcdMetadataInstallationResult::kErroneousSource);
+      return false;
+    }
   }
 
   WriteMetrics(TpcdMetadataInstallationResult::kSuccessful);
diff --git a/components/component_updater/installer_policies/tpcd_metadata_component_installer_policy.h b/components/component_updater/installer_policies/tpcd_metadata_component_installer_policy.h
index 864c7cf14..c3abf6b2 100644
--- a/components/component_updater/installer_policies/tpcd_metadata_component_installer_policy.h
+++ b/components/component_updater/installer_policies/tpcd_metadata_component_installer_policy.h
@@ -26,6 +26,8 @@
 
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
+// NOTE: Keep in sync with `TpcdMetadataInstallationResult` at
+// src/tools/metrics/histograms/metadata/navigation/enums.xml
 enum class TpcdMetadataInstallationResult {
   // The metadata component was successfully .
   kSuccessful = 0,
@@ -37,7 +39,9 @@
   kParsingToProtoFailed = 3,
   // One or more of the specs are erroneous or missing.
   kErroneousSpec = 4,
-  kMaxValue = kErroneousSpec,
+  // The field is erroneous or missing.
+  kErroneousSource = 5,
+  kMaxValue = kErroneousSource,
 };
 
 class TpcdMetadataComponentInstallerPolicy : public ComponentInstallerPolicy {
diff --git a/components/cronet/android/api.txt b/components/cronet/android/api.txt
index 2762af9..59d04cb 100644
--- a/components/cronet/android/api.txt
+++ b/components/cronet/android/api.txt
@@ -177,7 +177,7 @@
   public org.chromium.net.DnsOptions$StaleDnsOptions$Builder setFreshLookupTimeoutMillis(long);
   public org.chromium.net.DnsOptions$StaleDnsOptions$Builder setFreshLookupTimeout(java.time.Duration);
   public org.chromium.net.DnsOptions$StaleDnsOptions$Builder setMaxExpiredDelayMillis(long);
-  public org.chromium.net.DnsOptions$StaleDnsOptions$Builder setMaxExpiredDelayMillis(java.time.Duration);
+  public org.chromium.net.DnsOptions$StaleDnsOptions$Builder setMaxExpiredDelay(java.time.Duration);
   public org.chromium.net.DnsOptions$StaleDnsOptions$Builder allowCrossNetworkUsage(boolean);
   public org.chromium.net.DnsOptions$StaleDnsOptions$Builder useStaleOnNameNotResolved(boolean);
   public org.chromium.net.DnsOptions$StaleDnsOptions build();
@@ -379,6 +379,7 @@
   public org.chromium.net.QuicOptions$Builder setPreCryptoHandshakeIdleTimeoutSeconds(long);
   public org.chromium.net.QuicOptions$Builder setCryptoHandshakeTimeoutSeconds(long);
   public org.chromium.net.QuicOptions$Builder setIdleConnectionTimeoutSeconds(long);
+  public org.chromium.net.QuicOptions$Builder setIdleConnectionTimeout(java.time.Duration);
   public org.chromium.net.QuicOptions$Builder setRetransmittableOnWireTimeoutMillis(long);
   public org.chromium.net.QuicOptions$Builder closeSessionsOnIpChange(boolean);
   public org.chromium.net.QuicOptions$Builder goawaySessionsOnIpChange(boolean);
@@ -392,7 +393,7 @@
 }
 public interface org.chromium.net.QuicOptions$QuichePassthroughOption extends java.lang.annotation.Annotation {
 }
-public class org.chromium.net.QuicOptions {
+public final class org.chromium.net.QuicOptions {
   public java.util.Set<java.lang.String> getQuicHostAllowlist();
   public java.util.Set<java.lang.String> getEnabledQuicVersions();
   public java.util.Set<java.lang.String> getConnectionOptions();
@@ -632,4 +633,4 @@
   public static org.chromium.net.apihelpers.JsonCronetCallback forJsonBody(org.chromium.net.apihelpers.RedirectHandler, org.chromium.net.apihelpers.CronetRequestCompletionListener<org.json.JSONObject>);
   public static org.chromium.net.apihelpers.UrlRequestCallbacks$CallbackAndResponseFuturePair<org.json.JSONObject, org.chromium.net.apihelpers.JsonCronetCallback> forJsonBody(org.chromium.net.apihelpers.RedirectHandler);
 }
-Stamp: fb82aaef920d5db0ac22a2df3ddd26b4
+Stamp: 73bce49b7e758c401460fcb503a7345b
diff --git a/components/cronet/android/api/src/org/chromium/net/DnsOptions.java b/components/cronet/android/api/src/org/chromium/net/DnsOptions.java
index d521c53..b79fe4c6 100644
--- a/components/cronet/android/api/src/org/chromium/net/DnsOptions.java
+++ b/components/cronet/android/api/src/org/chromium/net/DnsOptions.java
@@ -6,11 +6,13 @@
 
 import android.os.Build.VERSION_CODES;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RequiresOptIn;
 
 import java.time.Duration;
+import java.util.Objects;
 
 /**
  * A class configuring Cronet's host resolution functionality. Note that while we refer to {@code
@@ -173,7 +175,8 @@
              * @return the builder for chaining
              */
             @RequiresApi(VERSION_CODES.O)
-            public Builder setFreshLookupTimeout(Duration freshLookupTimeout) {
+            public Builder setFreshLookupTimeout(@NonNull Duration freshLookupTimeout) {
+                Objects.requireNonNull(freshLookupTimeout);
                 return setFreshLookupTimeoutMillis(freshLookupTimeout.toMillis());
             }
 
@@ -194,7 +197,8 @@
              * @return the builder for chaining
              */
             @RequiresApi(VERSION_CODES.O)
-            public Builder setMaxExpiredDelayMillis(Duration maxExpiredDelay) {
+            public Builder setMaxExpiredDelay(@NonNull Duration maxExpiredDelay) {
+                Objects.requireNonNull(maxExpiredDelay);
                 return setMaxExpiredDelayMillis(maxExpiredDelay.toMillis());
             }
 
@@ -333,7 +337,8 @@
          * @return the builder for chaining
          */
         @RequiresApi(api = VERSION_CODES.O)
-        public Builder setPersistDelay(Duration persistToDiskPeriod) {
+        public Builder setPersistDelay(@NonNull Duration persistToDiskPeriod) {
+            Objects.requireNonNull(persistToDiskPeriod);
             return setPersistHostCachePeriodMillis(persistToDiskPeriod.toMillis());
         }
 
diff --git a/components/cronet/android/api/src/org/chromium/net/QuicOptions.java b/components/cronet/android/api/src/org/chromium/net/QuicOptions.java
index 3a217e3..4da1b8f 100644
--- a/components/cronet/android/api/src/org/chromium/net/QuicOptions.java
+++ b/components/cronet/android/api/src/org/chromium/net/QuicOptions.java
@@ -4,21 +4,27 @@
 
 package org.chromium.net;
 
+import android.os.Build.VERSION_CODES;
+
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RequiresOptIn;
 
+import java.time.Duration;
 import java.util.Collections;
 import java.util.LinkedHashSet;
+import java.util.Objects;
 import java.util.Set;
 
 /**
  * Configuration options for QUIC in Cronet.
  *
- * <p>The settings in this class are only relevant if QUIC is enabled. Use
- * {@link org.chromium.net.CronetEngine.Builder#enableQuic(boolean)} to enable / disable QUIC for
- * the Cronet engine.
+ * <p>The settings in this class are only relevant if QUIC is enabled. Use {@link
+ * org.chromium.net.CronetEngine.Builder#enableQuic(boolean)} to enable / disable QUIC for the
+ * Cronet engine.
  */
-public class QuicOptions {
+public final class QuicOptions {
     private final Set<String> mQuicHostAllowlist;
     private final Set<String> mEnabledQuicVersions;
 
@@ -329,9 +335,13 @@
         }
 
         /**
-         * Sets the maximum idle time for a connection.
+         * Sets the maximum idle time for a connection. The actual value for the idle timeout is the
+         * minimum of this value and the server's and is negotiated during the handshake. Thus, it
+         * only applies after the handshake has completed. If no activity is detected on the
+         * connection for the set duration, the connection is closed.
          *
-         * TODO what happens to connection that are idle for too long?
+         * <p>See <a href="https://www.rfc-editor.org/rfc/rfc9114.html#name-idle-connections">RFC
+         * 9114, section 5.1 </a> for more details.
          *
          * @return the builder for chaining
          */
@@ -341,6 +351,18 @@
         }
 
         /**
+         * Same as {@link #setIdleConnectionTimeoutSeconds(long)} but using {@link
+         * java.time.Duration}.
+         *
+         * @return the builder for chaining
+         */
+        @RequiresApi(VERSION_CODES.O)
+        public Builder setIdleConnectionTimeout(@NonNull Duration idleConnectionTimeout) {
+            Objects.requireNonNull(idleConnectionTimeout);
+            return setIdleConnectionTimeoutSeconds(idleConnectionTimeout.toSeconds());
+        }
+
+        /**
          * Sets the maximum desired time between packets on wire.
          *
          * <p>When the retransmittable-on-wire time is exceeded Cronet will probe quality of the
diff --git a/components/cronet/android/api_version.txt b/components/cronet/android/api_version.txt
index f64f5d8d..f04c001f 100644
--- a/components/cronet/android/api_version.txt
+++ b/components/cronet/android/api_version.txt
@@ -1 +1 @@
-27
+29
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/AndroidHttpEngineBuilderWrapper.java b/components/cronet/android/java/src/org/chromium/net/impl/AndroidHttpEngineBuilderWrapper.java
index ba31d0d..53cf76ac 100644
--- a/components/cronet/android/java/src/org/chromium/net/impl/AndroidHttpEngineBuilderWrapper.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/AndroidHttpEngineBuilderWrapper.java
@@ -19,6 +19,7 @@
 import org.chromium.net.telemetry.ExperimentalOptions;
 import org.chromium.net.telemetry.OptionalBoolean;
 
+import java.time.Duration;
 import java.util.Date;
 import java.util.Set;
 
@@ -116,6 +117,8 @@
         // This only translates known experimental options
         ExperimentalOptions options = new ExperimentalOptions(stringOptions);
         mBackend.setConnectionMigrationOptions(parseConnectionMigrationOptions(options));
+        mBackend.setDnsOptions(parseDnsOptions(options));
+        mBackend.setQuicOptions(parseQuicOptions(options));
         return this;
     }
 
@@ -152,6 +155,85 @@
         return cmOptionsBuilder.build();
     }
 
+    @VisibleForTesting
+    public static android.net.http.DnsOptions parseDnsOptions(ExperimentalOptions options) {
+        android.net.http.DnsOptions.StaleDnsOptions.Builder staleDnsOptionBuilder =
+                new android.net.http.DnsOptions.StaleDnsOptions.Builder();
+        int staleDnsDelay = options.getStaleDnsDelayMillisOption();
+        if (staleDnsDelay != ExperimentalOptions.UNSET_INT_VALUE) {
+            staleDnsOptionBuilder.setFreshLookupTimeout(Duration.ofMillis(staleDnsDelay));
+        }
+
+        int expiredDelay = options.getStaleDnsMaxExpiredTimeMillisOption();
+        if (expiredDelay != ExperimentalOptions.UNSET_INT_VALUE) {
+            staleDnsOptionBuilder.setMaxExpiredDelay(Duration.ofMillis(expiredDelay));
+        }
+
+        staleDnsOptionBuilder
+                .setAllowCrossNetworkUsage(
+                        optionalBooleanToMigrationOptionState(
+                                options.getStaleDnsAllowOtherNetworkOption()))
+                .setUseStaleOnNameNotResolved(
+                        optionalBooleanToMigrationOptionState(
+                                options.getStaleDnsUseStaleOnNameNotResolvedOption()));
+
+        android.net.http.DnsOptions.Builder dnsOptionsBuilder =
+                new android.net.http.DnsOptions.Builder();
+        dnsOptionsBuilder
+                .setUseHttpStackDnsResolver(
+                        optionalBooleanToMigrationOptionState(options.getAsyncDnsEnableOption()))
+                .setStaleDns(
+                        optionalBooleanToMigrationOptionState(options.getStaleDnsEnableOption()))
+                .setStaleDnsOptions(staleDnsOptionBuilder.build())
+                .setPreestablishConnectionsToStaleDnsResults(
+                        optionalBooleanToMigrationOptionState(
+                                options.getRaceStaleDnsOnConnection()))
+                .setPersistHostCache(
+                        optionalBooleanToMigrationOptionState(
+                                options.getStaleDnsPersistToDiskOption()));
+        int persistHostCachePeriod = options.getStaleDnsPersistDelayMillisOption();
+        if (persistHostCachePeriod != ExperimentalOptions.UNSET_INT_VALUE) {
+            dnsOptionsBuilder.setPersistHostCachePeriod(Duration.ofMillis(persistHostCachePeriod));
+        }
+
+        return dnsOptionsBuilder.build();
+    }
+
+    @VisibleForTesting
+    public static android.net.http.QuicOptions parseQuicOptions(ExperimentalOptions options) {
+        android.net.http.QuicOptions.Builder quicOptionsBuilder =
+                new android.net.http.QuicOptions.Builder();
+
+        if (options.getHostWhitelist() != null) {
+            for (String host : options.getHostWhitelist().split(",")) {
+                quicOptionsBuilder.addAllowedQuicHost(host);
+            }
+        }
+
+        int inMemoryServerConfigsCacheSize = options.getMaxServerConfigsStoredInPropertiesOption();
+        if (inMemoryServerConfigsCacheSize != ExperimentalOptions.UNSET_INT_VALUE) {
+            quicOptionsBuilder.setInMemoryServerConfigsCacheSize(inMemoryServerConfigsCacheSize);
+        }
+
+        String handshakeUserAgent = options.getUserAgentId();
+        if (handshakeUserAgent != null) {
+            quicOptionsBuilder.setHandshakeUserAgent(handshakeUserAgent);
+        }
+
+        int idleConnectionTimeoutSeconds = options.getIdleConnectionTimeoutSecondsOption();
+        if (idleConnectionTimeoutSeconds != ExperimentalOptions.UNSET_INT_VALUE) {
+            quicOptionsBuilder.setIdleConnectionTimeout(
+                    Duration.ofSeconds(idleConnectionTimeoutSeconds));
+        }
+
+        return quicOptionsBuilder.build();
+    }
+
+    /**
+     * HttpEngine XOptions exposes X_OPTION_* IntDefs that map to the same integer values. To
+     * simplify the code, we are reusing ConnectionMigrationOptions.MIGRATION_OPTION_* for
+     * DnsOptions and QuicOptions.
+     */
     private static int optionalBooleanToMigrationOptionState(OptionalBoolean value) {
         switch (value) {
             case TRUE:
diff --git a/components/cronet/android/java/src/org/chromium/net/telemetry/ExperimentalOptions.java b/components/cronet/android/java/src/org/chromium/net/telemetry/ExperimentalOptions.java
index bb97ba1b..d35aad2 100644
--- a/components/cronet/android/java/src/org/chromium/net/telemetry/ExperimentalOptions.java
+++ b/components/cronet/android/java/src/org/chromium/net/telemetry/ExperimentalOptions.java
@@ -114,6 +114,19 @@
                 getOrDefault(QUIC, "allow_port_migration", null, Boolean.class));
     }
 
+    public OptionalBoolean getRaceStaleDnsOnConnection() {
+        return OptionalBoolean.fromBoolean(
+                getOrDefault(QUIC, "race_stale_dns_on_connection", null, Boolean.class));
+    }
+
+    public String getHostWhitelist() {
+        return getOrDefault(QUIC, "host_whitelist", null, String.class);
+    }
+
+    public String getUserAgentId() {
+        return getOrDefault(QUIC, "user_agent_id", null, String.class);
+    }
+
     public OptionalBoolean getAsyncDnsEnableOption() {
         return OptionalBoolean.fromBoolean(getOrDefault(ASYNC_DNS, "enable", null, Boolean.class));
     }
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/impl/AndroidHttpEngineBuilderWrapperTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/impl/AndroidHttpEngineBuilderWrapperTest.java
index fbb0ccf..c0c3754 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/impl/AndroidHttpEngineBuilderWrapperTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/impl/AndroidHttpEngineBuilderWrapperTest.java
@@ -9,6 +9,8 @@
 
 import static org.chromium.net.ExperimentalOptionsTranslationTestUtil.assertJsonEquals;
 import static org.chromium.net.impl.AndroidHttpEngineBuilderWrapper.parseConnectionMigrationOptions;
+import static org.chromium.net.impl.AndroidHttpEngineBuilderWrapper.parseDnsOptions;
+import static org.chromium.net.impl.AndroidHttpEngineBuilderWrapper.parseQuicOptions;
 
 import android.content.Context;
 import android.net.http.HttpEngine;
@@ -28,6 +30,8 @@
 import org.chromium.net.ExperimentalOptionsTranslationTestUtil.MockCronetBuilderImpl;
 import org.chromium.net.telemetry.ExperimentalOptions;
 
+import java.time.Duration;
+
 @Batch(Batch.UNIT_TESTS)
 @RunWith(AndroidJUnit4.class)
 @OptIn(markerClass = {org.chromium.net.ConnectionMigrationOptions.Experimental.class})
@@ -175,6 +179,123 @@
 
     // ------------ End migrate_sessions_early_v2 specific tests -------------------
 
+    @Test
+    @SmallTest
+    public void testParseDnsOptions_allSet_returnsCorrectValues() {
+        long delay_ms = 373740587;
+        long persist_delay_ms = 737740529;
+        long max_expired_time_ms = 629397243;
+        ExperimentalOptions options =
+                new ExperimentalOptions(
+                        "{  \"AsyncDNS\": { \"enable\": true },  \"StaleDNS\": {    \"enable\":"
+                                + " true,  \"persist_to_disk\": false,    \"persist_delay_ms\": "
+                                + persist_delay_ms
+                                + ",\"allow_other_network\": true,    \"delay_ms\": "
+                                + delay_ms
+                                + ",\"use_stale_on_name_not_resolved\": true,"
+                                + " \"max_expired_time_ms\":"
+                                + max_expired_time_ms
+                                + "  },  \"QUIC\": {    \"race_stale_dns_on_connection\": true }}");
+
+        android.net.http.DnsOptions dnsOptions = parseDnsOptions(options);
+        // AsyncDNS
+        assertThat(dnsOptions.getUseHttpStackDnsResolver())
+                .isEqualTo(android.net.http.DnsOptions.DNS_OPTION_ENABLED);
+        // persist_to_disk
+        assertThat(dnsOptions.getPersistHostCache())
+                .isEqualTo(android.net.http.DnsOptions.DNS_OPTION_DISABLED);
+        assertThat(dnsOptions.getPersistHostCachePeriod())
+                .isEqualTo(Duration.ofMillis(persist_delay_ms));
+        assertThat(dnsOptions.getStaleDns())
+                .isEqualTo(android.net.http.DnsOptions.DNS_OPTION_ENABLED);
+        // race_stale_dns_on_connection
+        assertThat(dnsOptions.getPreestablishConnectionsToStaleDnsResults())
+                .isEqualTo(android.net.http.DnsOptions.DNS_OPTION_ENABLED);
+
+        android.net.http.DnsOptions.StaleDnsOptions staleDnsOptions =
+                dnsOptions.getStaleDnsOptions();
+        assertThat(staleDnsOptions.getFreshLookupTimeout()).isEqualTo(Duration.ofMillis(delay_ms));
+        assertThat(staleDnsOptions.getMaxExpiredDelay())
+                .isEqualTo(Duration.ofMillis(max_expired_time_ms));
+        // allow_other_network
+        assertThat(staleDnsOptions.getAllowCrossNetworkUsage())
+                .isEqualTo(android.net.http.DnsOptions.DNS_OPTION_ENABLED);
+        assertThat(staleDnsOptions.getUseStaleOnNameNotResolved())
+                .isEqualTo(android.net.http.DnsOptions.DNS_OPTION_ENABLED);
+    }
+
+    @Test
+    @SmallTest
+    public void testParseDnsOptions_noneSet_returnsCorrectValues() {
+        ExperimentalOptions options =
+                new ExperimentalOptions(
+                        "{  \"AsyncDNS\": { },  \"StaleDNS\": { },  \"QUIC\": { }}");
+
+        android.net.http.DnsOptions dnsOptions = parseDnsOptions(options);
+        // AsyncDNS
+        assertThat(dnsOptions.getUseHttpStackDnsResolver())
+                .isEqualTo(android.net.http.DnsOptions.DNS_OPTION_UNSPECIFIED);
+        // persist_to_disk
+        assertThat(dnsOptions.getPersistHostCache())
+                .isEqualTo(android.net.http.DnsOptions.DNS_OPTION_UNSPECIFIED);
+        assertThat(dnsOptions.getPersistHostCachePeriod()).isNull();
+        assertThat(dnsOptions.getStaleDns())
+                .isEqualTo(android.net.http.DnsOptions.DNS_OPTION_UNSPECIFIED);
+        // race_stale_dns_on_connection
+        assertThat(dnsOptions.getPreestablishConnectionsToStaleDnsResults())
+                .isEqualTo(android.net.http.DnsOptions.DNS_OPTION_UNSPECIFIED);
+
+        android.net.http.DnsOptions.StaleDnsOptions staleDnsOptions =
+                dnsOptions.getStaleDnsOptions();
+        assertThat(staleDnsOptions.getFreshLookupTimeout()).isNull();
+        assertThat(staleDnsOptions.getMaxExpiredDelay()).isNull();
+        // allow_other_network
+        assertThat(staleDnsOptions.getAllowCrossNetworkUsage())
+                .isEqualTo(android.net.http.DnsOptions.DNS_OPTION_UNSPECIFIED);
+        assertThat(staleDnsOptions.getUseStaleOnNameNotResolved())
+                .isEqualTo(android.net.http.DnsOptions.DNS_OPTION_UNSPECIFIED);
+    }
+
+    @Test
+    @SmallTest
+    public void testParseQuicOptions_allSet_returnsCorrectValues() {
+        int max_server_config = 466360493;
+        int idle_conn_timeout = 435320688;
+        String user_agent_id = "handshakeUserAgent";
+        String host_whitelist = "quicHost1.com,quicHost2.com";
+        ExperimentalOptions options =
+                new ExperimentalOptions(
+                        "{  \"QUIC\": {   \"host_whitelist\": \""
+                                + host_whitelist
+                                + "\",   \"max_server_configs_stored_in_properties\": "
+                                + max_server_config
+                                + ",  \"user_agent_id\": \""
+                                + user_agent_id
+                                + "\",   \"idle_connection_timeout_seconds\": "
+                                + idle_conn_timeout
+                                + "   }}");
+        android.net.http.QuicOptions quicOptions = parseQuicOptions(options);
+
+        assertThat(quicOptions.getAllowedQuicHosts())
+                .containsExactlyElementsIn(host_whitelist.split(","));
+        assertThat(quicOptions.getInMemoryServerConfigsCacheSize()).isEqualTo(max_server_config);
+        assertThat(quicOptions.getHandshakeUserAgent()).isEqualTo(user_agent_id);
+        assertThat(quicOptions.getIdleConnectionTimeout())
+                .isEqualTo(Duration.ofSeconds(idle_conn_timeout));
+    }
+
+    @Test
+    @SmallTest
+    public void testParseQuicOptions_noneSet_returnsCorrectValues() {
+        ExperimentalOptions options = new ExperimentalOptions("{  \"QUIC\": {  }}");
+        android.net.http.QuicOptions quicOptions = parseQuicOptions(options);
+
+        assertThat(quicOptions.getAllowedQuicHosts()).isEmpty();
+        assertThat(quicOptions.hasInMemoryServerConfigsCacheSize()).isFalse();
+        assertThat(quicOptions.getHandshakeUserAgent()).isNull();
+        assertThat(quicOptions.getIdleConnectionTimeout()).isNull();
+    }
+
     /**
      * JUnit uses reflection to fetch the TestClass's annotation and parameter types. Hence fails
      * when it can't find android.net.http.* class for Android T- devices. This class abstracts the
diff --git a/components/enterprise/client_certificates/core/ec_private_key_factory.cc b/components/enterprise/client_certificates/core/ec_private_key_factory.cc
index 4de11c3e..9100cede 100644
--- a/components/enterprise/client_certificates/core/ec_private_key_factory.cc
+++ b/components/enterprise/client_certificates/core/ec_private_key_factory.cc
@@ -30,7 +30,7 @@
 }
 
 scoped_refptr<ECPrivateKey> LoadKeyFromWrapped(
-    const std::vector<const uint8_t>& wrapped_key) {
+    const std::vector<uint8_t>& wrapped_key) {
   auto key = crypto::ECPrivateKey::CreateFromPrivateKeyInfo(wrapped_key);
   if (!key) {
     return nullptr;
@@ -61,9 +61,9 @@
   const auto& wrapped_key_str = serialized_private_key.wrapped_key();
   base::ThreadPool::PostTaskAndReplyWithResult(
       FROM_HERE, {base::MayBlock()},
-      base::BindOnce(LoadKeyFromWrapped,
-                     std::vector<const uint8_t>(wrapped_key_str.begin(),
-                                                wrapped_key_str.end())),
+      base::BindOnce(
+          LoadKeyFromWrapped,
+          std::vector<uint8_t>(wrapped_key_str.begin(), wrapped_key_str.end())),
       std::move(callback));
 }
 
diff --git a/components/enterprise/client_certificates/core/unexportable_private_key_factory.cc b/components/enterprise/client_certificates/core/unexportable_private_key_factory.cc
index 5c39ba9..e165de0 100644
--- a/components/enterprise/client_certificates/core/unexportable_private_key_factory.cc
+++ b/components/enterprise/client_certificates/core/unexportable_private_key_factory.cc
@@ -41,7 +41,7 @@
 }
 
 scoped_refptr<UnexportablePrivateKey> LoadKeyFromWrapped(
-    const std::vector<const uint8_t>& wrapped_key) {
+    const std::vector<uint8_t>& wrapped_key) {
   auto provider = crypto::GetUnexportableKeyProvider();
   if (!provider) {
     return nullptr;
@@ -92,9 +92,9 @@
   const auto& wrapped_key_str = serialized_private_key.wrapped_key();
   base::ThreadPool::PostTaskAndReplyWithResult(
       FROM_HERE, {base::MayBlock()},
-      base::BindOnce(LoadKeyFromWrapped,
-                     std::vector<const uint8_t>(wrapped_key_str.begin(),
-                                                wrapped_key_str.end())),
+      base::BindOnce(
+          LoadKeyFromWrapped,
+          std::vector<uint8_t>(wrapped_key_str.begin(), wrapped_key_str.end())),
       std::move(callback));
 }
 
diff --git a/components/feature_engagement/public/event_constants.cc b/components/feature_engagement/public/event_constants.cc
index fe8e1ca..7d930d71 100644
--- a/components/feature_engagement/public/event_constants.cc
+++ b/components/feature_engagement/public/event_constants.cc
@@ -121,8 +121,6 @@
 const char kIOSSwipeBackForwardUsed[] = "swiped_back_forward_used";
 const char kEnhancedSafeBrowsingPromoCriterionMet[] =
     "enhanced_safe_browsing_promo_criterion_met";
-const char kEnhancedSafeBrowsingInlinePromoClosed[] =
-    "enhanced_safe_browsing_inline_promo_closed";
 #endif  // BUILDFLAG(IS_IOS)
 
 #if BUILDFLAG(IS_ANDROID)
diff --git a/components/feature_engagement/public/event_constants.h b/components/feature_engagement/public/event_constants.h
index 14dbff1..e19e66c 100644
--- a/components/feature_engagement/public/event_constants.h
+++ b/components/feature_engagement/public/event_constants.h
@@ -229,10 +229,6 @@
 // inline and blue-dot promos eligible to be displayed.
 extern const char kEnhancedSafeBrowsingPromoCriterionMet[];
 
-// The user has tapped on the 'X' button to remove the Enhanced Safe Browsing
-// inline promo from the settings menu.
-extern const char kEnhancedSafeBrowsingInlinePromoClosed[];
-
 #endif  // BUILDFLAG(IS_IOS)
 
 // Android.
diff --git a/components/feature_engagement/public/feature_configurations.cc b/components/feature_engagement/public/feature_configurations.cc
index 7809f24..2aa7a83 100644
--- a/components/feature_engagement/public/feature_configurations.cc
+++ b/components/feature_engagement/public/feature_configurations.cc
@@ -2017,9 +2017,6 @@
     config->event_configs.insert(EventConfig(
         feature_engagement::events::kEnhancedSafeBrowsingPromoCriterionMet,
         Comparator(GREATER_THAN_OR_EQUAL, 1), 7, 360));
-    config->event_configs.insert(EventConfig(
-        feature_engagement::events::kEnhancedSafeBrowsingInlinePromoClosed,
-        Comparator(LESS_THAN, 1), 360, 360));
     config->used =
         EventConfig("enhanced_safe_browsing_inline_promo_used",
                     Comparator(EQUAL, 0), feature_engagement::kMaxStoragePeriod,
diff --git a/components/history_clusters/core/history_clusters_util.cc b/components/history_clusters/core/history_clusters_util.cc
index aaea704..6fee2e95 100644
--- a/components/history_clusters/core/history_clusters_util.cc
+++ b/components/history_clusters/core/history_clusters_util.cc
@@ -5,6 +5,7 @@
 #include "components/history_clusters/core/history_clusters_util.h"
 
 #include <algorithm>
+#include <set>
 
 #include "base/containers/contains.h"
 #include "base/containers/cxx20_erase.h"
@@ -470,4 +471,16 @@
   return false;
 }
 
+std::set<std::string> GetClusterCategoryIds(const history::Cluster& cluster) {
+  std::set<std::string> category_ids;
+  for (const auto& visit : cluster.visits) {
+    for (const auto& visit_category : visit.annotated_visit.content_annotations
+                                          .model_annotations.categories) {
+      category_ids.insert(visit_category.id);
+    }
+  }
+
+  return category_ids;
+}
+
 }  // namespace history_clusters
diff --git a/components/history_clusters/core/history_clusters_util.h b/components/history_clusters/core/history_clusters_util.h
index a23a494..4114b32 100644
--- a/components/history_clusters/core/history_clusters_util.h
+++ b/components/history_clusters/core/history_clusters_util.h
@@ -88,6 +88,9 @@
 bool IsClusterInCategories(const history::Cluster& cluster,
                            const base::flat_set<std::string>& categories);
 
+// Return the set of category ids associated with a given cluster.
+std::set<std::string> GetClusterCategoryIds(const history::Cluster& cluster);
+
 }  // namespace history_clusters
 
 #endif  // COMPONENTS_HISTORY_CLUSTERS_CORE_HISTORY_CLUSTERS_UTIL_H_
diff --git a/components/language/core/common/language_experiments.cc b/components/language/core/common/language_experiments.cc
index 8846600f..443c4e8 100644
--- a/components/language/core/common/language_experiments.cc
+++ b/components/language/core/common/language_experiments.cc
@@ -16,7 +16,7 @@
              base::FEATURE_ENABLED_BY_DEFAULT);
 BASE_FEATURE(kContentLanguagesInLanguagePicker,
              "ContentLanguagesInLanguagePicker",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 BASE_FEATURE(kCctAutoTranslate,
              "CCTAutoTranslate",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/components/metrics/structured/BUILD.gn b/components/metrics/structured/BUILD.gn
index 4cc4f5c..bef1628b 100644
--- a/components/metrics/structured/BUILD.gn
+++ b/components/metrics/structured/BUILD.gn
@@ -26,8 +26,9 @@
     "key_data_provider_prefs.h",
     "key_util.cc",
     "key_util.h",
-    "persistent_proto.cc",
     "persistent_proto.h",
+    "persistent_proto_internal.cc",
+    "persistent_proto_internal.h",
     "reporting/structured_metrics_log_metrics.cc",
     "reporting/structured_metrics_log_metrics.h",
     "reporting/structured_metrics_reporting_service.cc",
@@ -60,6 +61,7 @@
     "//components/metrics/structured/mojom",
     "//components/prefs",
     "//crypto",
+    "//third_party/protobuf:protobuf_lite",
   ]
 }
 
diff --git a/components/metrics/structured/DEPS b/components/metrics/structured/DEPS
index 1f172b2..dff9edfa 100644
--- a/components/metrics/structured/DEPS
+++ b/components/metrics/structured/DEPS
@@ -2,4 +2,5 @@
   "+components/metrics",
   "+components/prefs",
   "+tools/metrics/structured",
+  "+third_party/protobuf",
 ]
diff --git a/components/metrics/structured/event_storage.h b/components/metrics/structured/event_storage.h
index 29b538e..510c2cd 100644
--- a/components/metrics/structured/event_storage.h
+++ b/components/metrics/structured/event_storage.h
@@ -6,12 +6,10 @@
 #define COMPONENTS_METRICS_STRUCTURED_EVENT_STORAGE_H_
 
 #include "base/files/file_path.h"
-#include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h"
 #include "third_party/metrics_proto/structured_data.pb.h"
 
 namespace metrics {
 class StructuredEventProto;
-class ChromeUserMetricsExtension;
 }  // namespace metrics
 
 namespace metrics::structured {
@@ -34,10 +32,12 @@
   virtual void OnReady() {}
 
   // Add a new StructuredEventProto to be stored.
-  virtual void AddEvent(StructuredEventProto&& event) = 0;
+  virtual void AddEvent(StructuredEventProto event) = 0;
 
-  // Events are moved to UMA proto to be uploaded.
-  virtual void MoveEvents(ChromeUserMetricsExtension& uma_proto) = 0;
+  // External API for removing events from the storage.
+  // Intended to be used with a Swap for improved performance.
+  virtual ::google::protobuf::RepeatedPtrField<StructuredEventProto>
+  TakeEvents() = 0;
 
   // The number of events that have been recorded.
   virtual int RecordedEventsCount() const = 0;
diff --git a/components/metrics/structured/key_data_file_delegate.cc b/components/metrics/structured/key_data_file_delegate.cc
index 9a733a17..a7e5acb 100644
--- a/components/metrics/structured/key_data_file_delegate.cc
+++ b/components/metrics/structured/key_data_file_delegate.cc
@@ -10,6 +10,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
+#include "base/task/bind_post_task.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/time/time.h"
 #include "components/metrics/structured/histogram_util.h"
diff --git a/components/metrics/structured/key_data_file_delegate.h b/components/metrics/structured/key_data_file_delegate.h
index 85fd13f..1514ebe 100644
--- a/components/metrics/structured/key_data_file_delegate.h
+++ b/components/metrics/structured/key_data_file_delegate.h
@@ -21,7 +21,6 @@
 
 class FilePath;
 class TimeDelta;
-class SequencedTaskRunner;
 
 }  // namespace base
 
@@ -70,8 +69,6 @@
 
   SEQUENCE_CHECKER(sequence_checker_);
 
-  scoped_refptr<base::SequencedTaskRunner> task_runner_;
-
   base::WeakPtrFactory<KeyDataFileDelegate> weak_factory_{this};
 };
 
diff --git a/components/metrics/structured/lib/DEPS b/components/metrics/structured/lib/DEPS
index 39963068..5c5b65a 100644
--- a/components/metrics/structured/lib/DEPS
+++ b/components/metrics/structured/lib/DEPS
@@ -9,4 +9,5 @@
 
 include_rules = [
     "+base",
+    "+third_party/protobuf",
 ]
diff --git a/components/metrics/structured/persistent_proto.cc b/components/metrics/structured/persistent_proto.cc
deleted file mode 100644
index 8745e2e8..0000000
--- a/components/metrics/structured/persistent_proto.cc
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/metrics/structured/persistent_proto.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/files/file_util.h"
-#include "base/files/important_file_writer.h"
-#include "base/functional/bind.h"
-#include "base/logging.h"
-#include "base/rand_util.h"
-#include "base/task/bind_post_task.h"
-#include "base/task/sequenced_task_runner.h"
-#include "base/task/task_traits.h"
-#include "base/task/thread_pool.h"
-#include "base/threading/scoped_blocking_call.h"
-#include "components/metrics/structured/histogram_util.h"
-#include "components/metrics/structured/lib/proto/key.pb.h"
-#include "components/metrics/structured/proto/event_storage.pb.h"
-
-namespace metrics::structured {
-namespace {
-
-template <class T>
-// Attempts to read from |filepath| and returns a string with the file content
-// if successful.
-base::expected<std::unique_ptr<T>, ReadStatus> Read(
-    const base::FilePath& filepath) {
-  if (!base::PathExists(filepath)) {
-    return base::unexpected(ReadStatus::kMissing);
-  }
-
-  std::string proto_str;
-  if (!base::ReadFileToString(filepath, &proto_str)) {
-    return base::unexpected(ReadStatus::kReadError);
-  }
-
-  auto proto = std::make_unique<T>();
-  if (!proto->ParseFromString(proto_str)) {
-    return base::unexpected(ReadStatus::kParseError);
-  }
-
-  return base::ok(std::move(proto));
-}
-
-}  // namespace
-
-template <class T>
-PersistentProto<T>::PersistentProto(
-    const base::FilePath& path,
-    const base::TimeDelta write_delay,
-    typename PersistentProto<T>::ReadCallback on_read,
-    typename PersistentProto<T>::WriteCallback on_write)
-    : on_read_(std::move(on_read)),
-      on_write_(std::move(on_write)),
-      task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
-          {base::TaskPriority::BEST_EFFORT, base::MayBlock(),
-           base::TaskShutdownBehavior::BLOCK_SHUTDOWN})),
-      proto_file_(
-          base::ImportantFileWriter(path,
-                                    task_runner_,
-                                    write_delay,
-                                    "StructuredMetricsPersistentProto")) {
-  task_runner_->PostTaskAndReplyWithResult(
-      FROM_HERE, base::BindOnce(&Read<T>, proto_file_.path()),
-      base::BindOnce(&PersistentProto<T>::OnReadComplete,
-                     weak_factory_.GetWeakPtr()));
-}
-
-template <class T>
-PersistentProto<T>::~PersistentProto() {
-  // Flush any existing writes that are scheduled.
-  if (proto_file_.HasPendingWrite()) {
-    proto_file_.DoScheduledWrite();
-  }
-}
-
-template <class T>
-void PersistentProto<T>::OnReadComplete(
-    base::expected<std::unique_ptr<T>, ReadStatus> read_status) {
-  ReadStatus status;
-
-  if (read_status.has_value()) {
-    status = ReadStatus::kOk;
-    proto_ = std::move(read_status.value());
-  } else {
-    // If there was an error, write an empty proto.
-    status = read_status.error();
-    proto_ = std::make_unique<T>();
-    QueueWrite();
-  }
-
-  // Purge the read proto if |purge_after_reading_|.
-  if (purge_after_reading_) {
-    proto_.reset();
-    proto_ = std::make_unique<T>();
-    QueueWrite();
-    purge_after_reading_ = false;
-  }
-
-  std::move(on_read_).Run(std::move(status));
-}
-
-template <class T>
-void PersistentProto<T>::QueueWrite() {
-  // |proto_| will be null if OnReadComplete() has not finished executing. It is
-  // up to the user to verify that OnReadComplete() has finished with callback
-  // |on_read_| before calling QueueWrite().
-  CHECK(proto_);
-  proto_file_.ScheduleWrite(this);
-}
-
-template <class T>
-void PersistentProto<T>::OnWriteAttempt(bool write_successful) {
-  if (write_successful) {
-    OnWriteComplete(WriteStatus::kOk);
-  } else {
-    OnWriteComplete(WriteStatus::kWriteError);
-  }
-}
-
-template <class T>
-void PersistentProto<T>::OnWriteComplete(const WriteStatus status) {
-  on_write_.Run(status);
-}
-
-template <class T>
-void PersistentProto<T>::Purge() {
-  if (proto_) {
-    proto_.reset();
-    proto_ = std::make_unique<T>();
-    QueueWrite();
-  } else {
-    purge_after_reading_ = true;
-  }
-}
-
-template <class T>
-std::optional<std::string> PersistentProto<T>::SerializeData() {
-  std::string proto_str;
-  if (!proto_->SerializeToString(&proto_str)) {
-    OnWriteComplete(WriteStatus::kSerializationError);
-    return std::nullopt;
-  }
-  proto_file_.RegisterOnNextWriteCallbacks(
-      base::BindOnce(base::IgnoreResult(&base::CreateDirectory),
-                     proto_file_.path().DirName()),
-      base::BindPostTask(base::SequencedTaskRunner::GetCurrentDefault(),
-                         base::BindOnce(&PersistentProto<T>::OnWriteAttempt,
-                                        weak_factory_.GetWeakPtr())));
-  return proto_str;
-}
-
-template <class T>
-void PersistentProto<T>::StartWriteForTesting() {
-  proto_file_.ScheduleWrite(this);
-  proto_file_.DoScheduledWrite();
-}
-
-// A list of all types that the PersistentProto can be used with.
-template class PersistentProto<EventsProto>;
-template class PersistentProto<KeyDataProto>;
-template class PersistentProto<KeyProto>;
-
-}  // namespace metrics::structured
diff --git a/components/metrics/structured/persistent_proto.h b/components/metrics/structured/persistent_proto.h
index cee798ac..9c70f2c 100644
--- a/components/metrics/structured/persistent_proto.h
+++ b/components/metrics/structured/persistent_proto.h
@@ -5,7 +5,10 @@
 #ifndef COMPONENTS_METRICS_STRUCTURED_PERSISTENT_PROTO_H_
 #define COMPONENTS_METRICS_STRUCTURED_PERSISTENT_PROTO_H_
 
+#include <concepts>
+#include <memory>
 #include <optional>
+#include <type_traits>
 
 #include "base/files/file_path.h"
 #include "base/files/important_file_writer.h"
@@ -14,22 +17,10 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/time/time.h"
 #include "base/types/expected.h"
+#include "components/metrics/structured/persistent_proto_internal.h"
+#include "third_party/protobuf/src/google/protobuf/message_lite.h"
 
 namespace metrics::structured {
-// The result of reading a backing file from disk.
-enum class ReadStatus {
-  kOk = 0,
-  kMissing = 1,
-  kReadError = 2,
-  kParseError = 3,
-};
-
-// The result of writing a backing file to disk.
-enum class WriteStatus {
-  kOk = 0,
-  kWriteError = 1,
-  kSerializationError = 2,
-};
 
 // PersistentProto wraps a proto class and persists it to disk. Usage summary.
 //  - Init is asynchronous, usage before |on_read| is called will crash.
@@ -49,96 +40,28 @@
 // checked with the callback |on_read_|. Calling QueueWrite() before
 // OnReadComplete() has finished will result in a crash.
 //
-// WARNING. Every proto this class can be used with needs to be listed at the
-// bottom of the cc file.
+// The |on_write| callback is run each time a write has completed.
 template <class T>
-class PersistentProto : public base::ImportantFileWriter::DataSerializer {
+  requires(std::derived_from<T, google::protobuf::MessageLite>)
+class PersistentProto : public internal::PersistentProtoInternal {
  public:
-  using ReadCallback = base::OnceCallback<void(ReadStatus)>;
-  using WriteCallback = base::RepeatingCallback<void(WriteStatus)>;
+  using internal::PersistentProtoInternal::PersistentProtoInternal;
 
-  PersistentProto(const base::FilePath& path,
-                  base::TimeDelta write_delay,
-                  typename PersistentProto<T>::ReadCallback on_read,
-                  typename PersistentProto<T>::WriteCallback on_write);
-  ~PersistentProto();
-
-  PersistentProto(const PersistentProto&) = delete;
-  PersistentProto& operator=(const PersistentProto&) = delete;
-
-  T* get() { return proto_.get(); }
-
-  T* operator->() {
-    CHECK(proto_);
-    return proto_.get();
+  T* get() { return static_cast<T*>(internal::PersistentProtoInternal::get()); }
+  const T* get() const {
+    return static_cast<T*>(internal::PersistentProtoInternal::get());
   }
 
-  const T* operator->() const {
-    CHECK(proto_);
-    return proto_.get();
-  }
+  T* operator->() { return get(); }
+  const T* operator->() const { return get(); }
 
-  T& operator*() {
-    CHECK(proto_);
-    return *proto_;
-  }
-
-  const T& operator*() const {
-    CHECK(proto_);
-    return *proto_;
-  }
-
-  constexpr bool has_value() const { return proto_.get() != nullptr; }
-
-  constexpr explicit operator bool() const { return has_value(); }
-
-  // Write the backing proto to disk after |save_delay_ms_| has elapsed.
-  void QueueWrite();
-
-  // Safely clear this proto from memory and disk. This is preferred to clearing
-  // the proto, because it ensures the proto is purged even if called before the
-  // backing file is read from disk. In this case, the file is overwritten after
-  // it has been read. In either case, the file is written as soon as possible,
-  // skipping the |save_delay_ms_| wait time.
-  void Purge();
-
-  // base::ImportantFileWriter::DataSerializer:
-  std::optional<std::string> SerializeData() override;
-
-  // Schedules a write to be executed immediately. Only to be used for tests.
-  void StartWriteForTesting();
+  T& operator*() { return *get(); }
+  const T& operator*() const { return *get(); }
 
  private:
-  // Callback when the file has been loaded into a file.
-  void OnReadComplete(
-      base::expected<std::unique_ptr<T>, ReadStatus> read_status);
-
-  // Called after |proto_file_| has attempted to write with the write status
-  // captured in |write_successful|.
-  void OnWriteAttempt(bool write_successful);
-
-  // Called after OnWriteAttempt() or if the write was unsuccessful earlier.
-  void OnWriteComplete(WriteStatus status);
-
-  // Whether we should immediately clear the proto after reading it.
-  bool purge_after_reading_ = false;
-
-  // Run when the cache finishes reading from disk, if provided.
-  ReadCallback on_read_;
-
-  // Run when the cache finishes writing to disk, if provided.
-  WriteCallback on_write_;
-
-  // The proto itself.
-  std::unique_ptr<T> proto_;
-
-  // Task runner for reads and writes to be queued.
-  scoped_refptr<base::SequencedTaskRunner> task_runner_;
-
-  // Persistence for |proto_|.
-  base::ImportantFileWriter proto_file_;
-
-  base::WeakPtrFactory<PersistentProto> weak_factory_{this};
+  std::unique_ptr<google::protobuf::MessageLite> BuildEmptyProto() override {
+    return std::make_unique<T>();
+  }
 };
 
 }  // namespace metrics::structured
diff --git a/components/metrics/structured/persistent_proto_internal.cc b/components/metrics/structured/persistent_proto_internal.cc
new file mode 100644
index 0000000..26b8c94
--- /dev/null
+++ b/components/metrics/structured/persistent_proto_internal.cc
@@ -0,0 +1,153 @@
+#// 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/metrics/structured/persistent_proto_internal.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/files/file_util.h"
+#include "base/files/important_file_writer.h"
+#include "base/functional/bind.h"
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "base/task/bind_post_task.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "components/metrics/structured/histogram_util.h"
+
+namespace metrics::structured::internal {
+
+namespace {
+
+// Attempts to read from |filepath| and returns a string with the file content
+// if successful.
+base::expected<std::string, ReadStatus> Read(const base::FilePath& filepath) {
+  if (!base::PathExists(filepath)) {
+    return base::unexpected(ReadStatus::kMissing);
+  }
+
+  std::string proto_str;
+  if (!base::ReadFileToString(filepath, &proto_str)) {
+    return base::unexpected(ReadStatus::kReadError);
+  }
+
+  return base::ok(std::move(proto_str));
+}
+
+}  // namespace
+
+PersistentProtoInternal::PersistentProtoInternal(
+    const base::FilePath& path,
+    base::TimeDelta write_delay,
+    PersistentProtoInternal::ReadCallback on_read,
+    PersistentProtoInternal::WriteCallback on_write)
+    : on_read_(std::move(on_read)),
+      on_write_(std::move(on_write)),
+      task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
+          {base::TaskPriority::BEST_EFFORT, base::MayBlock(),
+           base::TaskShutdownBehavior::BLOCK_SHUTDOWN})),
+      proto_file_(
+          base::ImportantFileWriter(path,
+                                    task_runner_,
+                                    write_delay,
+                                    "StructuredMetricsPersistentProto")) {
+  task_runner_->PostTaskAndReplyWithResult(
+      FROM_HERE, base::BindOnce(&Read, proto_file_.path()),
+      base::BindOnce(&PersistentProtoInternal::OnReadComplete,
+                     weak_factory_.GetWeakPtr()));
+}
+
+PersistentProtoInternal::~PersistentProtoInternal() {
+  // Flush any existing writes that are scheduled.
+  if (proto_file_.HasPendingWrite()) {
+    proto_file_.DoScheduledWrite();
+  }
+}
+
+void PersistentProtoInternal::OnReadComplete(
+    base::expected<std::string, ReadStatus> read_status) {
+  ReadStatus status;
+
+  if (read_status.has_value()) {
+    status = ReadStatus::kOk;
+    proto_ = BuildEmptyProto();
+
+    if (!proto_->ParseFromString(read_status.value())) {
+      status = ReadStatus::kParseError;
+      QueueWrite();
+    }
+  } else {
+    status = read_status.error();
+  }
+
+  // If there was an error, write an empty proto.
+  if (status != ReadStatus::kOk) {
+    proto_ = BuildEmptyProto();
+    QueueWrite();
+  }
+
+  // Purge the read proto if |purge_after_reading_|.
+  if (purge_after_reading_) {
+    proto_ = BuildEmptyProto();
+    QueueWrite();
+    purge_after_reading_ = false;
+  }
+
+  std::move(on_read_).Run(std::move(status));
+}
+
+void PersistentProtoInternal::QueueWrite() {
+  // |proto_| will be null if OnReadComplete() has not finished executing. It is
+  // up to the user to verify that OnReadComplete() has finished with callback
+  // |on_read_| before calling QueueWrite().
+  CHECK(proto_);
+  proto_file_.ScheduleWrite(this);
+}
+
+void PersistentProtoInternal::OnWriteAttempt(bool write_successful) {
+  if (write_successful) {
+    OnWriteComplete(WriteStatus::kOk);
+  } else {
+    OnWriteComplete(WriteStatus::kWriteError);
+  }
+}
+
+void PersistentProtoInternal::OnWriteComplete(const WriteStatus status) {
+  on_write_.Run(status);
+}
+
+void PersistentProtoInternal::Purge() {
+  if (proto_) {
+    proto_.reset();
+    proto_ = BuildEmptyProto();
+    QueueWrite();
+  } else {
+    purge_after_reading_ = true;
+  }
+}
+
+std::optional<std::string> PersistentProtoInternal::SerializeData() {
+  std::string proto_str;
+  if (!proto_->SerializeToString(&proto_str)) {
+    OnWriteComplete(WriteStatus::kSerializationError);
+    return std::nullopt;
+  }
+  proto_file_.RegisterOnNextWriteCallbacks(
+      base::BindOnce(base::IgnoreResult(&base::CreateDirectory),
+                     proto_file_.path().DirName()),
+      base::BindPostTask(
+          base::SequencedTaskRunner::GetCurrentDefault(),
+          base::BindOnce(&PersistentProtoInternal::OnWriteAttempt,
+                         weak_factory_.GetWeakPtr())));
+  return proto_str;
+}
+
+void PersistentProtoInternal::StartWriteForTesting() {
+  proto_file_.ScheduleWrite(this);
+  proto_file_.DoScheduledWrite();
+}
+
+}  // namespace metrics::structured::internal
diff --git a/components/metrics/structured/persistent_proto_internal.h b/components/metrics/structured/persistent_proto_internal.h
new file mode 100644
index 0000000..c12cf867
--- /dev/null
+++ b/components/metrics/structured/persistent_proto_internal.h
@@ -0,0 +1,123 @@
+// 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_METRICS_STRUCTURED_PERSISTENT_PROTO_INTERNAL_H_
+#define COMPONENTS_METRICS_STRUCTURED_PERSISTENT_PROTO_INTERNAL_H_
+
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "base/files/important_file_writer.h"
+#include "base/functional/callback_forward.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/time/time.h"
+#include "base/types/expected.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/protobuf/src/google/protobuf/message_lite.h"
+
+namespace metrics::structured {
+
+// The result of reading a backing file from disk.
+enum class ReadStatus {
+  kOk = 0,
+  kMissing = 1,
+  kReadError = 2,
+  kParseError = 3,
+};
+
+// The result of writing a backing file to disk.
+enum class WriteStatus {
+  kOk = 0,
+  kWriteError = 1,
+  kSerializationError = 2,
+};
+
+namespace internal {
+
+// Implementation to be used for PersistentProto. Refer to persistent_proto.h
+// for more details.
+class PersistentProtoInternal
+    : public base::ImportantFileWriter::DataSerializer {
+ public:
+  using ReadCallback = base::OnceCallback<void(ReadStatus)>;
+  using WriteCallback = base::RepeatingCallback<void(WriteStatus)>;
+
+  PersistentProtoInternal(const base::FilePath& path,
+                          base::TimeDelta write_delay,
+                          PersistentProtoInternal::ReadCallback on_read,
+                          PersistentProtoInternal::WriteCallback on_write);
+
+  PersistentProtoInternal(const PersistentProtoInternal&) = delete;
+  PersistentProtoInternal& operator=(const PersistentProtoInternal&) = delete;
+
+  ~PersistentProtoInternal() override;
+
+  // This function will be used to create an empty proto to populate or reset
+  // the proto if Purge() is called.
+  virtual std::unique_ptr<google::protobuf::MessageLite> BuildEmptyProto() = 0;
+
+  google::protobuf::MessageLite* get() { return proto_.get(); }
+  const google::protobuf::MessageLite* get() const { return proto_.get(); }
+
+  // Queues a write task on the current task runner.
+  void QueueWrite();
+
+  // Purges the proto by resetting |proto_| and triggering a write. If called
+  // before |proto_| is ready, |proto_| will be purged once it becomes ready.
+  void Purge();
+
+  constexpr bool has_value() const { return proto_.get() != nullptr; }
+
+  constexpr explicit operator bool() const { return has_value(); }
+
+  // base::ImportantFileWriter::DataSerializer:
+  absl::optional<std::string> SerializeData() override;
+
+  // Schedules a write to be executed immediately. Only to be used for tests.
+  void StartWriteForTesting();
+
+ private:
+  // Callback when the file has been loaded into a file.
+  void OnReadComplete(base::expected<std::string, ReadStatus> read_status);
+
+  // Called after |proto_file_| has attempted to write with the write status
+  // captured in |write_successful|.
+  void OnWriteAttempt(bool write_successful);
+
+  // Called after OnWriteAttempt() or if the write was unsuccessful earlier.
+  void OnWriteComplete(WriteStatus status);
+
+  // Whether or not a write is scheduled before OnReadComplete has finished.
+  //
+  // This is to prevent a race condition from happening where OnReadComplete may
+  // not finish before a call to QueueWrite() executes a Write().
+  bool write_queued_before_init_ = false;
+
+  // Whether we should immediately clear the proto after reading it.
+  bool purge_after_reading_ = false;
+
+  // Run when the cache finishes reading from disk, if provided.
+  ReadCallback on_read_;
+
+  // Run when the cache finishes writing to disk, if provided.
+  WriteCallback on_write_;
+
+  // The proto itself.
+  std::unique_ptr<google::protobuf::MessageLite> proto_;
+
+  // Task runner for reads and writes to be queued.
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  // Persistence for |proto_|.
+  base::ImportantFileWriter proto_file_;
+
+  base::WeakPtrFactory<PersistentProtoInternal> weak_factory_{this};
+};
+
+}  // namespace internal
+}  // namespace metrics::structured
+
+#endif  // COMPONENTS_METRICS_STRUCTURED_PERSISTENT_PROTO_INTERNAL_H_
diff --git a/components/metrics/structured/persistent_proto_unittest.cc b/components/metrics/structured/persistent_proto_unittest.cc
index ee703b3..5ce744a4 100644
--- a/components/metrics/structured/persistent_proto_unittest.cc
+++ b/components/metrics/structured/persistent_proto_unittest.cc
@@ -235,4 +235,33 @@
   EXPECT_EQ(write_count_, 1);
 }
 
+TEST_F(PersistentProtoTest, ClearContents) {
+  const auto test_proto = MakeTestProto();
+  WriteToDisk(test_proto);
+
+  std::unique_ptr<PersistentProto<KeyProto>> pproto =
+      std::make_unique<PersistentProto<KeyProto>>(
+          GetPath(), WriteDelay(), ReadCallback(), WriteCallback());
+
+  EXPECT_EQ(pproto->get(), nullptr);
+
+  Wait();
+  EXPECT_EQ(read_status_, ReadStatus::kOk);
+  EXPECT_EQ(read_count_, 1);
+  EXPECT_EQ(write_count_, 0);
+
+  (*pproto)->Clear();
+  pproto->QueueWrite();
+
+  pproto.reset();
+
+  Wait();
+
+  int64_t size = 0;
+  std::string empty_proto;
+  KeyProto().SerializeToString(&empty_proto);
+
+  ASSERT_TRUE(base::GetFileSize(GetPath(), &size));
+  EXPECT_EQ(size, static_cast<int64_t>(empty_proto.size()));
+}
 }  // namespace metrics::structured
diff --git a/components/metrics/structured/structured_metrics_recorder.cc b/components/metrics/structured/structured_metrics_recorder.cc
index b9be9b5e..bdd0aaf 100644
--- a/components/metrics/structured/structured_metrics_recorder.cc
+++ b/components/metrics/structured/structured_metrics_recorder.cc
@@ -102,9 +102,15 @@
   }
 
   // Get the events from event storage.
-  event_storage_->MoveEvents(uma_proto);
+  auto events = event_storage_->TakeEvents();
 
-  const auto& structured_data = uma_proto.structured_data();
+  if (events.size() == 0) {
+    return;
+  }
+
+  StructuredDataProto& structured_data = *uma_proto.mutable_structured_data();
+  *structured_data.mutable_events() = std::move(events);
+
   LogUploadSizeBytes(structured_data.ByteSizeLong());
   LogNumEventsInUpload(structured_data.events_size());
 
@@ -248,7 +254,7 @@
   NotifyEventRecorded(event_proto);
 
   // Add new event to storage.
-  event_storage_->AddEvent(std::move(event_proto));
+  event_storage_->AddEvent(event_proto);
 
   test_callback_on_record_.Run();
 }
diff --git a/components/metrics/structured/structured_metrics_recorder_unittest.cc b/components/metrics/structured/structured_metrics_recorder_unittest.cc
index 43cb7d74..7228a836 100644
--- a/components/metrics/structured/structured_metrics_recorder_unittest.cc
+++ b/components/metrics/structured/structured_metrics_recorder_unittest.cc
@@ -20,6 +20,7 @@
 #include "components/metrics/structured/proto/event_storage.pb.h"
 #include "components/metrics/structured/recorder.h"
 #include "components/metrics/structured/structured_events.h"
+#include "components/metrics/structured/structured_metrics_client.h"
 #include "components/metrics/structured/structured_metrics_features.h"
 #include "components/metrics/structured/test/test_event_storage.h"
 #include "components/metrics/structured/test/test_key_data_provider.h"
@@ -460,9 +461,9 @@
   Init();
 
   const std::string test_string = "a raw string value";
-  events::v2::test_project_five::TestEventSix()
-      .SetTestMetricSix(test_string)
-      .Record();
+  StructuredMetricsClient::Record(
+      std::move(events::v2::test_project_five::TestEventSix().SetTestMetricSix(
+          test_string)));
 
   const auto data = GetEventMetrics();
   ASSERT_EQ(data.events_size(), 1);
@@ -811,7 +812,8 @@
   Init();
   OnRecordingDisabled();
 
-  events::v2::test_project_seven::TestEventEight().Record();
+  StructuredMetricsClient::Record(
+      std::move(events::v2::test_project_seven::TestEventEight()));
 
   OnRecordingEnabled();
   const auto data = GetEventMetrics();
@@ -881,9 +883,9 @@
   Init();
 
   // Processor that sets |is_device_enrolled| to true.
-  events::v2::test_project_six::TestEnum()
-      .SetTestEnumMetric(events::v2::test_project_six::Enum1::VARIANT2)
-      .Record();
+  StructuredMetricsClient::Record(
+      std::move(events::v2::test_project_six::TestEnum().SetTestEnumMetric(
+          events::v2::test_project_six::Enum1::VARIANT2)));
   const auto data = GetEventMetrics();
 
   EXPECT_EQ(data.events_size(), 1);
@@ -895,4 +897,30 @@
             (int64_t)events::v2::test_project_six::Enum1::VARIANT2);
 }
 
+TEST_F(StructuredMetricsRecorderTest, MultipleReports) {
+  Init();
+
+  StructuredMetricsClient::Record(std::move(
+      events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
+  StructuredMetricsClient::Record(std::move(
+      events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
+  StructuredMetricsClient::Record(std::move(
+      events::v2::test_project_two::TestEventThree().SetTestMetricFour(
+          "test-string")));
+
+  const auto data1 = GetEventMetrics();
+  EXPECT_EQ(data1.events_size(), 3);
+
+  StructuredMetricsClient::Record(std::move(
+      events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
+  StructuredMetricsClient::Record(std::move(
+      events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
+  StructuredMetricsClient::Record(std::move(
+      events::v2::test_project_two::TestEventThree().SetTestMetricFour(
+          "test-string")));
+
+  const auto data2 = GetEventMetrics();
+  EXPECT_EQ(data2.events_size(), 3);
+}
+
 }  // namespace metrics::structured
diff --git a/components/metrics/structured/test/test_event_storage.cc b/components/metrics/structured/test/test_event_storage.cc
index eedbdf0..c0fab64 100644
--- a/components/metrics/structured/test/test_event_storage.cc
+++ b/components/metrics/structured/test/test_event_storage.cc
@@ -10,20 +10,20 @@
 
 namespace metrics::structured {
 
+namespace {
+using ::google::protobuf::RepeatedPtrField;
+}
+
 TestEventStorage::TestEventStorage() = default;
 
 TestEventStorage::~TestEventStorage() = default;
 
-void TestEventStorage::AddEvent(StructuredEventProto&& event) {
-  *events()->add_non_uma_events() = event;
+void TestEventStorage::AddEvent(StructuredEventProto event) {
+  events()->mutable_non_uma_events()->Add(std::move(event));
 }
 
-void TestEventStorage::MoveEvents(ChromeUserMetricsExtension& uma_proto) {
-  StructuredDataProto* proto = uma_proto.mutable_structured_data();
-  proto->mutable_events()->Swap(events_.mutable_non_uma_events());
-
-  events_.clear_uma_events();
-  events_.clear_non_uma_events();
+RepeatedPtrField<StructuredEventProto> TestEventStorage::TakeEvents() {
+  return std::move(*events_.mutable_non_uma_events());
 }
 
 int TestEventStorage::RecordedEventsCount() const {
@@ -36,7 +36,7 @@
 }
 
 void TestEventStorage::AddBatchEvents(
-    const google::protobuf::RepeatedPtrField<StructuredEventProto>& events) {
+    const RepeatedPtrField<StructuredEventProto>& events) {
   events_.mutable_non_uma_events()->MergeFrom(events);
 }
 
diff --git a/components/metrics/structured/test/test_event_storage.h b/components/metrics/structured/test/test_event_storage.h
index 936b0a0..9c0ce3683 100644
--- a/components/metrics/structured/test/test_event_storage.h
+++ b/components/metrics/structured/test/test_event_storage.h
@@ -18,8 +18,9 @@
   ~TestEventStorage() override;
 
   // EventStorage:
-  void AddEvent(StructuredEventProto&& event) override;
-  void MoveEvents(ChromeUserMetricsExtension& uma_proto) override;
+  void AddEvent(StructuredEventProto event) override;
+  ::google::protobuf::RepeatedPtrField<StructuredEventProto> TakeEvents()
+      override;
   int RecordedEventsCount() const override;
   void Purge() override;
   void AddBatchEvents(
diff --git a/components/omnibox/browser/autocomplete_controller.cc b/components/omnibox/browser/autocomplete_controller.cc
index 81ae0a8..e62c4bf 100644
--- a/components/omnibox/browser/autocomplete_controller.cc
+++ b/components/omnibox/browser/autocomplete_controller.cc
@@ -281,15 +281,11 @@
       // aren't personalized by the server. That is, it indicates either
       // client-side most-likely URL suggestions or server-side suggestions
       // that depend only on the URL as context.
-      if (match.type == AutocompleteMatchType::TILE_NAVSUGGEST ||
-          match.type == AutocompleteMatchType::TILE_MOST_VISITED_SITE ||
-          match.type == AutocompleteMatchType::NAVSUGGEST) {
+      if (match.type == AutocompleteMatchType::NAVSUGGEST) {
         subtypes->emplace(omnibox::SUBTYPE_ZERO_PREFIX_LOCAL_FREQUENT_URLS);
         subtypes->emplace(omnibox::SUBTYPE_URL_BASED);
       } else if (match.type == AutocompleteMatchType::SEARCH_SUGGEST) {
         subtypes->emplace(omnibox::SUBTYPE_URL_BASED);
-      } else if (match.type == AutocompleteMatchType::TILE_REPEATABLE_QUERY) {
-        subtypes->emplace(omnibox::SUBTYPE_ZERO_PREFIX_LOCAL_FREQUENT_QUERIES);
       }
     } else if (match.provider->type() ==
                AutocompleteProvider::TYPE_QUERY_TILE) {
@@ -1432,6 +1428,9 @@
         subtypes.contains(
             omnibox::SUBTYPE_ZERO_PREFIX_LOCAL_FREQUENT_QUERIES) ||
         subtypes.contains(omnibox::SUBTYPE_ZERO_PREFIX) ||
+        subtypes.contains(omnibox::SUBTYPE_CLIPBOARD_IMAGE) ||
+        subtypes.contains(omnibox::SUBTYPE_CLIPBOARD_TEXT) ||
+        subtypes.contains(omnibox::SUBTYPE_CLIPBOARD_URL) ||
         subtypes.contains(omnibox::SUBTYPE_ZERO_PREFIX_QUERY_TILE)) {
       num_zero_prefix_suggestions_shown++;
     }
@@ -1990,6 +1989,13 @@
       "omnibox",
       "AutocompleteController::RunBatchUrlScoringModelMappedSearchBlending");
 
+  // Sort according to traditional scores.
+  // This is needed in order to ensure that the relevance score assignment logic
+  // can properly break ties when two (or more) URL suggestions have the same ML
+  // score.
+  internal_result_.Sort(input_, template_url_service_,
+                        old_result.default_match_to_preserve);
+
   // Run the model for the eligible matches.
   std::vector<const ScoringSignals*> batch_scoring_signals;
   std::vector<size_t> scored_positions;
@@ -2039,6 +2045,42 @@
     match.shortcut_boosted = match.relevance > grouping_threshold;
   }
 
+  // Following the initial relevance assignment, build a sorted list of
+  // values which will contain the finalized set of relevance scores for URL
+  // suggestions.
+  std::vector<int> scores_pool;
+  for (size_t i = 0; i < internal_result_.size(); ++i) {
+    const auto& match = internal_result_.matches_[i];
+    if (!match.IsUrlScoringEligible()) {
+      continue;
+    }
+    scores_pool.push_back(match.relevance);
+  }
+  base::ranges::sort(scores_pool, std::greater<>());
+
+  // Avoid duplicate scores by ensuring that no two URL suggestions are assigned
+  // the same score.
+  int max_score = INT_MAX;
+  for (auto& score : scores_pool) {
+    score = std::min(score, max_score - 1);
+    max_score = score;
+  }
+
+  std::vector<std::pair<float, size_t>> prediction_and_position_heap;
+  for (size_t i = 0; i < results.size(); ++i) {
+    prediction_and_position_heap.push_back({*results[i], scored_positions[i]});
+  }
+  base::ranges::stable_sort(prediction_and_position_heap, std::greater<>(),
+                            [](const auto& pair) { return pair.first; });
+
+  // Assign the finalized relevance scores to each URL suggestion in order of
+  // priority (i.e. ML score).
+  for (size_t i = 0; i < prediction_and_position_heap.size(); ++i) {
+    auto& match =
+        internal_result_.matches_[prediction_and_position_heap[i].second];
+    match.relevance = scores_pool[i];
+  }
+
   for (Observer& obs : observers_)
     obs.OnMlScored(this, internal_result_);
 }
diff --git a/components/omnibox/browser/autocomplete_controller_unittest.cc b/components/omnibox/browser/autocomplete_controller_unittest.cc
index e717774..4260eebd 100644
--- a/components/omnibox/browser/autocomplete_controller_unittest.cc
+++ b/components/omnibox/browser/autocomplete_controller_unittest.cc
@@ -1080,6 +1080,209 @@
       }));
 }
 
+TEST_F(AutocompleteControllerTest, MlRanking_MappedSearchBlending) {
+  OmniboxFieldTrial::ScopedMLConfigForTesting scoped_ml_config;
+  scoped_ml_config.GetMLConfig().ml_url_scoring = true;
+  scoped_ml_config.GetMLConfig().url_scoring_model = true;
+  scoped_ml_config.GetMLConfig().mapped_search_blending = true;
+
+  scoped_ml_config.GetMLConfig().mapped_search_blending_min = 600;
+  scoped_ml_config.GetMLConfig().mapped_search_blending_max = 2800;
+  scoped_ml_config.GetMLConfig().mapped_search_blending_grouping_threshold =
+      1400;
+
+  EXPECT_THAT(controller_.SimulateCleanAutocompletePass({}),
+              testing::ElementsAre());
+
+  // If ML ranks a URL 0, then the final relevance score should be set to the
+  // value of `mapped_search_blending_min` (since ML scores are mapped using the
+  // formula "final_score = min + ml_score * (max - min))".
+  EXPECT_THAT(controller_.SimulateCleanAutocompletePass({
+                  CreateHistoryUrlMlScoredMatch("history", true, 1400, 0),
+                  CreateSearchMatch("search", true, 1300),
+              }),
+              testing::ElementsAreArray({
+                  "search",
+                  "history",
+              }));
+
+  // Simple case of ranking with linear score mapping.
+  EXPECT_THAT(
+      controller_.SimulateCleanAutocompletePass({
+          // Final score: 1700 (== 600 + 0.5 * (2800 - 600))
+          CreateHistoryUrlMlScoredMatch("history 1350 .5", true, 1350, .5),
+          // Final score: 2580 (== 600 + 0.9 * (2800 - 600))
+          CreateHistoryUrlMlScoredMatch("history 1200 .9", true, 1200, .9),
+          // Final score: 820 (== 600 + 0.1 * (2800 - 600))
+          CreateHistoryUrlMlScoredMatch("history 1100 .1", false, 1100, .1),
+          // Final score: 1040 (== 600 + 0.2 * (2800 - 600))
+          CreateHistoryUrlMlScoredMatch("history 500 .2", true, 500, .2),
+      }),
+      testing::ElementsAreArray({
+          "history 1200 .9",
+          "history 1350 .5",
+          "history 500 .2",
+          "history 1100 .1",
+      }));
+
+  // Verify that URLs are grouped above searches if their final score is
+  // greater than `grouping_threshold` (i.e. "shortcut boosting").
+  EXPECT_THAT(
+      controller_.SimulateCleanAutocompletePass({
+          // Final score: 1700 (== 600 + 0.5 * (2800 - 600))
+          CreateHistoryUrlMlScoredMatch("history 1350 .5", true, 1350, .5),
+          CreateSearchMatch("search 1400", false, 1400),
+          CreateSearchMatch("search 800", true, 800),
+          CreateSearchMatch("search 600", false, 600),
+          // Final score: 2580 (== 600 + 0.9 * (2800 - 600))
+          CreateHistoryUrlMlScoredMatch("history 1200 .9", true, 1200, .9),
+          // Final score: 820 (== 600 + 0.1 * (2800 - 600))
+          CreateHistoryUrlMlScoredMatch("history 1100 .1", false, 1100, .1),
+          // Final score: 1040 (== 600 + 0.2 * (2800 - 600))
+          CreateHistoryUrlMlScoredMatch("history 500 .2", true, 500, .2),
+      }),
+      testing::ElementsAreArray({
+          "history 1200 .9",
+          "history 1350 .5",
+          "search 1400",
+          "search 800",
+          "search 600",
+          "history 500 .2",
+          "history 1100 .1",
+      }));
+
+  // When multiple URL suggestions have been assigned the same score by the ML
+  // model, those suggestions which were top-ranked according to legacy scoring
+  // should continue to be top-ranked once ML scoring has run.
+  EXPECT_THAT(
+      // Each of the below URL suggestions are assigned an initial relevance
+      // score of 1040 (== 600 + 0.2 * (2800 - 600)). After initial assignment,
+      // score adjustment logic is applied in order to generate the final
+      // relevance scores (which are guaranteed to be distinct).
+      controller_.SimulateCleanAutocompletePass({
+          // Final score: 1039
+          CreateHistoryUrlMlScoredMatch("history B 1200 .2", true, 1200, .2),
+          // Final score: 1036
+          CreateHistoryUrlMlScoredMatch("history E 200 .2", true, 200, .2),
+          // Final score: 1040
+          CreateHistoryUrlMlScoredMatch("history A 1350 .2", true, 1350, .2),
+          // Final score: 1037
+          CreateHistoryUrlMlScoredMatch("history D 300 .2", true, 300, .2),
+          // Final score: 1038
+          CreateHistoryUrlMlScoredMatch("history C 1100 .2", false, 1100, .2),
+          // Final score: 1035
+          CreateHistoryUrlMlScoredMatch("history F 100 .2", true, 100, .2),
+      }),
+      testing::ElementsAreArray({
+          "history A 1350 .2",
+          "history B 1200 .2",
+          "history C 1100 .2",
+          "history D 300 .2",
+          "history E 200 .2",
+          "history F 100 .2",
+      }));
+
+  // Can change the default suggestion from 1 history to another.
+  EXPECT_THAT(
+      controller_.SimulateCleanAutocompletePass({
+          // Final score: 1040 (== 600 + 0.2 * (2800 - 600))
+          CreateHistoryUrlMlScoredMatch("history 1400 .2", true, 1400, .2),
+          CreateSearchMatch("search", true, 1100),
+          // Final score: 1260 (== 600 + 0.3 * (2800 - 600))
+          CreateHistoryUrlMlScoredMatch("history 1200 .3", true, 1200, .3),
+      }),
+      testing::ElementsAreArray({
+          "history 1200 .3",
+          "search",
+          "history 1400 .2",
+      }));
+
+  // Can change the default from search to history (unlike StableSearchRanking
+  // variant).
+  EXPECT_THAT(
+      controller_.SimulateCleanAutocompletePass({
+          CreateSearchMatch("search 1200", true, 1200),
+          // Final score: 1040 (== 600 + 0.2 * (2800 - 600))
+          CreateHistoryUrlMlScoredMatch("history 1400 .2", false, 1400, .2),
+          // Final score: 1260 (== 600 + 0.3 * (2800 - 600))
+          CreateHistoryUrlMlScoredMatch("history 1100 .3", true, 1100, .3),
+      }),
+      testing::ElementsAreArray({
+          "history 1100 .3",
+          "search 1200",
+          "history 1400 .2",
+      }));
+
+  // Can change the default from history to search (unlike StableSearchRanking
+  // variant).
+  EXPECT_THAT(
+      controller_.SimulateCleanAutocompletePass({
+          // Final score: 1040 (== 600 + 0.2 * (2800 - 600))
+          CreateHistoryUrlMlScoredMatch("history 1400 .2", true, 1400, .2),
+          CreateSearchMatch("search 1300", true, 1300),
+          // Final score: 820 (== 600 + 0.1 * (2800 - 600))
+          CreateHistoryUrlMlScoredMatch("history 1200 .1", false, 1200, .1),
+      }),
+      testing::ElementsAreArray({
+          "search 1300",
+          "history 1400 .2",
+          "history 1200 .1",
+      }));
+
+  // When transferring matches, culls the lowest ML ranked matches, rather than
+  // the lowest traditional ranked matches.
+  controller_.internal_result_.Reset();
+  EXPECT_THAT(
+      controller_.SimulateAutocompletePass(
+          true, false,
+          {
+              CreateSearchMatch("search 1270", true, 1270),
+              CreateSearchMatch("search 1260", true, 1260),
+              CreateSearchMatch("search 1250", true, 1250),
+              CreateSearchMatch("search 1240", true, 1240),
+              CreateSearchMatch("search 1230", true, 1230),
+              CreateSearchMatch("search 1220", true, 1220),
+              CreateSearchMatch("search 1210", true, 1210),
+              CreateHistoryUrlMlScoredMatch("history 1100 .1", true, 1100, .1),
+              CreateHistoryUrlMlScoredMatch("history 1000 .2", true, 1000, .2),
+          }),
+      testing::ElementsAreArray({
+          "search 1270",
+          "search 1260",
+          "search 1250",
+          "search 1240",
+          "search 1230",
+          "search 1220",
+          "search 1210",
+          "history 1000 .2",
+      }));
+
+  // When not transferring matches, like above, culls the lowest ML ranked
+  // matches, rather than the lowest traditional ranked matches.
+  EXPECT_THAT(
+      controller_.SimulateCleanAutocompletePass({
+          CreateSearchMatch("search 1270", true, 1270),
+          CreateSearchMatch("search 1260", true, 1260),
+          CreateSearchMatch("search 1250", true, 1250),
+          CreateSearchMatch("search 1240", true, 1240),
+          CreateSearchMatch("search 1230", true, 1230),
+          CreateSearchMatch("search 1220", true, 1220),
+          CreateSearchMatch("search 1210", true, 1210),
+          CreateHistoryUrlMlScoredMatch("history 1100 .1", true, 1100, .1),
+          CreateHistoryUrlMlScoredMatch("history 1000 .2", true, 1000, .2),
+      }),
+      testing::ElementsAreArray({
+          "search 1270",
+          "search 1260",
+          "search 1250",
+          "search 1240",
+          "search 1230",
+          "search 1220",
+          "search 1210",
+          "history 1000 .2",
+      }));
+}
+
 TEST_F(AutocompleteControllerTest, UpdateResult_MLRanking_PreserveDefault) {
   OmniboxFieldTrial::ScopedMLConfigForTesting scoped_ml_config;
   scoped_ml_config.GetMLConfig().ml_url_scoring = true;
diff --git a/components/omnibox/browser/document_provider.cc b/components/omnibox/browser/document_provider.cc
index 8852635..0b606b85 100644
--- a/components/omnibox/browser/document_provider.cc
+++ b/components/omnibox/browser/document_provider.cc
@@ -835,6 +835,12 @@
 }
 
 void DocumentProvider::DemoteMatchesBeyondMax() {
+  // Allow all matches to retain their scores if unlimited matches param is
+  // enabled.
+  if (OmniboxFieldTrial::IsMlUrlScoringUnlimitedNumCandidatesEnabled()) {
+    return;
+  }
+
   for (size_t i = provider_max_matches_; i < matches_.size(); ++i)
     matches_[i].relevance = 0;
 }
diff --git a/components/omnibox/browser/document_provider_unittest.cc b/components/omnibox/browser/document_provider_unittest.cc
index a928128..a084a5e5 100644
--- a/components/omnibox/browser/document_provider_unittest.cc
+++ b/components/omnibox/browser/document_provider_unittest.cc
@@ -1047,6 +1047,60 @@
                            Summary{u"Document 1 longer title", 0, true}));
 }
 
+TEST_F(DocumentProviderTest, MaxMatches) {
+  InitClient();
+
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeaturesAndParameters(
+      /*enabled_features=*/{{omnibox::kUrlScoringModel, {}},
+                            {omnibox::kMlUrlScoring,
+                             {{"MlUrlScoringUnlimitedNumCandidates", "true"}}}},
+      /*disabled_feature=*/{});
+
+  OmniboxFieldTrial::ScopedMLConfigForTesting scoped_ml_config;
+
+  AutocompleteInput input(u"document", metrics::OmniboxEventProto::OTHER,
+                          TestSchemeClassifier());
+  input.set_omit_asynchronous_matches(true);
+
+  // Sync matches should have scores.
+  // Sync matches beyond |provider_max_matches_| should NOT have scores set to 0
+  // (since the "unlimited candidate matches" param is enabled).
+  provider_->input_ = input;
+  provider_->UpdateResults(MakeTestResponse({"0", "1", "2", "3", "4"}, 1000));
+  provider_->Start(input, false);
+  EXPECT_THAT(
+      ExtractMatchSummary(provider_->matches_),
+      testing::ElementsAre(Summary{u"Document 0 longer title", 1000, true},
+                           Summary{u"Document 1 longer title", 999, true},
+                           Summary{u"Document 2 longer title", 998, true},
+                           Summary{u"Document 3 longer title", 997, true}));
+
+  // Sync matches from the latest response should have scores.
+  // Sync matches from previous responses should not have scores.
+  provider_->UpdateResults(MakeTestResponse({"4", "5"}, 600));
+  provider_->Start(input, false);
+  EXPECT_THAT(
+      ExtractMatchSummary(provider_->matches_),
+      testing::ElementsAre(Summary{u"Document 4 longer title", 600, true},
+                           Summary{u"Document 5 longer title", 599, true},
+                           Summary{u"Document 0 longer title", 0, true},
+                           Summary{u"Document 1 longer title", 0, true}));
+
+  // Unlimited matches should ignore the provider max matches, even if the
+  // `kMlUrlScoringMaxMatchesByProvider` param is set.
+  scoped_ml_config.GetMLConfig().ml_url_scoring_max_matches_by_provider = "*:2";
+
+  provider_->UpdateResults(MakeTestResponse({"6", "7", "8", "9"}, 2000));
+  provider_->Start(input, false);
+  EXPECT_THAT(
+      ExtractMatchSummary(provider_->matches_),
+      testing::ElementsAre(Summary{u"Document 6 longer title", 2000, true},
+                           Summary{u"Document 7 longer title", 1999, true},
+                           Summary{u"Document 8 longer title", 1998, true},
+                           Summary{u"Document 9 longer title", 1997, true}));
+}
+
 TEST_F(DocumentProviderTest, StartCallsStop) {
   // Test that a call to ::Start will stop old requests to prevent their results
   // from appearing with the new input
diff --git a/components/omnibox/browser/most_visited_sites_provider.cc b/components/omnibox/browser/most_visited_sites_provider.cc
index e95f391..8d0c22f5 100644
--- a/components/omnibox/browser/most_visited_sites_provider.cc
+++ b/components/omnibox/browser/most_visited_sites_provider.cc
@@ -119,6 +119,8 @@
                      is_search ? AutocompleteMatchType::TILE_REPEATABLE_QUERY
                                : AutocompleteMatchType::TILE_MOST_VISITED_SITE);
       if (is_search) {
+        match.subtypes.emplace(
+            omnibox::SUBTYPE_ZERO_PREFIX_LOCAL_FREQUENT_QUERIES);
         match.keyword = dse->keyword();
         std::u16string query = tile.title;
 
@@ -136,6 +138,9 @@
             std::make_unique<TemplateURLRef::SearchTermsArgs>(query);
         num_search_tiles++;
       } else {
+        match.subtypes.emplace(
+            omnibox::SUBTYPE_ZERO_PREFIX_LOCAL_FREQUENT_URLS);
+        match.subtypes.emplace(omnibox::SUBTYPE_URL_BASED);
         num_url_tiles++;
       }
       matches.emplace_back(std::move(match));
@@ -175,6 +180,8 @@
       }
     }
 
+    match.subtypes.emplace(omnibox::SUBTYPE_ZERO_PREFIX_LOCAL_FREQUENT_URLS);
+    match.subtypes.emplace(omnibox::SUBTYPE_URL_BASED);
     matches.push_back(std::move(match));
   }
 
diff --git a/components/omnibox/browser/most_visited_sites_provider_unittest.cc b/components/omnibox/browser/most_visited_sites_provider_unittest.cc
index ac206f6..84334cf 100644
--- a/components/omnibox/browser/most_visited_sites_provider_unittest.cc
+++ b/components/omnibox/browser/most_visited_sites_provider_unittest.cc
@@ -22,6 +22,11 @@
 #include "ui/base/device_form_factor.h"
 
 namespace {
+struct TestData {
+  bool is_search;
+  history::MostVisitedURL entry;
+};
+
 class FakeTopSites : public history::TopSites {
  public:
   FakeTopSites() = default;
@@ -57,22 +62,26 @@
   // set per call.
   // Returns true if there was a recipient to receive the URLs and the list was
   // emitted, otherwise returns false.
-  bool EmitURLs() {
+  bool EmitURLs(const std::vector<TestData>& data) {
     if (callbacks_.empty())
       return false;
-    std::move(callbacks_.front()).Run(std::move(urls_));
+
+    history::MostVisitedURLList urls;
+    for (const auto& test_element : data) {
+      urls.push_back(test_element.entry);
+    }
+
+    std::move(callbacks_.front()).Run(std::move(urls));
     callbacks_.pop_front();
     return true;
   }
 
-  history::MostVisitedURLList& urls() { return urls_; }
   const std::set<std::string>& blocked_urls() const { return blocked_urls_; }
 
  protected:
   // A test-specific field for controlling when most visited callback is run
   // after top sites have been requested.
   std::list<GetMostVisitedURLsCallback> callbacks_;
-  history::MostVisitedURLList urls_;
   std::set<std::string> blocked_urls_;
 
   ~FakeTopSites() override = default;
@@ -86,6 +95,15 @@
   kAggregateMatch,
   kIndividualTiles
 };
+
+const std::vector<TestData> DefaultTestData() {
+  return {{false, {GURL("http://www.a.art/"), u"A art"}},
+          {false, {GURL("http://www.b.biz/"), u"B biz"}},
+          {false, {GURL("http://www.c.com/"), u"C com"}},
+          {false, {GURL("http://www.d.de/"), u"D de"}},
+          {true, {GURL("http://www.google.com/search?q=abc"), u"abc"}}};
+}
+
 }  // namespace
 
 class MostVisitedSitesProviderTest : public testing::Test,
@@ -119,7 +137,7 @@
 
   // Iterate over all matches offered by the Provider and verify these against
   // the supplied list of History URLs.
-  void CheckMatchesEquivalentTo(const history::MostVisitedURLList& urls,
+  void CheckMatchesEquivalentTo(const std::vector<TestData>& data,
                                 ExpectedUiType ui_type);
 
   // Returns total number of all NAVSUGGEST and TILE_NAVSUGGEST elements.
@@ -172,7 +190,7 @@
 }
 
 void MostVisitedSitesProviderTest::CheckMatchesEquivalentTo(
-    const history::MostVisitedURLList& urls,
+    const std::vector<TestData>& data,
     ExpectedUiType ui_type) {
   // Compare the AutocompleteResult against a set of URLs that we expect to see.
   // Note that additional matches may be offered if other providers are also
@@ -188,25 +206,38 @@
     for (const auto& match : result) {
       if (match.type != AutocompleteMatchType::TILE_NAVSUGGEST)
         continue;
+      EXPECT_TRUE(match.subtypes.contains(
+          omnibox::SUBTYPE_ZERO_PREFIX_LOCAL_FREQUENT_URLS));
+      EXPECT_TRUE(match.subtypes.contains(omnibox::SUBTYPE_URL_BASED));
       const auto& tiles = match.suggest_tiles;
-      ASSERT_EQ(urls.size(), tiles.size()) << "Wrong number of tiles reported";
-      for (size_t index = 0u; index < urls.size(); index++) {
-        EXPECT_EQ(urls[index].url, tiles[index].url)
+      ASSERT_EQ(data.size(), tiles.size()) << "Wrong number of tiles reported";
+      for (size_t index = 0u; index < data.size(); index++) {
+        EXPECT_EQ(data[index].entry.url, tiles[index].url)
             << "Invalid Tile URL at position " << index;
-        EXPECT_EQ(urls[index].title, tiles[index].title)
+        EXPECT_EQ(data[index].entry.title, tiles[index].title)
             << "Invalid Tile Title at position " << index;
       }
       break;
     }
   } else if (ui_type == ExpectedUiType::kIndividualTiles) {
-    ASSERT_EQ(urls.size(), NumMostVisitedMatches())
+    ASSERT_EQ(data.size(), NumMostVisitedMatches())
         << "Unexpected number of TILE matches";
     int expected_relevance = 1600;  // kMostVisitedTilesIndividualHighRelevance
     for (const auto& match : result) {
-      EXPECT_EQ(match.type, AutocompleteMatchType::TILE_MOST_VISITED_SITE);
-      EXPECT_EQ(urls[match_index].url, match.destination_url)
+      if (data[match_index].is_search) {
+        EXPECT_EQ(match.type, AutocompleteMatchType::TILE_REPEATABLE_QUERY);
+        EXPECT_TRUE(match.subtypes.contains(
+            omnibox::SUBTYPE_ZERO_PREFIX_LOCAL_FREQUENT_QUERIES));
+      } else {
+        EXPECT_EQ(match.type, AutocompleteMatchType::TILE_MOST_VISITED_SITE);
+        EXPECT_TRUE(match.subtypes.contains(
+            omnibox::SUBTYPE_ZERO_PREFIX_LOCAL_FREQUENT_URLS));
+        EXPECT_TRUE(match.subtypes.contains(omnibox::SUBTYPE_URL_BASED));
+      }
+
+      EXPECT_EQ(data[match_index].entry.url, match.destination_url)
           << "Invalid Match URL at position " << match_index;
-      EXPECT_EQ(urls[match_index].title, match.description)
+      EXPECT_EQ(data[match_index].entry.title, match.description)
           << "Invalid Match Title at position " << match_index;
       EXPECT_EQ(expected_relevance, match.relevance)
           << "Invalid Match Relevance at position " << match_index;
@@ -231,17 +262,6 @@
 
   // For tests requiring direct interaction with the Provider.
   provider_ = new MostVisitedSitesProvider(&client_, this);
-
-  // Inject a few URLs to test MostVisitedSitesProvider behavior.
-  std::array<history::MostVisitedURL, 5> test_data{{
-      {GURL("http://www.a.art/"), u"A art"},
-      {GURL("http://www.b.biz/"), u"B biz"},
-      {GURL("http://www.c.com/"), u"C com"},
-      {GURL("http://www.d.de/"), u"D de"},
-      {GURL("http://www.e.edu/"), u"E edu"},
-  }};
-
-  top_sites_->urls().assign(test_data.begin(), test_data.end());
 }
 
 void MostVisitedSitesProviderTest::OnProviderUpdate(
@@ -257,8 +277,9 @@
   auto input = BuildAutocompleteInputForWebOnFocus();
   provider_->Start(input, true);
   EXPECT_EQ(0u, NumMostVisitedMatches());
-  EXPECT_TRUE(top_sites_->EmitURLs());
-  CheckMatchesEquivalentTo(top_sites_->urls(), ExpectedUiType::kAggregateMatch);
+  auto test_data = DefaultTestData();
+  EXPECT_TRUE(top_sites_->EmitURLs(test_data));
+  CheckMatchesEquivalentTo(test_data, ExpectedUiType::kAggregateMatch);
   EXPECT_EQ(1, provider_update_count_);
   provider_->Stop(false, false);
 
@@ -270,15 +291,13 @@
   EXPECT_EQ(0ul, NumMostVisitedMatches());
   EXPECT_EQ(1, provider_update_count_);
 
-  history::MostVisitedURLList old_urls = top_sites_->urls();
-
   // Most visited results arriving after Stop() has been called, ensure they
   // are not displayed.
-  std::array<history::MostVisitedURL, 1> new_urls{{
+  std::vector<TestData> new_urls{{
+      false,
       {GURL("http://www.g.gov/"), u"G gov"},
   }};
-  top_sites_->urls().assign(new_urls.begin(), new_urls.end());
-  EXPECT_TRUE(top_sites_->EmitURLs());
+  EXPECT_TRUE(top_sites_->EmitURLs(new_urls));
   EXPECT_EQ(0ul, NumMostVisitedMatches());
   EXPECT_EQ(1, provider_update_count_);
 
@@ -288,13 +307,13 @@
 
   // Stale results (reported for the first of the two Start() requests) should
   // be rejected.
-  EXPECT_TRUE(top_sites_->EmitURLs());
+  EXPECT_TRUE(top_sites_->EmitURLs(DefaultTestData()));
   EXPECT_EQ(0ul, NumMostVisitedMatches());
   EXPECT_EQ(1, provider_update_count_);
 
   // Results for the second Start() action should be recorded.
-  EXPECT_TRUE(top_sites_->EmitURLs());
-  CheckMatchesEquivalentTo(top_sites_->urls(), ExpectedUiType::kAggregateMatch);
+  EXPECT_TRUE(top_sites_->EmitURLs(test_data));
+  CheckMatchesEquivalentTo(test_data, ExpectedUiType::kAggregateMatch);
   EXPECT_EQ(2, provider_update_count_);
   provider_->Stop(false, false);
 }
@@ -313,7 +332,7 @@
   EXPECT_EQ(0u, NumMostVisitedMatches());
 
   // Most visited results arriving after a new request has been started.
-  EXPECT_TRUE(top_sites_->EmitURLs());
+  EXPECT_TRUE(top_sites_->EmitURLs(DefaultTestData()));
   EXPECT_EQ(0u, NumMostVisitedMatches());
 }
 
@@ -359,17 +378,17 @@
   provider_->Start(BuildAutocompleteInputForWebOnFocus(), true);
   EXPECT_EQ(0u, NumMostVisitedMatches());
   // Accept only direct TopSites data.
-  EXPECT_TRUE(top_sites_->EmitURLs());
-  CheckMatchesEquivalentTo(top_sites_->urls(), ExpectedUiType::kAggregateMatch);
+  auto test_data = DefaultTestData();
+  EXPECT_TRUE(top_sites_->EmitURLs(test_data));
+  CheckMatchesEquivalentTo(test_data, ExpectedUiType::kAggregateMatch);
 }
 
 TEST_F(MostVisitedSitesProviderTest, NoMatchesWhenNoMostVisitedSites) {
   // Start with no URLs.
-  top_sites_->urls().clear();
   provider_->Start(BuildAutocompleteInputForWebOnFocus(), true);
   EXPECT_EQ(0u, NumMostVisitedMatches());
   // Accept only direct TopSites data, confirm no matches are built.
-  EXPECT_TRUE(top_sites_->EmitURLs());
+  EXPECT_TRUE(top_sites_->EmitURLs({}));
   EXPECT_EQ(0u, NumMostVisitedMatches());
 }
 
@@ -384,7 +403,7 @@
   EXPECT_TRUE(provider_->done());
   EXPECT_EQ(0u, NumMostVisitedMatches());
   // No callbacks should have been added due to early return.
-  EXPECT_FALSE(top_sites_->EmitURLs());
+  EXPECT_FALSE(top_sites_->EmitURLs(DefaultTestData()));
   EXPECT_EQ(0u, NumMostVisitedMatches());
 }
 
@@ -393,18 +412,18 @@
   features.InitAndDisableFeature(
       omnibox::kMostVisitedTilesHorizontalRenderGroup);
   // Make a copy (intentional - we'll modify this later)
-  auto urls = top_sites_->urls();
   provider_->Start(BuildAutocompleteInputForWebOnFocus(), true);
   // Accept only direct TopSites data.
-  EXPECT_TRUE(top_sites_->EmitURLs());
-  CheckMatchesEquivalentTo(urls, ExpectedUiType::kAggregateMatch);
+  auto test_data = DefaultTestData();
+  EXPECT_TRUE(top_sites_->EmitURLs(test_data));
+  CheckMatchesEquivalentTo(test_data, ExpectedUiType::kAggregateMatch);
 
   // Commence delete.
   histogram_.ExpectTotalCount("Omnibox.SuggestTiles.TileTypeCount.Search", 1);
-  histogram_.ExpectBucketCount("Omnibox.SuggestTiles.TileTypeCount.Search", 0,
+  histogram_.ExpectBucketCount("Omnibox.SuggestTiles.TileTypeCount.Search", 1,
                                1);
   histogram_.ExpectTotalCount("Omnibox.SuggestTiles.TileTypeCount.URL", 1);
-  histogram_.ExpectBucketCount("Omnibox.SuggestTiles.TileTypeCount.URL", 5, 1);
+  histogram_.ExpectBucketCount("Omnibox.SuggestTiles.TileTypeCount.URL", 4, 1);
   histogram_.ExpectTotalCount("Omnibox.SuggestTiles.DeletedTileIndex", 0);
   auto* match = GetMatch(AutocompleteMatchType::TILE_NAVSUGGEST, 0);
   ASSERT_NE(nullptr, match) << "No TILE_NAVSUGGEST Match found";
@@ -414,9 +433,9 @@
   // Note: TileTypeCounts are not emitted after deletion.
 
   // Observe that the URL is now blocked and removed from suggestion.
-  auto deleted_url = urls[1].url;
-  urls.erase(urls.begin() + 1);
-  CheckMatchesEquivalentTo(urls, ExpectedUiType::kAggregateMatch);
+  auto deleted_url = test_data[1].entry.url;
+  test_data.erase(test_data.begin() + 1);
+  CheckMatchesEquivalentTo(test_data, ExpectedUiType::kAggregateMatch);
   EXPECT_TRUE(top_sites_->IsBlocked(deleted_url));
 }
 
@@ -426,12 +445,12 @@
       omnibox::kMostVisitedTilesHorizontalRenderGroup);
 
   // Start with just one URL.
-  auto& urls = top_sites_->urls();
-  urls.clear();
-  urls.emplace_back(GURL("http://www.a.art/"), u"A art");
+  std::vector<TestData> urls{{
+      {false, {GURL("http://www.a.art/"), u"A art"}},
+  }};
 
   provider_->Start(BuildAutocompleteInputForWebOnFocus(), true);
-  EXPECT_TRUE(top_sites_->EmitURLs());
+  EXPECT_TRUE(top_sites_->EmitURLs(urls));
   CheckMatchesEquivalentTo(urls, ExpectedUiType::kAggregateMatch);
 
   // Commence delete of the only item that we have.
@@ -461,8 +480,7 @@
   provider_->Start(BuildAutocompleteInputForWebOnFocus(), true);
   EXPECT_EQ(0u, NumMostVisitedMatches());
   // Accept only direct TopSites data.
-  EXPECT_TRUE(top_sites_->EmitURLs());
-  EXPECT_EQ(5u, top_sites_->urls().size());
-  CheckMatchesEquivalentTo(top_sites_->urls(),
-                           ExpectedUiType::kIndividualTiles);
+  auto test_data = DefaultTestData();
+  EXPECT_TRUE(top_sites_->EmitURLs(test_data));
+  CheckMatchesEquivalentTo(test_data, ExpectedUiType::kIndividualTiles);
 }
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index 5136df70..9661f23 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -654,7 +654,7 @@
   // Enables approach (2) above.
   bool stable_search_blending{false};
 
-  // Enables approach (3) above. No affect if `stable_search_blending` is true.
+  // Enables approach (3) above. No effect if `stable_search_blending` is true.
   // Map ML scores [0, 1] to [`min`, `max`]. Groups URLs above searches if their
   // mapped relevance is greater than `grouping_threshold`
   bool mapped_search_blending{false};
diff --git a/components/optimization_guide/core/model_execution/settings_enabled_observer.h b/components/optimization_guide/core/model_execution/settings_enabled_observer.h
index 3e344a41..2728cb0 100644
--- a/components/optimization_guide/core/model_execution/settings_enabled_observer.h
+++ b/components/optimization_guide/core/model_execution/settings_enabled_observer.h
@@ -22,7 +22,7 @@
   // team should call `ShouldFeatureBeCurrentlyEnabledForUser` before displaying
   // any feature functionality. TODO(rajendrant): Remove this once all the
   // consumers stop using it.
-  virtual void PrepareToEnableOnRestart() = 0;
+  virtual void PrepareToEnableOnRestart() {}
 
   // Notifies the consumers whenever the feature enabled state is changed.
   // `is_now_enabled` indicates the current enabled state of the feature. This
diff --git a/components/optimization_guide/core/prediction_manager.cc b/components/optimization_guide/core/prediction_manager.cc
index 068280d6f..e4935cd0 100644
--- a/components/optimization_guide/core/prediction_manager.cc
+++ b/components/optimization_guide/core/prediction_manager.cc
@@ -149,7 +149,11 @@
          model_metadata.type_url() ==
              "type.googleapis.com/"
              "google.internal.chrome.optimizationguide.v1."
-             "OnDeviceBaseModelMetadata";
+             "OnDeviceBaseModelMetadata" ||
+         model_metadata.type_url() ==
+             "type.googleapis.com/"
+             "google.internal.chrome.optimizationguide.v1."
+             "HistoryClustersModuleRankingModelMetadata";
 }
 
 void RecordModelAvailableAtRegistration(
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal
index 0f3da70..b5dce53 160000
--- a/components/optimization_guide/internal
+++ b/components/optimization_guide/internal
@@ -1 +1 @@
-Subproject commit 0f3da705ae7b53e643f8454750743a98b3f6c619
+Subproject commit b5dce536acaa9d9fedc1ea98b42586100f0e11c7
diff --git a/components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.cc b/components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.cc
index c27489c..dd9bd22 100644
--- a/components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.cc
+++ b/components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.cc
@@ -71,12 +71,11 @@
       MergeTimingsBySizeAndTime(new_candidate, *inout_timing);
   // Image discovery time, load start/end are not reported for subframe image
   // LCP elements.
-  inout_timing->Reset(merged_candidate.Time(), merged_candidate.Size(),
-                      merged_candidate.Type(), merged_candidate.ImageBPP(),
-                      merged_candidate.ImageRequestPriority(),
-                      /*image_discovery_time=*/std::nullopt,
-                      /*image_load_start=*/std::nullopt,
-                      /*image_load_end=*/std::nullopt);
+  inout_timing->Reset(
+      merged_candidate.Time(), merged_candidate.Size(), merged_candidate.Type(),
+      merged_candidate.ImageBPP(), merged_candidate.ImageRequestPriority(),
+      merged_candidate.ImageDiscoveryTime(), merged_candidate.ImageLoadStart(),
+      merged_candidate.ImageLoadEnd());
 }
 
 void Reset(ContentfulPaintTimingInfo& timing) {
@@ -123,14 +122,27 @@
     double image_bpp,
     const std::optional<net::RequestPriority>& image_request_priority,
     bool in_main_frame,
-    blink::LargestContentfulPaintType type)
+    const blink::LargestContentfulPaintType type,
+    const absl::optional<base::TimeDelta>& image_discovery_time,
+    const absl::optional<base::TimeDelta>& image_load_start,
+    const absl::optional<base::TimeDelta>& image_load_end)
     : time_(time),
       size_(size),
       text_or_image_(text_or_image),
       type_(type),
       image_bpp_(image_bpp),
       image_request_priority_(image_request_priority),
-      in_main_frame_(in_main_frame) {}
+      in_main_frame_(in_main_frame) {
+  if (image_discovery_time.has_value()) {
+    image_discovery_time_ = image_discovery_time.value();
+  }
+  if (image_load_start.has_value()) {
+    image_load_start_ = image_load_start.value();
+  }
+  if (image_load_end.has_value()) {
+    image_load_end_ = image_load_end.value();
+  }
+}
 
 ContentfulPaintTimingInfo::ContentfulPaintTimingInfo(
     const ContentfulPaintTimingInfo& other) = default;
@@ -345,13 +357,15 @@
     // We received timing information for an untracked load. Ignore it.
     return;
   }
-  RecordSubFrameTimingInternal(largest_contentful_paint,
-                               first_input_or_scroll_notified_timestamp,
-                               it->second);
+  UpdateSubFrameTiming(largest_contentful_paint, subframe_contentful_paint_,
+                       first_input_or_scroll_notified_timestamp, it->second,
+                       false);
   // Note that subframe can be in other page like FencedFrames.
   // So, we can't know `main_frame_url` without help of PageLoadTracker.
   if (!IsSameSite(subframe_rfh->GetLastCommittedURL(), main_frame_url)) {
-    RecordCrossSiteSubframeTiming(largest_contentful_paint, it->second);
+    UpdateSubFrameTiming(
+        largest_contentful_paint, cross_site_subframe_contentful_paint_,
+        first_input_or_scroll_notified_timestamp, it->second, true);
   }
 }
 
@@ -363,16 +377,20 @@
 // should have been able when a large ephemeral element is removed). This is a
 // trade-off we make to keep a simple algorithm, otherwise we will have to
 // track one candidate per subframe.
-void LargestContentfulPaintHandler::RecordSubFrameTimingInternal(
+void LargestContentfulPaintHandler::UpdateSubFrameTiming(
     const page_load_metrics::mojom::LargestContentfulPaintTiming&
         largest_contentful_paint,
-    const std::optional<base::TimeDelta>&
+    ContentfulPaint& subframe_contentful_paint,
+    const absl::optional<base::TimeDelta>&
         first_input_or_scroll_notified_timestamp,
-    const base::TimeDelta& navigation_start_offset) {
-  UpdateFirstInputOrScrollNotified(first_input_or_scroll_notified_timestamp,
-                                   navigation_start_offset);
-  DCHECK(!subframe_contentful_paint_.Text().InMainFrame());
-  DCHECK(!subframe_contentful_paint_.Image().InMainFrame());
+    const base::TimeDelta& navigation_start_offset,
+    const bool is_cross_site) {
+  if (!is_cross_site) {
+    UpdateFirstInputOrScrollNotified(first_input_or_scroll_notified_timestamp,
+                                     navigation_start_offset);
+  }
+  DCHECK(!subframe_contentful_paint.Text().InMainFrame());
+  DCHECK(!subframe_contentful_paint.Image().InMainFrame());
   ContentfulPaintTimingInfo new_text_candidate(
       AdjustedTime(largest_contentful_paint.largest_text_paint,
                    navigation_start_offset),
@@ -381,50 +399,14 @@
       /*image_bpp=*/0.0, /*image_request_priority=*/std::nullopt,
       /*in_main_frame=*/false,
       static_cast<blink::LargestContentfulPaintType>(
-          largest_contentful_paint.type));
+          largest_contentful_paint.type),
+      /*image_discovery_time=*/std::nullopt, /*image_load_start=*/std::nullopt,
+      /*image_load_end=*/std::nullopt);
   if (IsValid(new_text_candidate.Time())) {
-    MergeForSubframesWithAdjustedTime(&subframe_contentful_paint_.Text(),
+    MergeForSubframesWithAdjustedTime(&subframe_contentful_paint.Text(),
                                       new_text_candidate);
   }
-  // TODO(iclelland): Use the remainder of the fields from
-  // largest_contentful_paint to construct the ContentfulPaintTimingInfo here
-  ContentfulPaintTimingInfo new_image_candidate(
-      AdjustedTime(largest_contentful_paint.largest_image_paint,
-                   navigation_start_offset),
-      largest_contentful_paint.largest_image_paint_size,
-      ContentfulPaintTimingInfo::LargestContentTextOrImage::kImage,
-      largest_contentful_paint.image_bpp,
-      GetImageRequestPriority(largest_contentful_paint),
-      /*in_main_frame=*/false,
-      static_cast<blink::LargestContentfulPaintType>(
-          largest_contentful_paint.type));
-  if (IsValid(new_image_candidate.Time())) {
-    MergeForSubframesWithAdjustedTime(&subframe_contentful_paint_.Image(),
-                                      new_image_candidate);
-  }
-}
 
-void LargestContentfulPaintHandler::RecordCrossSiteSubframeTiming(
-    const page_load_metrics::mojom::LargestContentfulPaintTiming&
-        largest_contentful_paint,
-    const base::TimeDelta& navigation_start_offset) {
-  DCHECK(!cross_site_subframe_contentful_paint_.Text().InMainFrame());
-  DCHECK(!cross_site_subframe_contentful_paint_.Image().InMainFrame());
-  ContentfulPaintTimingInfo new_text_candidate(
-      AdjustedTime(largest_contentful_paint.largest_text_paint,
-                   navigation_start_offset),
-      largest_contentful_paint.largest_text_paint_size,
-      ContentfulPaintTimingInfo::LargestContentTextOrImage::kText,
-      /*image_bpp=*/0.0, /*image_request_priority=*/std::nullopt,
-      /*in_main_frame=*/false,
-      static_cast<blink::LargestContentfulPaintType>(
-          largest_contentful_paint.type));
-  if (IsValid(new_text_candidate.Time())) {
-    MergeForSubframesWithAdjustedTime(
-        &cross_site_subframe_contentful_paint_.Text(), new_text_candidate);
-  }
-  // TODO(iclelland): Use the remainder of the fields from
-  // largest_contentful_paint to construct the ContentfulPaintTimingInfo here
   ContentfulPaintTimingInfo new_image_candidate(
       AdjustedTime(largest_contentful_paint.largest_image_paint,
                    navigation_start_offset),
@@ -434,10 +416,17 @@
       GetImageRequestPriority(largest_contentful_paint),
       /*in_main_frame=*/false,
       static_cast<blink::LargestContentfulPaintType>(
-          largest_contentful_paint.type));
+          largest_contentful_paint.type),
+      AdjustedTime(largest_contentful_paint.largest_image_discovery_time,
+                   navigation_start_offset),
+      AdjustedTime(largest_contentful_paint.largest_image_load_start,
+                   navigation_start_offset),
+      AdjustedTime(largest_contentful_paint.largest_image_load_end,
+                   navigation_start_offset));
+
   if (IsValid(new_image_candidate.Time())) {
-    MergeForSubframesWithAdjustedTime(
-        &cross_site_subframe_contentful_paint_.Image(), new_image_candidate);
+    MergeForSubframesWithAdjustedTime(&subframe_contentful_paint.Image(),
+                                      new_image_candidate);
   }
 }
 
diff --git a/components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.h b/components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.h
index e1e9cfd9..2d9b32c 100644
--- a/components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.h
+++ b/components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.h
@@ -43,7 +43,10 @@
       double image_bpp,
       const std::optional<net::RequestPriority>& image_request_priority,
       bool in_main_frame,
-      blink::LargestContentfulPaintType type);
+      const blink::LargestContentfulPaintType type,
+      const absl::optional<base::TimeDelta>& image_discovery_time,
+      const absl::optional<base::TimeDelta>& image_load_start,
+      const absl::optional<base::TimeDelta>& image_load_end);
   ContentfulPaintTimingInfo(const ContentfulPaintTimingInfo& other);
   void Reset(const std::optional<base::TimeDelta>& time,
              const uint64_t& size,
@@ -63,6 +66,7 @@
   std::optional<base::TimeDelta> ImageLoadEnd() const {
     return image_load_end_;
   }
+
   bool InMainFrame() const { return in_main_frame_; }
   blink::LargestContentfulPaintType Type() const { return type_; }
   uint64_t Size() const { return size_; }
@@ -199,18 +203,14 @@
   }
 
  private:
-  void RecordSubFrameTimingInternal(
+  void UpdateSubFrameTiming(
       const page_load_metrics::mojom::LargestContentfulPaintTiming&
           largest_contentful_paint,
-      const std::optional<base::TimeDelta>&
+      ContentfulPaint& subframe_contentful_paint,
+      const absl::optional<base::TimeDelta>&
           first_input_or_scroll_notified_timestamp,
-      const base::TimeDelta& navigation_start_offset);
-  // RecordCrossSiteSubframeTiming updates
-  // `cross_site_subframe_contentful_paint_` with a new lcp candidate.
-  void RecordCrossSiteSubframeTiming(
-      const page_load_metrics::mojom::LargestContentfulPaintTiming&
-          largest_contentful_paint,
-      const base::TimeDelta& navigation_start_offset);
+      const base::TimeDelta& navigation_start_offset,
+      const bool is_cross_site);
   void UpdateFirstInputOrScrollNotified(
       const std::optional<base::TimeDelta>& candidate_new_time,
       const base::TimeDelta& navigation_start_offset);
diff --git a/components/performance_manager/BUILD.gn b/components/performance_manager/BUILD.gn
index df1e846..8771e11 100644
--- a/components/performance_manager/BUILD.gn
+++ b/components/performance_manager/BUILD.gn
@@ -202,6 +202,7 @@
     "resource_attribution/memory_measurement_provider.cc",
     "resource_attribution/memory_measurement_provider.h",
     "resource_attribution/page_context.cc",
+    "resource_attribution/performance_manager_aliases.h",
     "resource_attribution/process_context.cc",
     "resource_attribution/queries.cc",
     "resource_attribution/query_params.cc",
diff --git a/components/performance_manager/DEPS b/components/performance_manager/DEPS
index 12b4f1e..83b4acb 100644
--- a/components/performance_manager/DEPS
+++ b/components/performance_manager/DEPS
@@ -27,4 +27,8 @@
   "+third_party/blink/public/mojom/tokens",
   "+third_party/leveldatabase",
   "+ui/gfx/geometry",
+
+  # Aliases used in the resource_attribution implementation should never be
+  # included elsewhere, to avoid cluttering the public namespace.
+  "-components/performance_manager/resource_attribution/performance_manager_aliases.h",
 ]
diff --git a/components/performance_manager/public/resource_attribution/attribution_helpers.h b/components/performance_manager/public/resource_attribution/attribution_helpers.h
index b196aef5..216b615 100644
--- a/components/performance_manager/public/resource_attribution/attribution_helpers.h
+++ b/components/performance_manager/public/resource_attribution/attribution_helpers.h
@@ -16,15 +16,16 @@
 #include "third_party/abseil-cpp/absl/types/variant.h"
 
 namespace performance_manager {
-
 class FrameNode;
 class WorkerNode;
+}  // namespace performance_manager
 
 namespace resource_attribution {
 
 // A set of nodes to include or exclude from a resource split.
 using NodeSplitSet =
-    std::set<absl::variant<const FrameNode*, const WorkerNode*>>;
+    std::set<absl::variant<const performance_manager::FrameNode*,
+                           const performance_manager::WorkerNode*>>;
 
 // Splits a resource of type T between all frames and workers hosted in
 // `process_node`. `frame_setter` or `worker_setter` will be called for each
@@ -34,25 +35,35 @@
 // in `process_node`, and any node in `nodes_to_skip` will be excluded from the
 // split.
 template <typename T,
+          // Template parameters are used to alias FrameNode and WorkerNode
+          // without introducing them into the resource_attribution namespace.
+          typename FrameNode = performance_manager::FrameNode,
+          typename WorkerNode = performance_manager::WorkerNode,
           typename FrameSetter = base::FunctionRef<void(const FrameNode*, T)>,
           typename WorkerSetter = base::FunctionRef<void(const WorkerNode*, T)>>
-void SplitResourceAmongFramesAndWorkers(T resource_value,
-                                        const ProcessNode* process_node,
-                                        const NodeSplitSet& extra_nodes,
-                                        const NodeSplitSet& nodes_to_skip,
-                                        FrameSetter frame_setter,
-                                        WorkerSetter worker_setter);
+void SplitResourceAmongFramesAndWorkers(
+    T resource_value,
+    const performance_manager::ProcessNode* process_node,
+    const NodeSplitSet& extra_nodes,
+    const NodeSplitSet& nodes_to_skip,
+    FrameSetter frame_setter,
+    WorkerSetter worker_setter);
 
 // Splits a resource of type T between all frames and workers hosted in
 // `process_node`. `frame_setter` or `worker_setter` will be called for each
 // node with that node's fraction of `resource_value`.
 template <typename T,
+          // Template parameters are used to alias FrameNode and WorkerNode
+          // without introducing them into the resource_attribution namespace.
+          typename FrameNode = performance_manager::FrameNode,
+          typename WorkerNode = performance_manager::WorkerNode,
           typename FrameSetter = base::FunctionRef<void(const FrameNode*, T)>,
           typename WorkerSetter = base::FunctionRef<void(const WorkerNode*, T)>>
-void SplitResourceAmongFramesAndWorkers(T resource_value,
-                                        const ProcessNode* process_node,
-                                        FrameSetter frame_setter,
-                                        WorkerSetter worker_setter) {
+void SplitResourceAmongFramesAndWorkers(
+    T resource_value,
+    const performance_manager::ProcessNode* process_node,
+    FrameSetter frame_setter,
+    WorkerSetter worker_setter) {
   SplitResourceAmongFramesAndWorkers(resource_value, process_node,
                                      /*extra_nodes=*/{},
                                      /*nodes_to_skip=*/{}, frame_setter,
@@ -61,13 +72,18 @@
 
 // Implementation
 
-template <typename T, typename FrameSetter, typename WorkerSetter>
-void SplitResourceAmongFramesAndWorkers(T resource_value,
-                                        const ProcessNode* process_node,
-                                        const NodeSplitSet& extra_nodes,
-                                        const NodeSplitSet& nodes_to_skip,
-                                        FrameSetter frame_setter,
-                                        WorkerSetter worker_setter) {
+template <typename T,
+          typename FrameNode,
+          typename WorkerNode,
+          typename FrameSetter,
+          typename WorkerSetter>
+void SplitResourceAmongFramesAndWorkers(
+    T resource_value,
+    const performance_manager::ProcessNode* process_node,
+    const NodeSplitSet& extra_nodes,
+    const NodeSplitSet& nodes_to_skip,
+    FrameSetter frame_setter,
+    WorkerSetter worker_setter) {
   // Attribute the resources of the process to its frames and workers
   // Only renderers can host frames and workers.
   CHECK(process_node);
@@ -122,6 +138,4 @@
 
 }  // namespace resource_attribution
 
-}  // namespace performance_manager
-
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_RESOURCE_ATTRIBUTION_ATTRIBUTION_HELPERS_H_
diff --git a/components/performance_manager/public/resource_attribution/cpu_measurement_delegate.h b/components/performance_manager/public/resource_attribution/cpu_measurement_delegate.h
index 26ea3f8..5f438a88 100644
--- a/components/performance_manager/public/resource_attribution/cpu_measurement_delegate.h
+++ b/components/performance_manager/public/resource_attribution/cpu_measurement_delegate.h
@@ -15,7 +15,7 @@
 class ProcessNode;
 }  // namespace performance_manager
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 // A shim that Resource Attribution queries use to request CPU measurements for
 // a process. A new CPUMeasurementDelegate object will be created for each
@@ -29,7 +29,8 @@
   // each ProcessNode in `graph` to be measured. The factory object must outlive
   // the graph. Usually it's owned by the test harness. nullptr will cause the
   // factory returned by GetDefaultFactory() to be used.
-  static void SetDelegateFactoryForTesting(Graph* graph, Factory* factory);
+  static void SetDelegateFactoryForTesting(performance_manager::Graph* graph,
+                                           Factory* factory);
 
   // Returns the default factory to use in production.
   static Factory* GetDefaultFactory();
@@ -52,14 +53,15 @@
   // `process_node`. The production factory returns true to measure
   // renderer processes with a valid (running) base::Process and a
   // base::ProcessId assigned.
-  virtual bool ShouldMeasureProcess(const ProcessNode* process_node) = 0;
+  virtual bool ShouldMeasureProcess(
+      const performance_manager::ProcessNode* process_node) = 0;
 
   // Creates a CPUMeasurementDelegate for `process_node`. This should only be
   // called if ShouldMeasureProcess(process_node) returns true.
   virtual std::unique_ptr<CPUMeasurementDelegate> CreateDelegateForProcess(
-      const ProcessNode* process_node) = 0;
+      const performance_manager::ProcessNode* process_node) = 0;
 };
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_RESOURCE_ATTRIBUTION_CPU_MEASUREMENT_DELEGATE_H_
diff --git a/components/performance_manager/public/resource_attribution/cpu_proportion_tracker.h b/components/performance_manager/public/resource_attribution/cpu_proportion_tracker.h
index a6c9e87..e23c7fa 100644
--- a/components/performance_manager/public/resource_attribution/cpu_proportion_tracker.h
+++ b/components/performance_manager/public/resource_attribution/cpu_proportion_tracker.h
@@ -14,7 +14,7 @@
 #include "components/performance_manager/public/resource_attribution/query_results.h"
 #include "components/performance_manager/public/resource_attribution/resource_contexts.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 // A class that, given a series of consecutive CPUTimeResult measurements, will
 // calculate the proportion of CPU used over a series of consecutive intervals.
@@ -87,6 +87,6 @@
   ContextFilterCallback context_filter_;
 };
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_RESOURCE_ATTRIBUTION_CPU_PROPORTION_TRACKER_H_
diff --git a/components/performance_manager/public/resource_attribution/frame_context.h b/components/performance_manager/public/resource_attribution/frame_context.h
index f0056ed8..6baee52 100644
--- a/components/performance_manager/public/resource_attribution/frame_context.h
+++ b/components/performance_manager/public/resource_attribution/frame_context.h
@@ -20,7 +20,7 @@
 class FrameNode;
 }
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 class FrameContext {
  public:
@@ -51,20 +51,20 @@
 
   // Returns the FrameNode for this context, or a null WeakPtr if it no longer
   // exists.
-  base::WeakPtr<FrameNode> GetWeakFrameNode() const;
+  base::WeakPtr<performance_manager::FrameNode> GetWeakFrameNode() const;
 
   // PM sequence methods.
 
   // Returns the FrameContext for `node`. Equivalent to
   // node->GetResourceContext().
-  static FrameContext FromFrameNode(const FrameNode* node);
+  static FrameContext FromFrameNode(const performance_manager::FrameNode* node);
 
   // Returns the FrameContext for `node`, or nullopt if `node` is null.
   static std::optional<FrameContext> FromWeakFrameNode(
-      base::WeakPtr<FrameNode> node);
+      base::WeakPtr<performance_manager::FrameNode> node);
 
   // Returns the FrameNode for this context, or nullptr if it no longer exists.
-  FrameNode* GetFrameNode() const;
+  performance_manager::FrameNode* GetFrameNode() const;
 
   // Returns a string representation of the context for debugging. This matches
   // the interface of base::TokenType and base::UnguessableToken, for
@@ -85,12 +85,12 @@
 
  private:
   FrameContext(content::GlobalRenderFrameHostId id,
-               base::WeakPtr<FrameNode> weak_node);
+               base::WeakPtr<performance_manager::FrameNode> weak_node);
 
   content::GlobalRenderFrameHostId id_;
-  base::WeakPtr<FrameNode> weak_node_;
+  base::WeakPtr<performance_manager::FrameNode> weak_node_;
 };
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_RESOURCE_ATTRIBUTION_FRAME_CONTEXT_H_
diff --git a/components/performance_manager/public/resource_attribution/memory_measurement_delegate.h b/components/performance_manager/public/resource_attribution/memory_measurement_delegate.h
index fb170dd..35293ed 100644
--- a/components/performance_manager/public/resource_attribution/memory_measurement_delegate.h
+++ b/components/performance_manager/public/resource_attribution/memory_measurement_delegate.h
@@ -15,7 +15,7 @@
 class Graph;
 }
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 class ProcessContext;
 
@@ -52,7 +52,8 @@
   // measure ProcessNodes in `graph`. The factory object must outlive the graph.
   // Usually it's owned by the test harness. nullptr will cause the factory
   // returned by GetDefaultFactory() to be used.
-  static void SetDelegateFactoryForTesting(Graph* graph, Factory* factory);
+  static void SetDelegateFactoryForTesting(performance_manager::Graph* graph,
+                                           Factory* factory);
 
   // Returns the default factory to use in production.
   static Factory* GetDefaultFactory();
@@ -71,9 +72,9 @@
 
   // Creates a MemoryMeasurementDelegate for all ProcessNodes in `graph`.
   virtual std::unique_ptr<MemoryMeasurementDelegate> CreateDelegate(
-      Graph* graph) = 0;
+      performance_manager::Graph* graph) = 0;
 };
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_RESOURCE_ATTRIBUTION_MEMORY_MEASUREMENT_DELEGATE_H_
diff --git a/components/performance_manager/public/resource_attribution/page_context.h b/components/performance_manager/public/resource_attribution/page_context.h
index bbdabb3..5c9d272 100644
--- a/components/performance_manager/public/resource_attribution/page_context.h
+++ b/components/performance_manager/public/resource_attribution/page_context.h
@@ -22,7 +22,7 @@
 class PageNode;
 }
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 class PageContext {
  public:
@@ -46,20 +46,20 @@
 
   // Returns the PageNode for this context, or a null WeakPtr if it no longer
   // exists.
-  base::WeakPtr<PageNode> GetWeakPageNode() const;
+  base::WeakPtr<performance_manager::PageNode> GetWeakPageNode() const;
 
   // PM sequence methods
 
   // Returns the PageContext for `node`. Equivalent to
   // node->GetResourceContext().
-  static PageContext FromPageNode(const PageNode* node);
+  static PageContext FromPageNode(const performance_manager::PageNode* node);
 
   // Returns the PageContext for `node`, or nullopt if `node` is null.
   static std::optional<PageContext> FromWeakPageNode(
-      base::WeakPtr<PageNode> node);
+      base::WeakPtr<performance_manager::PageNode> node);
 
   // Returns the PageNode for this context, or nullptr if it no longer exists.
-  PageNode* GetPageNode() const;
+  performance_manager::PageNode* GetPageNode() const;
 
   // Returns a string representation of the context for debugging. This matches
   // the interface of base::TokenType and base::UnguessableToken, for
@@ -79,8 +79,8 @@
 
  private:
   PageContext(base::UnguessableToken token,
-              WebContentsProxy web_contents_proxy,
-              base::WeakPtr<PageNode> weak_node);
+              performance_manager::WebContentsProxy web_contents_proxy,
+              base::WeakPtr<performance_manager::PageNode> weak_node);
 
   // A unique identifier for the PageNode. A PageNodeImpl::PageToken will be
   // assigned to this, but DEPS rules won't let PageNodeImpl be included in a
@@ -90,12 +90,12 @@
 
   // A PerformanceManager proxy object that resolves to the WebContents on the
   // UI thread.
-  WebContentsProxy web_contents_proxy_;
+  performance_manager::WebContentsProxy web_contents_proxy_;
 
   // A pointer to the PageNode that must be dereferenced on the PM sequence.
-  base::WeakPtr<PageNode> weak_node_;
+  base::WeakPtr<performance_manager::PageNode> weak_node_;
 };
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_RESOURCE_ATTRIBUTION_PAGE_CONTEXT_H_
diff --git a/components/performance_manager/public/resource_attribution/process_context.h b/components/performance_manager/public/resource_attribution/process_context.h
index 1d3ba77..86ed4c0 100644
--- a/components/performance_manager/public/resource_attribution/process_context.h
+++ b/components/performance_manager/public/resource_attribution/process_context.h
@@ -23,7 +23,7 @@
 class ProcessNode;
 }
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 class ProcessContext {
  public:
@@ -71,7 +71,7 @@
   // If this context refers to a renderer process, returns the
   // RenderProcessHostId that was assigned to it, otherwise returns a null
   // RenderProcessHostId.
-  RenderProcessHostId GetRenderProcessHostId() const;
+  performance_manager::RenderProcessHostId GetRenderProcessHostId() const;
 
   // If this context refers to a non-renderer child process, returns its
   // BrowserChildProcessHost. Returns nullptr if it is not a non-renderer child
@@ -81,25 +81,27 @@
   // If this context refers to a non-renderer child process, returns
   // the BrowserChildProcessHostId that was assigned to it, otherwise returns a
   // null BrowserChildProcessHostId.
-  BrowserChildProcessHostId GetBrowserChildProcessHostId() const;
+  performance_manager::BrowserChildProcessHostId GetBrowserChildProcessHostId()
+      const;
 
   // Returns the ProcessNode for this context, or a null WeakPtr if it no longer
   // exists.
-  base::WeakPtr<ProcessNode> GetWeakProcessNode() const;
+  base::WeakPtr<performance_manager::ProcessNode> GetWeakProcessNode() const;
 
   // PM sequence methods.
 
   // Returns the ProcessContext for `node`. Equivalent to
   // node->GetResourceContext().
-  static ProcessContext FromProcessNode(const ProcessNode* node);
+  static ProcessContext FromProcessNode(
+      const performance_manager::ProcessNode* node);
 
   // Returns the ProcessContext for `node`, or nullopt if `node` is null.
   static std::optional<ProcessContext> FromWeakProcessNode(
-      base::WeakPtr<ProcessNode> node);
+      base::WeakPtr<performance_manager::ProcessNode> node);
 
   // Returns the ProcessNode for this context, or nullptr if it no longer
   // exists.
-  ProcessNode* GetProcessNode() const;
+  performance_manager::ProcessNode* GetProcessNode() const;
 
   // Returns a string representation of the context for debugging. This matches
   // the interface of base::TokenType and base::UnguessableToken, for
@@ -136,16 +138,18 @@
   static_assert(BrowserProcessTag{} == BrowserProcessTag{},
                 "empty structs should always compare equal");
 
-  using AnyProcessHostId = absl::variant<BrowserProcessTag,
-                                         RenderProcessHostId,
-                                         BrowserChildProcessHostId>;
+  using AnyProcessHostId =
+      absl::variant<BrowserProcessTag,
+                    performance_manager::RenderProcessHostId,
+                    performance_manager::BrowserChildProcessHostId>;
 
-  ProcessContext(AnyProcessHostId id, base::WeakPtr<ProcessNode> weak_node);
+  ProcessContext(AnyProcessHostId id,
+                 base::WeakPtr<performance_manager::ProcessNode> weak_node);
 
   AnyProcessHostId id_;
-  base::WeakPtr<ProcessNode> weak_node_;
+  base::WeakPtr<performance_manager::ProcessNode> weak_node_;
 };
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_RESOURCE_ATTRIBUTION_PROCESS_CONTEXT_H_
diff --git a/components/performance_manager/public/resource_attribution/queries.h b/components/performance_manager/public/resource_attribution/queries.h
index ba866ec..71659d7b 100644
--- a/components/performance_manager/public/resource_attribution/queries.h
+++ b/components/performance_manager/public/resource_attribution/queries.h
@@ -21,7 +21,7 @@
 #include "components/performance_manager/public/resource_attribution/resource_types.h"
 #include "components/performance_manager/public/resource_attribution/type_helpers.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 namespace internal {
 struct QueryParams;
@@ -221,6 +221,6 @@
       GUARDED_BY_CONTEXT(sequence_checker_);
 };
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_RESOURCE_ATTRIBUTION_QUERIES_H_
diff --git a/components/performance_manager/public/resource_attribution/query_results.h b/components/performance_manager/public/resource_attribution/query_results.h
index e0e83d2..22cea83 100644
--- a/components/performance_manager/public/resource_attribution/query_results.h
+++ b/components/performance_manager/public/resource_attribution/query_results.h
@@ -13,7 +13,7 @@
 #include "components/performance_manager/public/resource_attribution/resource_contexts.h"
 #include "components/performance_manager/public/resource_attribution/resource_types.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 // The Resource Attribution result and metadata structs described in
 // https://bit.ly/resource-attribution-api#heading=h.k8fjwkwxxdj6.
@@ -108,6 +108,6 @@
 // A map from a ResourceContext to all query results received for that context.
 using QueryResultMap = std::map<ResourceContext, QueryResults>;
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_RESOURCE_ATTRIBUTION_QUERY_RESULTS_H_
diff --git a/components/performance_manager/public/resource_attribution/resource_contexts.h b/components/performance_manager/public/resource_attribution/resource_contexts.h
index de7dc5ce..59c56f9 100644
--- a/components/performance_manager/public/resource_attribution/resource_contexts.h
+++ b/components/performance_manager/public/resource_attribution/resource_contexts.h
@@ -14,7 +14,7 @@
 #include "components/performance_manager/public/resource_attribution/worker_context.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 // Each ResourceContext measured by resource attribution is identified by an
 // object with the following properties:
@@ -99,6 +99,6 @@
 
 }  // namespace internal
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_RESOURCE_ATTRIBUTION_RESOURCE_CONTEXTS_H_
diff --git a/components/performance_manager/public/resource_attribution/resource_types.h b/components/performance_manager/public/resource_attribution/resource_types.h
index 3c3a3ee9..ac0622d 100644
--- a/components/performance_manager/public/resource_attribution/resource_types.h
+++ b/components/performance_manager/public/resource_attribution/resource_types.h
@@ -7,7 +7,7 @@
 
 #include "base/containers/enum_set.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 // Types of resources that Resource Attribution can measure.
 enum class ResourceType {
@@ -23,6 +23,6 @@
                                       /*min=*/ResourceType::kCPUTime,
                                       /*max=*/ResourceType::kMemorySummary>;
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_RESOURCE_ATTRIBUTION_RESOURCE_TYPES_H_
diff --git a/components/performance_manager/public/resource_attribution/type_helpers.h b/components/performance_manager/public/resource_attribution/type_helpers.h
index 7a293f0..d7794623 100644
--- a/components/performance_manager/public/resource_attribution/type_helpers.h
+++ b/components/performance_manager/public/resource_attribution/type_helpers.h
@@ -13,7 +13,7 @@
 #include "base/types/optional_util.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 namespace internal {
 
@@ -144,6 +144,6 @@
 using internal::operator==;
 using internal::operator!=;
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_RESOURCE_ATTRIBUTION_TYPE_HELPERS_H_
diff --git a/components/performance_manager/public/resource_attribution/worker_context.h b/components/performance_manager/public/resource_attribution/worker_context.h
index 455dddb..a62e50af 100644
--- a/components/performance_manager/public/resource_attribution/worker_context.h
+++ b/components/performance_manager/public/resource_attribution/worker_context.h
@@ -16,7 +16,7 @@
 class WorkerNode;
 }  // namespace performance_manager
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 class WorkerContext {
  public:
@@ -39,20 +39,21 @@
 
   // Returns the WorkerNode for this context, or a null WeakPtr if it no longer
   // exists.
-  base::WeakPtr<WorkerNode> GetWeakWorkerNode() const;
+  base::WeakPtr<performance_manager::WorkerNode> GetWeakWorkerNode() const;
 
   // PM sequence methods.
 
   // Returns the WorkerContext for `node`. Equivalent to
   // node->GetResourceContext().
-  static WorkerContext FromWorkerNode(const WorkerNode* node);
+  static WorkerContext FromWorkerNode(
+      const performance_manager::WorkerNode* node);
 
   // Returns the WorkerContext for `node`, or nullopt if `node` is null.
   static std::optional<WorkerContext> FromWeakWorkerNode(
-      base::WeakPtr<WorkerNode> node);
+      base::WeakPtr<performance_manager::WorkerNode> node);
 
   // Returns the WorkerNode for this context, or nullptr if it no longer exists.
-  WorkerNode* GetWorkerNode() const;
+  performance_manager::WorkerNode* GetWorkerNode() const;
 
   // Returns a string representation of the context for debugging. This matches
   // the interface of base::TokenType and base::UnguessableToken, for
@@ -73,12 +74,12 @@
 
  private:
   WorkerContext(const blink::WorkerToken& token,
-                base::WeakPtr<WorkerNode> weak_node);
+                base::WeakPtr<performance_manager::WorkerNode> weak_node);
 
   blink::WorkerToken token_;
-  base::WeakPtr<WorkerNode> weak_node_;
+  base::WeakPtr<performance_manager::WorkerNode> weak_node_;
 };
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_RESOURCE_ATTRIBUTION_WORKER_CONTEXT_H_
diff --git a/components/performance_manager/resource_attribution/DEPS b/components/performance_manager/resource_attribution/DEPS
new file mode 100644
index 0000000..b5396f6
--- /dev/null
+++ b/components/performance_manager/resource_attribution/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  # Aliases may be used in this directory.
+  "+components/performance_manager/resource_attribution/performance_manager_aliases.h",
+]
\ No newline at end of file
diff --git a/components/performance_manager/resource_attribution/README.md b/components/performance_manager/resource_attribution/README.md
index cbcc2913..b2cbac6d 100644
--- a/components/performance_manager/resource_attribution/README.md
+++ b/components/performance_manager/resource_attribution/README.md
@@ -16,11 +16,10 @@
 
 * Public headers are in
   components/performance_manager/public/resource_attribution/.
-* Public symbols are in the `performance_manager::resource_attribution`
-  namespace.
+* Public symbols are in the `resource_attribution` namespace.
 * Private symbols that must be referenced from
   components/performance_manager/public/ (eg. in templates) are in the
-  `performance_manager::resource_attribution::internal` namespace.
+  `resource_attribution::internal` namespace.
 
 # Implementation
 
diff --git a/components/performance_manager/resource_attribution/attribution_impl_helpers.h b/components/performance_manager/resource_attribution/attribution_impl_helpers.h
index 6eb8f2c..81a2b3f 100644
--- a/components/performance_manager/resource_attribution/attribution_impl_helpers.h
+++ b/components/performance_manager/resource_attribution/attribution_impl_helpers.h
@@ -11,8 +11,9 @@
 #include "components/performance_manager/graph/frame_node_impl.h"
 #include "components/performance_manager/graph/process_node_impl.h"
 #include "components/performance_manager/graph/worker_node_impl.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 // Splits a resource of type T between all frames and workers hosted in
 // `process_node`, by calling setter methods on the FrameNodeImpl and
@@ -45,6 +46,6 @@
       }));
 }
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_RESOURCE_ATTRIBUTION_ATTRIBUTION_IMPL_HELPERS_H_
diff --git a/components/performance_manager/resource_attribution/context_collection.cc b/components/performance_manager/resource_attribution/context_collection.cc
index f78a95a..e17aaad 100644
--- a/components/performance_manager/resource_attribution/context_collection.cc
+++ b/components/performance_manager/resource_attribution/context_collection.cc
@@ -8,7 +8,7 @@
 
 #include "base/containers/contains.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 using ResourceContextTypeId = internal::ResourceContextTypeId;
 
@@ -50,4 +50,4 @@
   return collection;
 }
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
diff --git a/components/performance_manager/resource_attribution/context_collection.h b/components/performance_manager/resource_attribution/context_collection.h
index 219d0ee..febdef4 100644
--- a/components/performance_manager/resource_attribution/context_collection.h
+++ b/components/performance_manager/resource_attribution/context_collection.h
@@ -11,7 +11,7 @@
 #include "components/performance_manager/public/resource_attribution/resource_contexts.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 // A mixed collection of individual ResourceContext's and
 // ResourceContextTypeId's.
@@ -59,6 +59,6 @@
   std::bitset<absl::variant_size<ResourceContext>::value> all_context_types_;
 };
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_RESOURCE_ATTRIBUTION_CONTEXT_COLLECTION_H_
diff --git a/components/performance_manager/resource_attribution/cpu_measurement_delegate.cc b/components/performance_manager/resource_attribution/cpu_measurement_delegate.cc
index 94214944..a73c70a 100644
--- a/components/performance_manager/resource_attribution/cpu_measurement_delegate.cc
+++ b/components/performance_manager/resource_attribution/cpu_measurement_delegate.cc
@@ -12,11 +12,12 @@
 #include "build/build_config.h"
 #include "components/performance_manager/public/graph/process_node.h"
 #include "components/performance_manager/resource_attribution/cpu_measurement_monitor.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 #include "components/performance_manager/resource_attribution/query_scheduler.h"
 #include "content/public/browser/browser_child_process_host.h"
 #include "content/public/common/process_type.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 namespace {
 
@@ -114,4 +115,4 @@
   return default_factory.get();
 }
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
diff --git a/components/performance_manager/resource_attribution/cpu_measurement_monitor.cc b/components/performance_manager/resource_attribution/cpu_measurement_monitor.cc
index ddb9005..95858f6a 100644
--- a/components/performance_manager/resource_attribution/cpu_measurement_monitor.cc
+++ b/components/performance_manager/resource_attribution/cpu_measurement_monitor.cc
@@ -33,7 +33,7 @@
 #include "content/public/common/process_type.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 namespace {
 
@@ -430,10 +430,12 @@
     const base::TimeDelta measurement_interval =
         result.metadata.measurement_time - result.start_time;
     dict.Set("algorithm", static_cast<int>(result.metadata.algorithm));
-    dict.Set("measurement_time",
-             TimeSinceEpochToValue(result.metadata.measurement_time));
-    dict.Set("measurement_interval", TimeDeltaToValue(measurement_interval));
-    dict.Set("cumulative_cpu", TimeDeltaToValue(result.cumulative_cpu));
+    dict.Set("measurement_time", performance_manager::TimeSinceEpochToValue(
+                                     result.metadata.measurement_time));
+    dict.Set("measurement_interval",
+             performance_manager::TimeDeltaToValue(measurement_interval));
+    dict.Set("cumulative_cpu",
+             performance_manager::TimeDeltaToValue(result.cumulative_cpu));
   }
   return dict;
 }
@@ -631,4 +633,4 @@
       });
 }
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
diff --git a/components/performance_manager/resource_attribution/cpu_measurement_monitor.h b/components/performance_manager/resource_attribution/cpu_measurement_monitor.h
index 734134c..f7be819 100644
--- a/components/performance_manager/resource_attribution/cpu_measurement_monitor.h
+++ b/components/performance_manager/resource_attribution/cpu_measurement_monitor.h
@@ -23,13 +23,9 @@
 #include "components/performance_manager/public/resource_attribution/query_results.h"
 #include "components/performance_manager/public/resource_attribution/resource_contexts.h"
 #include "components/performance_manager/resource_attribution/graph_change.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 
-namespace performance_manager {
-class Graph;
-class PageNode;
-}
-
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 // Periodically collect CPU usage from process nodes.
 //
@@ -37,10 +33,11 @@
 // measurement for a node may not arrive until after it's removed from the
 // graph. So this is not a decorator as defined in
 // components/performance_manager/README.md
-class CPUMeasurementMonitor : public FrameNode::ObserverDefaultImpl,
-                              public ProcessNode::ObserverDefaultImpl,
-                              public WorkerNode::ObserverDefaultImpl,
-                              public NodeDataDescriberDefaultImpl {
+class CPUMeasurementMonitor
+    : public FrameNode::ObserverDefaultImpl,
+      public ProcessNode::ObserverDefaultImpl,
+      public WorkerNode::ObserverDefaultImpl,
+      public performance_manager::NodeDataDescriberDefaultImpl {
  public:
   CPUMeasurementMonitor();
   ~CPUMeasurementMonitor() override;
@@ -206,6 +203,6 @@
   raw_ptr<Graph> graph_ GUARDED_BY_CONTEXT(sequence_checker_) = nullptr;
 };
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_RESOURCE_ATTRIBUTION_CPU_MEASUREMENT_MONITOR_H_
diff --git a/components/performance_manager/resource_attribution/cpu_measurement_monitor_unittest.cc b/components/performance_manager/resource_attribution/cpu_measurement_monitor_unittest.cc
index 089845c..9bfae95d 100644
--- a/components/performance_manager/resource_attribution/cpu_measurement_monitor_unittest.cc
+++ b/components/performance_manager/resource_attribution/cpu_measurement_monitor_unittest.cc
@@ -28,12 +28,12 @@
 #include "components/performance_manager/graph/page_node_impl.h"
 #include "components/performance_manager/graph/process_node_impl.h"
 #include "components/performance_manager/graph/worker_node_impl.h"
-#include "components/performance_manager/public/graph/process_node.h"
 #include "components/performance_manager/public/performance_manager.h"
 #include "components/performance_manager/public/resource_attribution/cpu_measurement_delegate.h"
 #include "components/performance_manager/public/resource_attribution/cpu_proportion_tracker.h"
 #include "components/performance_manager/public/resource_attribution/query_results.h"
 #include "components/performance_manager/public/resource_attribution/resource_contexts.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 #include "components/performance_manager/test_support/graph_test_harness.h"
 #include "components/performance_manager/test_support/mock_graphs.h"
 #include "components/performance_manager/test_support/performance_manager_test_harness.h"
@@ -48,7 +48,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 namespace {
 
@@ -60,14 +60,17 @@
 using ::testing::Not;
 using ::testing::Pair;
 
+using performance_manager::TestNodeWrapper;
+
 constexpr base::TimeDelta kTimeBetweenMeasurements = base::Minutes(5);
 
 }  // namespace
 
 // A test that creates mock processes to simulate exact CPU usage.
-class ResourceAttrCPUMonitorTest : public GraphTestHarness {
+class ResourceAttrCPUMonitorTest
+    : public performance_manager::GraphTestHarness {
  protected:
-  using Super = GraphTestHarness;
+  using Super = performance_manager::GraphTestHarness;
 
   void SetUp() override {
     GetGraphFeatures().EnableResourceAttributionScheduler();
@@ -532,7 +535,8 @@
 // workers in those processes, and correctly aggregated to pages containing
 // frames and workers from multiple processes.
 TEST_F(ResourceAttrCPUMonitorTest, CPUDistribution) {
-  MockUtilityAndMultipleRenderProcessesGraph mock_graph(graph());
+  performance_manager::MockUtilityAndMultipleRenderProcessesGraph mock_graph(
+      graph());
 
   // The mock browser and utility processes should be measured, but do not
   // contain frames or workers so should not affect the distribution of
@@ -743,7 +747,8 @@
 // Tests that CPU usage of processes is correctly distributed between FrameNodes
 // and WorkerNodes that are added and removed between measurements.
 TEST_F(ResourceAttrCPUMonitorTest, AddRemoveNodes) {
-  MockMultiplePagesAndWorkersWithMultipleProcessesGraph mock_graph(graph());
+  performance_manager::MockMultiplePagesAndWorkersWithMultipleProcessesGraph
+      mock_graph(graph());
 
   SetProcessCPUUsage(mock_graph.process.get(), 0.6);
   SetProcessCPUUsage(mock_graph.other_process.get(), 0.5);
@@ -954,7 +959,8 @@
 // Tests that WorkerNode CPU usage is correctly distributed to pages as clients
 // are added and removed.
 TEST_F(ResourceAttrCPUMonitorTest, AddRemoveWorkerClients) {
-  MockMultiplePagesAndWorkersWithMultipleProcessesGraph mock_graph(graph());
+  performance_manager::MockMultiplePagesAndWorkersWithMultipleProcessesGraph
+      mock_graph(graph());
 
   SetProcessCPUUsage(mock_graph.process.get(), 0.6);
   SetProcessCPUUsage(mock_graph.other_process.get(), 0.5);
@@ -1396,7 +1402,8 @@
 // Tests that multiple CPUProportionTrackers with different schedules are
 // independent. Also tests trackers with and without a context filter.
 TEST_F(ResourceAttrCPUMonitorTest, MultipleCPUProportionTrackers) {
-  MockMultiplePagesWithMultipleProcessesGraph mock_graph(graph());
+  performance_manager::MockMultiplePagesWithMultipleProcessesGraph mock_graph(
+      graph());
   SetProcessCPUUsage(mock_graph.process.get(), 1.0);
   SetProcessCPUUsage(mock_graph.other_process.get(), 1.0);
 
@@ -1515,21 +1522,22 @@
 
 // A test that creates real processes, to verify that measurement works with the
 // timing of real node creation.
-class ResourceAttrCPUMonitorTimingTest : public PerformanceManagerTestHarness {
+class ResourceAttrCPUMonitorTimingTest
+    : public performance_manager::PerformanceManagerTestHarness {
  protected:
-  using Super = PerformanceManagerTestHarness;
+  using Super = performance_manager::PerformanceManagerTestHarness;
 
   void SetUp() override {
     GetGraphFeatures().EnableResourceAttributionScheduler();
     Super::SetUp();
-    RunInGraph([&](Graph* graph) {
+    performance_manager::RunInGraph([&](performance_manager::Graph* graph) {
       cpu_monitor_ = std::make_unique<CPUMeasurementMonitor>();
       cpu_monitor_->StartMonitoring(graph);
     });
   }
 
   void TearDown() override {
-    RunInGraph([&] { cpu_monitor_.reset(); });
+    performance_manager::RunInGraph([&] { cpu_monitor_.reset(); });
     Super::TearDown();
   }
 
@@ -1557,7 +1565,7 @@
   // but has no pid. (Equivalent to the time between OnProcessNodeAdded and
   // OnProcessLifetimeChange.)
   LetTimePass();
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     ASSERT_TRUE(process_node);
     EXPECT_EQ(process_node->GetProcessId(), base::kNullProcessId);
 
@@ -1582,7 +1590,7 @@
         ->SetProcess(base::Process::Current(), base::TimeTicks::Now());
     EXPECT_NE(process_node->GetProcessId(), base::kNullProcessId);
   };
-  RunInGraph(set_process_on_pm_sequence);
+  performance_manager::RunInGraph(set_process_on_pm_sequence);
 
   // Let some time pass so there's CPU to measure after monitoring starts.
   LetTimePass();
@@ -1596,7 +1604,7 @@
   base::TimeDelta cumulative_process_cpu;
   base::TimeDelta cumulative_browser_process_cpu;
   base::TimeDelta cumulative_frame_cpu;
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     ASSERT_TRUE(process_node);
     ASSERT_TRUE(browser_process_node);
     EXPECT_TRUE(process_node->GetProcess().IsValid());
@@ -1626,7 +1634,7 @@
   process()->SimulateRenderProcessExit(
       base::TERMINATION_STATUS_NORMAL_TERMINATION, 0);
   LetTimePass();
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     // Process is no longer running, so can't be measured.
     ASSERT_TRUE(process_node);
     EXPECT_FALSE(process_node->GetProcess().IsValid());
@@ -1655,10 +1663,10 @@
   // (Navigating the renderer will create a new frame tree in that process.)
   EXPECT_FALSE(main_rfh()->IsRenderFrameLive());
   EXPECT_TRUE(process()->MayReuseHost());
-  RunInGraph(set_process_on_pm_sequence);
+  performance_manager::RunInGraph(set_process_on_pm_sequence);
 
   LetTimePass();
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     ASSERT_TRUE(process_node);
     EXPECT_TRUE(process_node->GetProcess().IsValid());
 
@@ -1675,4 +1683,4 @@
   });
 }
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
diff --git a/components/performance_manager/resource_attribution/cpu_proportion_tracker.cc b/components/performance_manager/resource_attribution/cpu_proportion_tracker.cc
index 1f4335de..21f8613b6 100644
--- a/components/performance_manager/resource_attribution/cpu_proportion_tracker.cc
+++ b/components/performance_manager/resource_attribution/cpu_proportion_tracker.cc
@@ -10,7 +10,7 @@
 #include "base/check.h"
 #include "base/functional/callback.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 CPUProportionTracker::CPUProportionTracker(ContextFilterCallback context_filter)
     : context_filter_(std::move(context_filter)) {}
@@ -158,4 +158,4 @@
   return last_measurement_time_.has_value();
 }
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
diff --git a/components/performance_manager/resource_attribution/frame_context.cc b/components/performance_manager/resource_attribution/frame_context.cc
index 67d940acd..913854d 100644
--- a/components/performance_manager/resource_attribution/frame_context.cc
+++ b/components/performance_manager/resource_attribution/frame_context.cc
@@ -16,11 +16,12 @@
 #include "components/performance_manager/public/graph/graph.h"
 #include "components/performance_manager/public/performance_manager.h"
 #include "components/performance_manager/public/render_process_host_id.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/global_routing_id.h"
 #include "content/public/browser/render_frame_host.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 namespace {
 
@@ -123,4 +124,4 @@
   return s.str();
 }
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
diff --git a/components/performance_manager/resource_attribution/frame_context_unittest.cc b/components/performance_manager/resource_attribution/frame_context_unittest.cc
index 2ab06a2..415a885 100644
--- a/components/performance_manager/resource_attribution/frame_context_unittest.cc
+++ b/components/performance_manager/resource_attribution/frame_context_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/memory/weak_ptr.h"
 #include "components/performance_manager/public/graph/frame_node.h"
 #include "components/performance_manager/public/performance_manager.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 #include "components/performance_manager/test_support/performance_manager_test_harness.h"
 #include "components/performance_manager/test_support/run_in_graph.h"
 #include "content/public/browser/global_routing_id.h"
@@ -21,11 +22,12 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 namespace {
 
-using ResourceAttrFrameContextTest = PerformanceManagerTestHarness;
+using ResourceAttrFrameContextTest =
+    performance_manager::PerformanceManagerTestHarness;
 using ResourceAttrFrameContextNoPMTest = content::RenderViewHostTestHarness;
 
 TEST_F(ResourceAttrFrameContextTest, FrameContexts) {
@@ -47,7 +49,7 @@
   base::WeakPtr<FrameNode> frame_node = frame_context->GetWeakFrameNode();
   base::WeakPtr<FrameNode> frame_node_from_pm =
       PerformanceManager::GetFrameNodeForRenderFrameHost(rfh);
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     ASSERT_TRUE(frame_node);
     ASSERT_TRUE(frame_node_from_pm);
     EXPECT_EQ(frame_node.get(), frame_node_from_pm.get());
@@ -78,7 +80,7 @@
   EXPECT_EQ(nullptr, frame_context->GetRenderFrameHost());
   EXPECT_EQ(rfh_id, frame_context->GetRenderFrameHostId());
 
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     EXPECT_FALSE(frame_node);
     EXPECT_EQ(nullptr, frame_context->GetFrameNode());
     EXPECT_EQ(std::nullopt, FrameContext::FromWeakFrameNode(frame_node));
@@ -99,9 +101,10 @@
   ASSERT_TRUE(rfh);
 
   // Verify that PM didn't see the frame.
-  RunInGraph([node = PerformanceManager::GetFrameNodeForRenderFrameHost(rfh)] {
-    EXPECT_FALSE(node);
-  });
+  performance_manager::RunInGraph(
+      [node = PerformanceManager::GetFrameNodeForRenderFrameHost(rfh)] {
+        EXPECT_FALSE(node);
+      });
 
   // FromRenderFrameHost() should return nullopt, not a context that's missing
   // PM info.
@@ -110,4 +113,4 @@
 
 }  // namespace
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
diff --git a/components/performance_manager/resource_attribution/graph_change.h b/components/performance_manager/resource_attribution/graph_change.h
index d77dc8e..a0ac5672 100644
--- a/components/performance_manager/resource_attribution/graph_change.h
+++ b/components/performance_manager/resource_attribution/graph_change.h
@@ -6,14 +6,10 @@
 #define COMPONENTS_PERFORMANCE_MANAGER_RESOURCE_ATTRIBUTION_GRAPH_CHANGE_H_
 
 #include "base/memory/raw_ptr.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 
-namespace performance_manager {
-class FrameNode;
-class WorkerNode;
-}  // namespace performance_manager
-
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 // Graph changes that can affect resource measurement distribution.
 // These are all passed on the stack so don't need to use raw_ptr.
@@ -90,6 +86,6 @@
                                   GraphChangeAddClientWorkerToWorker,
                                   GraphChangeRemoveClientWorkerFromWorker>;
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_RESOURCE_ATTRIBUTION_GRAPH_CHANGE_H_
diff --git a/components/performance_manager/resource_attribution/memory_measurement_delegate.cc b/components/performance_manager/resource_attribution/memory_measurement_delegate.cc
index 21825c4..f0324db 100644
--- a/components/performance_manager/resource_attribution/memory_measurement_delegate.cc
+++ b/components/performance_manager/resource_attribution/memory_measurement_delegate.cc
@@ -17,11 +17,12 @@
 #include "components/performance_manager/graph/graph_impl.h"
 #include "components/performance_manager/graph/process_node_impl.h"
 #include "components/performance_manager/public/resource_attribution/process_context.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 #include "components/performance_manager/resource_attribution/query_scheduler.h"
 #include "services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.h"
 #include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 namespace {
 
@@ -132,4 +133,4 @@
   return default_factory.get();
 }
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
diff --git a/components/performance_manager/resource_attribution/memory_measurement_provider.cc b/components/performance_manager/resource_attribution/memory_measurement_provider.cc
index c948c6a..903005d1 100644
--- a/components/performance_manager/resource_attribution/memory_measurement_provider.cc
+++ b/components/performance_manager/resource_attribution/memory_measurement_provider.cc
@@ -17,9 +17,10 @@
 #include "components/performance_manager/public/graph/process_node.h"
 #include "components/performance_manager/public/graph/worker_node.h"
 #include "components/performance_manager/public/resource_attribution/attribution_helpers.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 #include "components/performance_manager/resource_attribution/worker_client_pages.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 MemoryMeasurementProvider::MemoryMeasurementProvider(Graph* graph)
     : graph_(graph) {
@@ -105,4 +106,4 @@
   std::move(callback).Run(std::move(results));
 }
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
diff --git a/components/performance_manager/resource_attribution/memory_measurement_provider.h b/components/performance_manager/resource_attribution/memory_measurement_provider.h
index c23a824..e64e8c0b 100644
--- a/components/performance_manager/resource_attribution/memory_measurement_provider.h
+++ b/components/performance_manager/resource_attribution/memory_measurement_provider.h
@@ -14,12 +14,9 @@
 #include "components/performance_manager/public/resource_attribution/memory_measurement_delegate.h"
 #include "components/performance_manager/public/resource_attribution/query_results.h"
 #include "components/performance_manager/public/resource_attribution/resource_contexts.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 
-namespace performance_manager {
-class Graph;
-}
-
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 class MemoryMeasurementProvider {
  public:
@@ -57,6 +54,6 @@
   raw_ptr<Graph> graph_ GUARDED_BY_CONTEXT(sequence_checker_) = nullptr;
 };
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_RESOURCE_ATTRIBUTION_MEMORY_MEASUREMENT_PROVIDER_H_
diff --git a/components/performance_manager/resource_attribution/memory_measurement_provider_browsertest.cc b/components/performance_manager/resource_attribution/memory_measurement_provider_browsertest.cc
index c35d79ef..d119d14 100644
--- a/components/performance_manager/resource_attribution/memory_measurement_provider_browsertest.cc
+++ b/components/performance_manager/resource_attribution/memory_measurement_provider_browsertest.cc
@@ -15,6 +15,7 @@
 #include "components/performance_manager/public/graph/graph.h"
 #include "components/performance_manager/public/resource_attribution/query_results.h"
 #include "components/performance_manager/public/resource_attribution/resource_contexts.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 #include "components/performance_manager/test_support/performance_manager_browsertest_harness.h"
 #include "components/performance_manager/test_support/resource_attribution/gtest_util.h"
 #include "components/performance_manager/test_support/run_in_graph.h"
@@ -31,7 +32,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 namespace {
 
@@ -46,9 +47,9 @@
 using ::testing::Pair;
 
 class ResourceAttrMemoryMeasurementProviderBrowserTest
-    : public PerformanceManagerBrowserTestHarness {
+    : public performance_manager::PerformanceManagerBrowserTestHarness {
  protected:
-  using Super = PerformanceManagerBrowserTestHarness;
+  using Super = performance_manager::PerformanceManagerBrowserTestHarness;
 
   void OnGraphCreated(Graph* graph) override {
     memory_provider_ = std::make_unique<MemoryMeasurementProvider>(graph);
@@ -58,7 +59,7 @@
   void TearDownOnMainThread() override {
     // Delete MemoryMeasurementProvider before tearing down the graph to avoid
     // dangling pointers.
-    RunInGraph([&] { memory_provider_.reset(); });
+    performance_manager::RunInGraph([&] { memory_provider_.reset(); });
     Super::TearDownOnMainThread();
   }
 
@@ -68,7 +69,7 @@
     base::test::TestFuture<QueryResultMap> results_future;
     base::OnceCallback<void(QueryResultMap)> results_callback =
         results_future.GetSequenceBoundCallback();
-    RunInGraph([&](base::OnceClosure quit_closure) {
+    performance_manager::RunInGraph([&](base::OnceClosure quit_closure) {
       memory_provider_->RequestMemorySummary(
           std::move(results_callback).Then(std::move(quit_closure)));
     });
@@ -250,4 +251,4 @@
 
 }  // namespace
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
diff --git a/components/performance_manager/resource_attribution/page_context.cc b/components/performance_manager/resource_attribution/page_context.cc
index 395751b..c6a48a2 100644
--- a/components/performance_manager/resource_attribution/page_context.cc
+++ b/components/performance_manager/resource_attribution/page_context.cc
@@ -11,10 +11,15 @@
 #include "components/performance_manager/graph/page_node_impl.h"
 #include "components/performance_manager/performance_manager_tab_helper.h"
 #include "components/performance_manager/public/graph/graph.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
+
+using PerformanceManagerTabHelper =
+    performance_manager::PerformanceManagerTabHelper;
+using WebContentsProxy = performance_manager::WebContentsProxy;
 
 PageContext::PageContext(base::UnguessableToken token,
                          WebContentsProxy web_contents_proxy,
@@ -96,4 +101,4 @@
   return base::StrCat({"PageContext:", token_.ToString()});
 }
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
diff --git a/components/performance_manager/resource_attribution/page_context_unittest.cc b/components/performance_manager/resource_attribution/page_context_unittest.cc
index bab20b3e..9d584ff 100644
--- a/components/performance_manager/resource_attribution/page_context_unittest.cc
+++ b/components/performance_manager/resource_attribution/page_context_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/memory/weak_ptr.h"
 #include "components/performance_manager/public/graph/page_node.h"
 #include "components/performance_manager/public/performance_manager.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 #include "components/performance_manager/test_support/performance_manager_test_harness.h"
 #include "components/performance_manager/test_support/run_in_graph.h"
 #include "content/public/browser/render_frame_host.h"
@@ -20,11 +21,12 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 namespace {
 
-using ResourceAttrPageContextTest = PerformanceManagerTestHarness;
+using ResourceAttrPageContextTest =
+    performance_manager::PerformanceManagerTestHarness;
 using ResourceAttrPageContextNoPMTest = content::RenderViewHostTestHarness;
 
 TEST_F(ResourceAttrPageContextTest, PageContexts) {
@@ -37,7 +39,7 @@
   base::WeakPtr<PageNode> page_node = page_context->GetWeakPageNode();
   base::WeakPtr<PageNode> page_node_from_pm =
       PerformanceManager::GetPrimaryPageNodeForWebContents(web_contents.get());
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     ASSERT_TRUE(page_node);
     ASSERT_TRUE(page_node_from_pm);
     EXPECT_EQ(page_node.get(), page_node_from_pm.get());
@@ -69,7 +71,7 @@
   web_contents.reset();
 
   EXPECT_EQ(nullptr, page_context->GetWebContents());
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     EXPECT_FALSE(page_node);
     EXPECT_EQ(nullptr, page_context->GetPageNode());
     EXPECT_EQ(std::nullopt, PageContext::FromWeakPageNode(page_node));
@@ -95,12 +97,13 @@
   std::unique_ptr<content::WebContents> web_contents = CreateTestWebContents();
 
   // Verify that PM didn't see the page.
-  RunInGraph([node = PerformanceManager::GetPrimaryPageNodeForWebContents(
-                  web_contents.get())] { EXPECT_FALSE(node); });
+  performance_manager::RunInGraph(
+      [node = PerformanceManager::GetPrimaryPageNodeForWebContents(
+           web_contents.get())] { EXPECT_FALSE(node); });
 
   EXPECT_FALSE(PageContext::FromWebContents(web_contents.get()));
 }
 
 }  // namespace
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
diff --git a/components/performance_manager/resource_attribution/performance_manager_aliases.h b/components/performance_manager/resource_attribution/performance_manager_aliases.h
new file mode 100644
index 0000000..304b69d
--- /dev/null
+++ b/components/performance_manager/resource_attribution/performance_manager_aliases.h
@@ -0,0 +1,51 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PERFORMANCE_MANAGER_RESOURCE_ATTRIBUTION_PERFORMANCE_MANAGER_ALIASES_H_
+#define COMPONENTS_PERFORMANCE_MANAGER_RESOURCE_ATTRIBUTION_PERFORMANCE_MANAGER_ALIASES_H_
+
+#include "components/performance_manager/public/browser_child_process_host_id.h"
+#include "components/performance_manager/public/render_process_host_id.h"
+
+// This header imports performance_manager symbols that are commonly used in the
+// resource attribution implementation into the resource_attribution namespace,
+// for convenience. It should never be included in header files that can be
+// included outside components/performance_manager/resource_attribution.
+
+namespace performance_manager {
+
+class FrameNode;
+class FrameNodeImpl;
+class Graph;
+class GraphImpl;
+class PageNode;
+class PageNodeImpl;
+class PerformanceManager;
+class ProcessNode;
+class ProcessNodeImpl;
+class WorkerNode;
+class WorkerNodeImpl;
+
+}  // namespace performance_manager
+
+namespace resource_attribution {
+
+using BrowserChildProcessHostId =
+    performance_manager::BrowserChildProcessHostId;
+using FrameNode = performance_manager::FrameNode;
+using FrameNodeImpl = performance_manager::FrameNodeImpl;
+using Graph = performance_manager::Graph;
+using GraphImpl = performance_manager::GraphImpl;
+using PageNode = performance_manager::PageNode;
+using PageNodeImpl = performance_manager::PageNodeImpl;
+using PerformanceManager = performance_manager::PerformanceManager;
+using ProcessNode = performance_manager::ProcessNode;
+using ProcessNodeImpl = performance_manager::ProcessNodeImpl;
+using RenderProcessHostId = performance_manager::RenderProcessHostId;
+using WorkerNode = performance_manager::WorkerNode;
+using WorkerNodeImpl = performance_manager::WorkerNodeImpl;
+
+}  // namespace resource_attribution
+
+#endif  // COMPONENTS_PERFORMANCE_MANAGER_RESOURCE_ATTRIBUTION_PERFORMANCE_MANAGER_ALIASES_H_
diff --git a/components/performance_manager/resource_attribution/process_context.cc b/components/performance_manager/resource_attribution/process_context.cc
index 4cb325f..e9fb2cd 100644
--- a/components/performance_manager/resource_attribution/process_context.cc
+++ b/components/performance_manager/resource_attribution/process_context.cc
@@ -17,13 +17,14 @@
 #include "components/performance_manager/public/graph/graph.h"
 #include "components/performance_manager/public/performance_manager.h"
 #include "components/performance_manager/public/render_process_host_id.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 #include "content/public/browser/browser_child_process_host.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/child_process_data.h"
 #include "content/public/browser/render_process_host.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 ProcessContext::ProcessContext(AnyProcessHostId id,
                                base::WeakPtr<ProcessNode> weak_node)
@@ -189,4 +190,4 @@
       id_);
 }
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
diff --git a/components/performance_manager/resource_attribution/process_context_unittest.cc b/components/performance_manager/resource_attribution/process_context_unittest.cc
index fa872058..c7e6b46d 100644
--- a/components/performance_manager/resource_attribution/process_context_unittest.cc
+++ b/components/performance_manager/resource_attribution/process_context_unittest.cc
@@ -8,10 +8,10 @@
 #include <optional>
 
 #include "base/memory/weak_ptr.h"
-#include "components/performance_manager/performance_manager_registry_impl.h"
 #include "components/performance_manager/public/graph/process_node.h"
 #include "components/performance_manager/public/performance_manager.h"
 #include "components/performance_manager/public/render_process_host_id.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 #include "components/performance_manager/test_support/performance_manager_test_harness.h"
 #include "components/performance_manager/test_support/run_in_graph.h"
 #include "components/performance_manager/test_support/test_browser_child_process.h"
@@ -26,11 +26,14 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 namespace {
 
-using ResourceAttrProcessContextTest = PerformanceManagerTestHarness;
+using TestBrowserChildProcess = performance_manager::TestBrowserChildProcess;
+
+using ResourceAttrProcessContextTest =
+    performance_manager::PerformanceManagerTestHarness;
 using ResourceAttrProcessContextNoPMTest = content::RenderViewHostTestHarness;
 
 TEST_F(ResourceAttrProcessContextTest, BrowserProcessContext) {
@@ -52,7 +55,7 @@
       process_context->GetWeakProcessNode();
   base::WeakPtr<ProcessNode> process_node_from_pm =
       PerformanceManager::GetProcessNodeForBrowserProcess();
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     ASSERT_TRUE(process_node);
     ASSERT_TRUE(process_node_from_pm);
     EXPECT_EQ(process_node.get(), process_node_from_pm.get());
@@ -65,10 +68,10 @@
               ProcessContext::FromWeakProcessNode(process_node));
   });
 
-  DeleteBrowserProcessNodeForTesting();
+  performance_manager::DeleteBrowserProcessNodeForTesting();
 
   EXPECT_TRUE(process_context->IsBrowserProcessContext());
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     EXPECT_FALSE(process_node);
     EXPECT_EQ(nullptr, process_context->GetProcessNode());
     EXPECT_EQ(std::nullopt, ProcessContext::FromWeakProcessNode(process_node));
@@ -103,7 +106,7 @@
       process_context->GetWeakProcessNode();
   base::WeakPtr<ProcessNode> process_node_from_pm =
       PerformanceManager::GetProcessNodeForRenderProcessHost(rph);
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     ASSERT_TRUE(process_node);
     ASSERT_TRUE(process_node_from_pm);
     EXPECT_EQ(process_node.get(), process_node_from_pm.get());
@@ -137,7 +140,7 @@
   EXPECT_EQ(nullptr, process_context->GetRenderProcessHost());
   EXPECT_EQ(rph_id, process_context->GetRenderProcessHostId());
 
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     EXPECT_FALSE(process_node);
     EXPECT_EQ(nullptr, process_context->GetProcessNode());
     EXPECT_EQ(std::nullopt, ProcessContext::FromWeakProcessNode(process_node));
@@ -168,7 +171,7 @@
   base::WeakPtr<ProcessNode> process_node_from_pm =
       PerformanceManager::GetProcessNodeForBrowserChildProcessHost(
           utility_process->host());
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     ASSERT_TRUE(process_node);
     ASSERT_TRUE(process_node_from_pm);
     EXPECT_EQ(process_node.get(), process_node_from_pm.get());
@@ -196,7 +199,7 @@
   EXPECT_EQ(nullptr, process_context->GetBrowserChildProcessHost());
   EXPECT_EQ(utility_id, process_context->GetBrowserChildProcessHostId());
 
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     EXPECT_FALSE(process_node);
     EXPECT_EQ(nullptr, process_context->GetProcessNode());
     EXPECT_EQ(std::nullopt, ProcessContext::FromWeakProcessNode(process_node));
@@ -225,4 +228,4 @@
 
 }  // namespace
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
diff --git a/components/performance_manager/resource_attribution/queries.cc b/components/performance_manager/resource_attribution/queries.cc
index 93d6716..d97fe5c 100644
--- a/components/performance_manager/resource_attribution/queries.cc
+++ b/components/performance_manager/resource_attribution/queries.cc
@@ -15,7 +15,7 @@
 #include "components/performance_manager/resource_attribution/query_params.h"
 #include "components/performance_manager/resource_attribution/query_scheduler.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 namespace {
 
@@ -305,4 +305,4 @@
   CHECK(!params_->resource_types.Empty());
 }
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
diff --git a/components/performance_manager/resource_attribution/queries_unittest.cc b/components/performance_manager/resource_attribution/queries_unittest.cc
index 9bb9b1c..31d2580 100644
--- a/components/performance_manager/resource_attribution/queries_unittest.cc
+++ b/components/performance_manager/resource_attribution/queries_unittest.cc
@@ -28,6 +28,7 @@
 #include "components/performance_manager/public/resource_attribution/resource_contexts.h"
 #include "components/performance_manager/public/resource_attribution/resource_types.h"
 #include "components/performance_manager/resource_attribution/context_collection.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 #include "components/performance_manager/resource_attribution/query_params.h"
 #include "components/performance_manager/resource_attribution/query_scheduler.h"
 #include "components/performance_manager/test_support/graph_test_harness.h"
@@ -44,7 +45,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 namespace {
 
@@ -74,13 +75,14 @@
 using MockQueryResultObserver =
     ::testing::StrictMock<LenientMockQueryResultObserver>;
 
-using ResourceAttrQueriesTest = GraphTestHarness;
+using ResourceAttrQueriesTest = performance_manager::GraphTestHarness;
 
 // Tests that interact with the QueryScheduler use PerformanceManagerTestHarness
 // to test its interactions on the PM sequence.
-class ResourceAttrQueriesPMTest : public PerformanceManagerTestHarness {
+class ResourceAttrQueriesPMTest
+    : public performance_manager::PerformanceManagerTestHarness {
  protected:
-  using Super = PerformanceManagerTestHarness;
+  using Super = performance_manager::PerformanceManagerTestHarness;
 
   ResourceAttrQueriesPMTest()
       : Super(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
@@ -89,7 +91,7 @@
     GetGraphFeatures().EnableResourceAttributionScheduler();
     Super::SetUp();
 
-    RunInGraph([&](Graph* graph) {
+    performance_manager::RunInGraph([&](Graph* graph) {
       graph_ = graph;
       CPUMeasurementDelegate::SetDelegateFactoryForTesting(
           graph, &cpu_delegate_factory_);
@@ -181,7 +183,7 @@
 }  // namespace
 
 TEST_F(ResourceAttrQueriesTest, QueryBuilder_Params) {
-  MockSinglePageInSingleProcessGraph mock_graph(graph());
+  performance_manager::MockSinglePageInSingleProcessGraph mock_graph(graph());
 
   QueryBuilder builder;
   ASSERT_TRUE(builder.GetParamsForTesting());
@@ -210,7 +212,7 @@
 }
 
 TEST_F(ResourceAttrQueriesTest, QueryBuilder_Clone) {
-  MockSinglePageInSingleProcessGraph mock_graph(graph());
+  performance_manager::MockSinglePageInSingleProcessGraph mock_graph(graph());
   QueryBuilder builder;
   builder.AddResourceContext(mock_graph.page->GetResourceContext())
       .AddAllContextsOfType<FrameContext>()
@@ -312,7 +314,7 @@
 
   // Create the query on the graph sequence, but tell it to run the result
   // callback on the main thread.
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     QueryBuilder()
         .AddResourceContext(main_frame_context())
         .AddResourceType(ResourceType::kMemorySummary)
@@ -326,7 +328,7 @@
 
 TEST_F(ResourceAttrQueriesPMTest, AddRemoveScopedQuery) {
   QueryScheduler* scheduler = nullptr;
-  RunInGraph([&](Graph* graph) {
+  performance_manager::RunInGraph([&](Graph* graph) {
     scheduler = QueryScheduler::GetFromGraph(graph);
     ASSERT_TRUE(scheduler);
     EXPECT_EQ(scheduler->GetQueryCountForTesting(ResourceType::kCPUTime), 0U);
@@ -341,7 +343,7 @@
           .AddResourceContext(main_frame_context())
           .AddResourceType(ResourceType::kMemorySummary)
           .CreateScopedQuery();
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     EXPECT_EQ(scheduler->GetQueryCountForTesting(ResourceType::kCPUTime), 0U);
     EXPECT_EQ(scheduler->GetQueryCountForTesting(ResourceType::kMemorySummary),
               1U);
@@ -351,13 +353,13 @@
           .AddResourceContext(main_frame_context())
           .AddResourceType(ResourceType::kCPUTime)
           .CreateScopedQuery();
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     EXPECT_EQ(scheduler->GetQueryCountForTesting(ResourceType::kCPUTime), 1U);
     EXPECT_EQ(scheduler->GetQueryCountForTesting(ResourceType::kMemorySummary),
               1U);
   });
   scoped_memory_query.reset();
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     EXPECT_EQ(scheduler->GetQueryCountForTesting(ResourceType::kCPUTime), 1U);
     EXPECT_EQ(scheduler->GetQueryCountForTesting(ResourceType::kMemorySummary),
               0U);
@@ -368,19 +370,19 @@
           .AddResourceType(ResourceType::kCPUTime)
           .AddResourceType(ResourceType::kMemorySummary)
           .CreateScopedQuery();
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     EXPECT_EQ(scheduler->GetQueryCountForTesting(ResourceType::kCPUTime), 2U);
     EXPECT_EQ(scheduler->GetQueryCountForTesting(ResourceType::kMemorySummary),
               1U);
   });
   scoped_cpu_query.reset();
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     EXPECT_EQ(scheduler->GetQueryCountForTesting(ResourceType::kCPUTime), 1U);
     EXPECT_EQ(scheduler->GetQueryCountForTesting(ResourceType::kMemorySummary),
               1U);
   });
   scoped_cpu_memory_query.reset();
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     EXPECT_EQ(scheduler->GetQueryCountForTesting(ResourceType::kCPUTime), 0U);
     EXPECT_EQ(scheduler->GetQueryCountForTesting(ResourceType::kMemorySummary),
               0U);
@@ -389,7 +391,7 @@
 
 TEST_F(ResourceAttrQueriesPMTest, ScopedQueryIsMovable) {
   QueryScheduler* scheduler = nullptr;
-  RunInGraph([&](Graph* graph) {
+  performance_manager::RunInGraph([&](Graph* graph) {
     scheduler = QueryScheduler::GetFromGraph(graph);
     ASSERT_TRUE(scheduler);
     EXPECT_EQ(scheduler->GetQueryCountForTesting(ResourceType::kCPUTime), 0U);
@@ -404,7 +406,7 @@
             .AddResourceContext(main_frame_context())
             .AddResourceType(ResourceType::kCPUTime)
             .CreateScopedQuery();
-    RunInGraph([&] {
+    performance_manager::RunInGraph([&] {
       EXPECT_EQ(scheduler->GetQueryCountForTesting(ResourceType::kCPUTime), 1U);
     });
 
@@ -426,11 +428,11 @@
   }
 
   // `inner_query` should not notify the scheduler when it goes out of scope.
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     EXPECT_EQ(scheduler->GetQueryCountForTesting(ResourceType::kCPUTime), 1U);
   });
   outer_query.reset();
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     EXPECT_EQ(scheduler->GetQueryCountForTesting(ResourceType::kCPUTime), 0U);
   });
 }
@@ -453,7 +455,7 @@
 
   // Post an empty task to the graph sequence to give time for the query to run
   // there. Nothing should happen.
-  RunInGraph([] {});
+  performance_manager::RunInGraph([] {});
 
   // Observer can be notified from the graph sequence when installed on any
   // thread.
@@ -463,7 +465,7 @@
 
   MockQueryResultObserver graph_sequence_observer;
   scoped_refptr<base::SequencedTaskRunner> graph_sequence_task_runner;
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     scoped_query.AddObserver(&graph_sequence_observer);
     graph_sequence_task_runner = base::SequencedTaskRunner::GetCurrentDefault();
   });
@@ -776,11 +778,12 @@
   // From the PM sequence (after all queued queries), post a task back to the
   // main thread (arrives after all query results). Wait for the task to be sure
   // all notifications are delivered.
-  RunInGraph([task_runner = base::SequencedTaskRunner::GetCurrentDefault(),
-              quit_closure = task_environment()->QuitClosure()] {
-    task_runner->PostTask(FROM_HERE, std::move(quit_closure));
-  });
+  performance_manager::RunInGraph(
+      [task_runner = base::SequencedTaskRunner::GetCurrentDefault(),
+       quit_closure = task_environment()->QuitClosure()] {
+        task_runner->PostTask(FROM_HERE, std::move(quit_closure));
+      });
   task_environment()->RunUntilQuit();
 }
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
diff --git a/components/performance_manager/resource_attribution/query_params.cc b/components/performance_manager/resource_attribution/query_params.cc
index a0d32546..e1df302 100644
--- a/components/performance_manager/resource_attribution/query_params.cc
+++ b/components/performance_manager/resource_attribution/query_params.cc
@@ -4,7 +4,7 @@
 
 #include "components/performance_manager/resource_attribution/query_params.h"
 
-namespace performance_manager::resource_attribution::internal {
+namespace resource_attribution::internal {
 
 QueryParams::QueryParams() = default;
 
@@ -14,4 +14,4 @@
 
 QueryParams& QueryParams::operator=(const QueryParams& other) = default;
 
-}  // namespace performance_manager::resource_attribution::internal
+}  // namespace resource_attribution::internal
diff --git a/components/performance_manager/resource_attribution/query_params.h b/components/performance_manager/resource_attribution/query_params.h
index 6d26c46..7021ca5 100644
--- a/components/performance_manager/resource_attribution/query_params.h
+++ b/components/performance_manager/resource_attribution/query_params.h
@@ -8,7 +8,7 @@
 #include "components/performance_manager/public/resource_attribution/resource_types.h"
 #include "components/performance_manager/resource_attribution/context_collection.h"
 
-namespace performance_manager::resource_attribution::internal {
+namespace resource_attribution::internal {
 
 struct QueryParams {
   QueryParams();
@@ -27,6 +27,6 @@
   ContextCollection contexts;
 };
 
-}  // namespace performance_manager::resource_attribution::internal
+}  // namespace resource_attribution::internal
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_RESOURCE_ATTRIBUTION_QUERY_PARAMS_H_
diff --git a/components/performance_manager/resource_attribution/query_scheduler.cc b/components/performance_manager/resource_attribution/query_scheduler.cc
index 6935f8d..1e16d7e 100644
--- a/components/performance_manager/resource_attribution/query_scheduler.cc
+++ b/components/performance_manager/resource_attribution/query_scheduler.cc
@@ -22,9 +22,10 @@
 #include "components/performance_manager/public/graph/node_data_describer_registry.h"
 #include "components/performance_manager/public/resource_attribution/resource_types.h"
 #include "components/performance_manager/resource_attribution/context_collection.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 #include "components/performance_manager/resource_attribution/query_params.h"
 
-namespace performance_manager::resource_attribution::internal {
+namespace resource_attribution::internal {
 
 namespace {
 
@@ -313,4 +314,4 @@
   std::move(callback).Run(merged_results);
 }
 
-}  // namespace performance_manager::resource_attribution::internal
+}  // namespace resource_attribution::internal
diff --git a/components/performance_manager/resource_attribution/query_scheduler.h b/components/performance_manager/resource_attribution/query_scheduler.h
index 8484d3b..7eed1bf0 100644
--- a/components/performance_manager/resource_attribution/query_scheduler.h
+++ b/components/performance_manager/resource_attribution/query_scheduler.h
@@ -21,19 +21,21 @@
 #include "components/performance_manager/public/resource_attribution/resource_types.h"
 #include "components/performance_manager/resource_attribution/cpu_measurement_monitor.h"
 #include "components/performance_manager/resource_attribution/memory_measurement_provider.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 class ContextCollection;
 }
 
-namespace performance_manager::resource_attribution::internal {
+namespace resource_attribution::internal {
 
 struct QueryParams;
 
 // QueryScheduler keeps track of all queries for a particular resource type and
 // owns the machinery that performs measurements.
-class QueryScheduler : public GraphRegisteredImpl<QueryScheduler>,
-                       public GraphOwned {
+class QueryScheduler
+    : public performance_manager::GraphRegisteredImpl<QueryScheduler>,
+      public performance_manager::GraphOwned {
  public:
   QueryScheduler();
   ~QueryScheduler() override;
@@ -114,6 +116,6 @@
   base::WeakPtrFactory<QueryScheduler> weak_factory_{this};
 };
 
-}  // namespace performance_manager::resource_attribution::internal
+}  // namespace resource_attribution::internal
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_RESOURCE_ATTRIBUTION_QUERY_SCHEDULER_H_
diff --git a/components/performance_manager/resource_attribution/query_scheduler_unittest.cc b/components/performance_manager/resource_attribution/query_scheduler_unittest.cc
index f051776..fb909a1a 100644
--- a/components/performance_manager/resource_attribution/query_scheduler_unittest.cc
+++ b/components/performance_manager/resource_attribution/query_scheduler_unittest.cc
@@ -26,6 +26,7 @@
 #include "components/performance_manager/public/resource_attribution/resource_types.h"
 #include "components/performance_manager/resource_attribution/context_collection.h"
 #include "components/performance_manager/resource_attribution/cpu_measurement_monitor.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 #include "components/performance_manager/resource_attribution/query_params.h"
 #include "components/performance_manager/test_support/graph_test_harness.h"
 #include "components/performance_manager/test_support/mock_graphs.h"
@@ -36,7 +37,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace performance_manager::resource_attribution::internal {
+namespace resource_attribution::internal {
 
 namespace {
 
@@ -71,9 +72,10 @@
 
 }  // namespace
 
-class ResourceAttrQuerySchedulerTest : public GraphTestHarness {
+class ResourceAttrQuerySchedulerTest
+    : public performance_manager::GraphTestHarness {
  protected:
-  using Super = GraphTestHarness;
+  using Super = performance_manager::GraphTestHarness;
 
   void SetUp() override {
     GetGraphFeatures().EnableResourceAttributionScheduler();
@@ -90,10 +92,12 @@
   FakeMemoryMeasurementDelegateFactory memory_delegate_factory_;
 };
 
-using ResourceAttrQuerySchedulerPMTest = PerformanceManagerTestHarness;
+using ResourceAttrQuerySchedulerPMTest =
+    performance_manager::PerformanceManagerTestHarness;
 
 TEST_F(ResourceAttrQuerySchedulerTest, AddRemoveQueries) {
-  MockMultiplePagesWithMultipleProcessesGraph mock_graph(graph());
+  performance_manager::MockMultiplePagesWithMultipleProcessesGraph mock_graph(
+      graph());
 
   // Install fake memory results for all processes.
   for (const ProcessNode* node :
@@ -171,7 +175,7 @@
   EXPECT_TRUE(PerformanceManager::IsAvailable());
   QueryScheduler* scheduler_ptr = nullptr;
   Graph* graph_ptr = nullptr;
-  RunInGraph([&](Graph* graph) {
+  performance_manager::RunInGraph([&](Graph* graph) {
     auto scheduler = std::make_unique<QueryScheduler>();
     scheduler_ptr = scheduler.get();
     graph_ptr = graph;
@@ -194,7 +198,7 @@
   // Tests that CallWithScheduler works from GraphTestHarness which doesn't set
   // up the PerformanceManager sequence. It's convenient to use GraphTestHarness
   // with mock graphs to test resource attribution queries.
-  EXPECT_FALSE(PerformanceManager::IsAvailable());
+  EXPECT_FALSE(performance_manager::PerformanceManager::IsAvailable());
   base::RunLoop run_loop;
   QueryScheduler::CallWithScheduler(
       base::BindLambdaForTesting([&](QueryScheduler* scheduler) {
@@ -204,4 +208,4 @@
   run_loop.Run();
 }
 
-}  // namespace performance_manager::resource_attribution::internal
+}  // namespace resource_attribution::internal
diff --git a/components/performance_manager/resource_attribution/resource_contexts_unittest.cc b/components/performance_manager/resource_attribution/resource_contexts_unittest.cc
index ac006a7..dbe7428 100644
--- a/components/performance_manager/resource_attribution/resource_contexts_unittest.cc
+++ b/components/performance_manager/resource_attribution/resource_contexts_unittest.cc
@@ -9,21 +9,23 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 namespace {
 
 template <typename PublicNode, typename NodeImpl>
-const PublicNode* ToPublic(const TestNodeWrapper<NodeImpl>& wrapper) {
+const PublicNode* ToPublic(
+    const performance_manager::TestNodeWrapper<NodeImpl>& wrapper) {
   return wrapper.get();
 }
 
-using ResourceAttrResourceContextsTest = GraphTestHarness;
+using ResourceAttrResourceContextsTest = performance_manager::GraphTestHarness;
 using ResourceAttrResourceContextsDeathTest = ResourceAttrResourceContextsTest;
 
 // Tests the context tokens returned from PM nodes.
 TEST_F(ResourceAttrResourceContextsTest, NodeContexts) {
-  MockUtilityAndMultipleRenderProcessesGraph mock_graph(graph());
+  performance_manager::MockUtilityAndMultipleRenderProcessesGraph mock_graph(
+      graph());
 
   // Test each type of ProcessNode (browser, renderer, non-renderer child) since
   // they use different constructors.
@@ -49,7 +51,8 @@
 }
 
 TEST_F(ResourceAttrResourceContextsTest, ResourceContextComparators) {
-  MockMultiplePagesAndWorkersWithMultipleProcessesGraph mock_graph(graph());
+  performance_manager::MockMultiplePagesAndWorkersWithMultipleProcessesGraph
+      mock_graph(graph());
 
   // Ensure tokens of the same type can be compared when wrapped in
   // ResourceContext.
@@ -87,9 +90,8 @@
 }
 
 TEST_F(ResourceAttrResourceContextsTest, ResourceContextConverters) {
-  using ::testing::Optional;
-
-  MockMultiplePagesAndWorkersWithMultipleProcessesGraph mock_graph(graph());
+  performance_manager::MockMultiplePagesAndWorkersWithMultipleProcessesGraph
+      mock_graph(graph());
 
   const ResourceContext process_context =
       mock_graph.process->GetResourceContext();
@@ -104,14 +106,15 @@
             mock_graph.process->GetResourceContext());
 
   EXPECT_THAT(AsOptionalContext<ProcessContext>(process_context),
-              Optional(mock_graph.process->GetResourceContext()));
+              ::testing::Optional(mock_graph.process->GetResourceContext()));
   EXPECT_EQ(AsOptionalContext<ProcessContext>(page_context), std::nullopt);
 }
 
 TEST_F(ResourceAttrResourceContextsTest, ResourceContextTypeId) {
   using ResourceContextTypeId = internal::ResourceContextTypeId;
 
-  MockMultiplePagesAndWorkersWithMultipleProcessesGraph mock_graph(graph());
+  performance_manager::MockMultiplePagesAndWorkersWithMultipleProcessesGraph
+      mock_graph(graph());
 
   const ResourceContext process_context =
       mock_graph.process->GetResourceContext();
@@ -132,7 +135,8 @@
 }
 
 TEST_F(ResourceAttrResourceContextsDeathTest, FailedResourceContextConverters) {
-  MockMultiplePagesAndWorkersWithMultipleProcessesGraph mock_graph(graph());
+  performance_manager::MockMultiplePagesAndWorkersWithMultipleProcessesGraph
+      mock_graph(graph());
   const ResourceContext page_context = mock_graph.page->GetResourceContext();
   EXPECT_DEATH_IF_SUPPORTED(AsContext<ProcessContext>(page_context),
                             "Bad variant access");
@@ -140,4 +144,4 @@
 
 }  // namespace
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
diff --git a/components/performance_manager/resource_attribution/type_helpers_unittest.cc b/components/performance_manager/resource_attribution/type_helpers_unittest.cc
index 430820d..32cf365 100644
--- a/components/performance_manager/resource_attribution/type_helpers_unittest.cc
+++ b/components/performance_manager/resource_attribution/type_helpers_unittest.cc
@@ -13,7 +13,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 
-namespace performance_manager::resource_attribution::internal {
+namespace resource_attribution::internal {
 
 namespace {
 
@@ -267,4 +267,4 @@
 
 }  // namespace
 
-}  // namespace performance_manager::resource_attribution::internal
+}  // namespace resource_attribution::internal
diff --git a/components/performance_manager/resource_attribution/worker_client_pages.cc b/components/performance_manager/resource_attribution/worker_client_pages.cc
index 02fb97b3a..58c70a03d 100644
--- a/components/performance_manager/resource_attribution/worker_client_pages.cc
+++ b/components/performance_manager/resource_attribution/worker_client_pages.cc
@@ -7,9 +7,10 @@
 #include "components/performance_manager/public/graph/frame_node.h"
 #include "components/performance_manager/public/graph/page_node.h"
 #include "components/performance_manager/public/graph/worker_node.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 namespace {
 
@@ -75,4 +76,4 @@
   return client_pages;
 }
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
diff --git a/components/performance_manager/resource_attribution/worker_client_pages.h b/components/performance_manager/resource_attribution/worker_client_pages.h
index 37a9fcd1..9532443 100644
--- a/components/performance_manager/resource_attribution/worker_client_pages.h
+++ b/components/performance_manager/resource_attribution/worker_client_pages.h
@@ -8,13 +8,9 @@
 #include <set>
 
 #include "components/performance_manager/resource_attribution/graph_change.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 
-namespace performance_manager {
-class PageNode;
-class WorkerNode;
-}  // namespace performance_manager
-
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 // Returns the complete set of pages that are clients of `worker_node`.
 // `graph_change` is a change to the graph topology in progress that may affect
@@ -23,6 +19,6 @@
     const WorkerNode* worker_node,
     GraphChange graph_change = NoGraphChange{});
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_RESOURCE_ATTRIBUTION_WORKER_CLIENT_PAGES_H_
diff --git a/components/performance_manager/resource_attribution/worker_context.cc b/components/performance_manager/resource_attribution/worker_context.cc
index be36bfb..c66d9f9 100644
--- a/components/performance_manager/resource_attribution/worker_context.cc
+++ b/components/performance_manager/resource_attribution/worker_context.cc
@@ -13,10 +13,11 @@
 #include "components/performance_manager/graph/worker_node_impl.h"
 #include "components/performance_manager/public/graph/graph.h"
 #include "components/performance_manager/public/performance_manager.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 #include "content/public/browser/browser_thread.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 WorkerContext::WorkerContext(const blink::WorkerToken& token,
                              base::WeakPtr<WorkerNode> weak_node)
@@ -86,4 +87,4 @@
   return base::StrCat({"WorkerContext:", token_.ToString()});
 }
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
diff --git a/components/performance_manager/resource_attribution/worker_context_unittest.cc b/components/performance_manager/resource_attribution/worker_context_unittest.cc
index 01f2ded..975b5f71 100644
--- a/components/performance_manager/resource_attribution/worker_context_unittest.cc
+++ b/components/performance_manager/resource_attribution/worker_context_unittest.cc
@@ -16,6 +16,7 @@
 #include "components/performance_manager/public/graph/graph.h"
 #include "components/performance_manager/public/graph/worker_node.h"
 #include "components/performance_manager/public/performance_manager.h"
+#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 #include "components/performance_manager/test_support/performance_manager_test_harness.h"
 #include "components/performance_manager/test_support/run_in_graph.h"
 #include "components/performance_manager/worker_watcher.h"
@@ -32,19 +33,20 @@
 #include "third_party/blink/public/common/tokens/tokens.h"
 #include "url/gurl.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 namespace {
 
-using ResourceAttrWorkerContextTest = PerformanceManagerTestHarness;
+using ResourceAttrWorkerContextTest =
+    performance_manager::PerformanceManagerTestHarness;
 using ResourceAttrWorkerContextNoPMTest = content::RenderViewHostTestHarness;
 
 TEST_F(ResourceAttrWorkerContextTest, WorkerContexts) {
   std::unique_ptr<content::WebContents> web_contents = CreateTestWebContents();
 
-  WorkerWatcher* worker_watcher =
-      PerformanceManagerRegistryImpl::GetInstance()->GetWorkerWatcherForTesting(
-          GetBrowserContext());
+  performance_manager::WorkerWatcher* worker_watcher =
+      performance_manager::PerformanceManagerRegistryImpl::GetInstance()
+          ->GetWorkerWatcherForTesting(GetBrowserContext());
   ASSERT_TRUE(worker_watcher);
 
   // Navigate to an initial page. This creates a TestFrameNode and
@@ -70,7 +72,7 @@
 
   // Validate that the right worker nodes were created, save a pointer to one.
   base::WeakPtr<WorkerNode> worker_node;
-  RunInGraph([&](Graph* graph) {
+  performance_manager::RunInGraph([&](Graph* graph) {
     bool found_worker = false;
     for (const WorkerNode* node : graph->GetAllWorkerNodes()) {
       EXPECT_THAT(node->GetWorkerToken(),
@@ -90,7 +92,7 @@
 
   base::WeakPtr<WorkerNode> worker_node_from_context =
       worker_context->GetWeakWorkerNode();
-  RunInGraph([&] {
+  performance_manager::RunInGraph([&] {
     ASSERT_TRUE(worker_node);
     ASSERT_TRUE(worker_node_from_context);
     EXPECT_EQ(worker_node.get(), worker_node_from_context.get());
@@ -114,7 +116,7 @@
   // Context still returns worker token, but it no longer matches any worker.
   EXPECT_EQ(worker_token, worker_context->GetWorkerToken());
   EXPECT_FALSE(WorkerContext::FromWorkerToken(worker_token).has_value());
-  RunInGraph([&](Graph* graph) {
+  performance_manager::RunInGraph([&](Graph* graph) {
     EXPECT_TRUE(graph->GetAllWorkerNodes().empty());
     EXPECT_FALSE(worker_node);
     EXPECT_EQ(nullptr, worker_context->GetWorkerNode());
@@ -132,4 +134,4 @@
 
 }  // namespace
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
diff --git a/components/performance_manager/test_support/resource_attribution/gtest_util.h b/components/performance_manager/test_support/resource_attribution/gtest_util.h
index 9dffbf2..8545891 100644
--- a/components/performance_manager/test_support/resource_attribution/gtest_util.h
+++ b/components/performance_manager/test_support/resource_attribution/gtest_util.h
@@ -13,7 +13,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 namespace internal {
 
@@ -203,6 +203,6 @@
   *os << context.ToString();
 }
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_TEST_SUPPORT_RESOURCE_ATTRIBUTION_GTEST_UTIL_H_
diff --git a/components/performance_manager/test_support/resource_attribution/measurement_delegates.cc b/components/performance_manager/test_support/resource_attribution/measurement_delegates.cc
index 659fc08..6d1b644 100644
--- a/components/performance_manager/test_support/resource_attribution/measurement_delegates.cc
+++ b/components/performance_manager/test_support/resource_attribution/measurement_delegates.cc
@@ -13,7 +13,10 @@
 #include "components/performance_manager/public/graph/process_node.h"
 #include "content/public/common/process_type.h"
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
+
+using Graph = performance_manager::Graph;
+using ProcessNode = performance_manager::ProcessNode;
 
 SimulatedCPUMeasurementDelegateFactory::
     SimulatedCPUMeasurementDelegateFactory() = default;
@@ -161,4 +164,4 @@
   std::move(callback).Run(factory_->memory_summaries());
 }
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
diff --git a/components/performance_manager/test_support/resource_attribution/measurement_delegates.h b/components/performance_manager/test_support/resource_attribution/measurement_delegates.h
index bbddfad..91ed57ab 100644
--- a/components/performance_manager/test_support/resource_attribution/measurement_delegates.h
+++ b/components/performance_manager/test_support/resource_attribution/measurement_delegates.h
@@ -22,7 +22,7 @@
 class ProcessNode;
 }
 
-namespace performance_manager::resource_attribution {
+namespace resource_attribution {
 
 class SimulatedCPUMeasurementDelegate;
 
@@ -71,14 +71,16 @@
   // Returns a delegate for `process_node`. This can be used to set initial
   // values on delegates before the code under test gets a pointer to them using
   // CreateDelegateForProcess().
-  SimulatedCPUMeasurementDelegate& GetDelegate(const ProcessNode* process_node);
+  SimulatedCPUMeasurementDelegate& GetDelegate(
+      const performance_manager::ProcessNode* process_node);
 
   // CPUMeasurementDelegate::Factory:
 
-  bool ShouldMeasureProcess(const ProcessNode* process_node) final;
+  bool ShouldMeasureProcess(
+      const performance_manager::ProcessNode* process_node) final;
 
   std::unique_ptr<CPUMeasurementDelegate> CreateDelegateForProcess(
-      const ProcessNode* process_node) final;
+      const performance_manager::ProcessNode* process_node) final;
 
   // Private implementation guarded by PassKey:
 
@@ -97,12 +99,14 @@
   // Map of ProcessNode to CPUMeasurementDelegate that simulates that process.
   // The delegates are owned by `pending_cpu_delegates_` when they're created,
   // then ownership is passed to the caller of TakeDelegate().
-  std::map<const ProcessNode*, SimulatedCPUMeasurementDelegate*>
+  std::map<const performance_manager::ProcessNode*,
+           SimulatedCPUMeasurementDelegate*>
       simulated_cpu_delegates_;
 
   // CPUMeasurementDelegates that have been created but not passed to the caller
   // of TakeDelegate() yet.
-  std::map<const ProcessNode*, std::unique_ptr<SimulatedCPUMeasurementDelegate>>
+  std::map<const performance_manager::ProcessNode*,
+           std::unique_ptr<SimulatedCPUMeasurementDelegate>>
       pending_cpu_delegates_;
 
   base::WeakPtrFactory<SimulatedCPUMeasurementDelegateFactory> weak_factory_{
@@ -187,7 +191,8 @@
 
   // MemoryMeasurementDelegate::Factory:
 
-  std::unique_ptr<MemoryMeasurementDelegate> CreateDelegate(Graph*) final;
+  std::unique_ptr<MemoryMeasurementDelegate> CreateDelegate(
+      performance_manager::Graph*) final;
 
  private:
   // The MemorySummary results returned by delegates created by this factory.
@@ -225,6 +230,6 @@
   base::SafeRef<FakeMemoryMeasurementDelegateFactory> factory_;
 };
 
-}  // namespace performance_manager::resource_attribution
+}  // namespace resource_attribution
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_TEST_SUPPORT_RESOURCE_ATTRIBUTION_MEASUREMENT_DELEGATES_H_
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index ec5df4b..d384b97 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -3161,6 +3161,18 @@
   optional SignedData signed_request = 1;
 }
 
+// Request from device to server to register a device (authenticated with an
+// enrollment token).
+message TokenBasedDeviceRegisterRequest {
+  optional DeviceRegisterRequest device_register_request = 1;
+}
+
+// Response to a token-based device registration request
+// (@code `TokenBasedDeviceRegisterRequest`).
+message TokenBasedDeviceRegisterResponse {
+  optional DeviceRegisterResponse device_register_response = 1;
+}
+
 // Requested configuration to be passed along a registration request.
 message DeviceRegisterConfiguration {
   // The device owner's email address.
@@ -4530,7 +4542,7 @@
 //   * deviceid: MUST BE no more than 64-char in [\x21-\x7E].
 //   * agent: MUST BE no more than 64-char long.
 // * HTTP Authorization header MUST be in the following formats:
-//   * For register for Chrome browsers
+//   * For register for Chrome browsers and token_based_register requests.
 //     Authorization: GoogleEnrollmentToken token=<enrollment token>
 //
 //   * For unregister, policy, status, cert_upload, remote_commands,
@@ -4715,7 +4727,12 @@
   // HTTP query parameter 'request': 'chrome_profile_report'
   optional ChromeProfileReportRequest chrome_profile_report_request = 40;
 
-  // Next id: 41.
+  // Request to register a device with an enrollment token.
+  // HTTP query parameter 'request': 'token_based_register'
+  optional TokenBasedDeviceRegisterRequest token_based_device_register_request =
+      41;
+
+  // Next id: 42.
 }
 
 // Response from server to device.
@@ -4728,7 +4745,8 @@
 // Possible HTTP status codes:
 // 200 OK: valid response is returned to client.
 // 400 Bad Request: invalid argument or payload.
-// 401 Unauthorized: invalid auth cookie/OAuth token or DMToken.
+// 401 Unauthorized: invalid auth cookie/OAuth token, DMToken or enrollment
+//     token.
 // 402 Missing license.
 // 403 Forbidden: device management is not allowed (e.g. user may not enroll).
 // 404 Not Found: the request URL is invalid.
@@ -4870,7 +4888,11 @@
   // Response to a Chrome Profile report request.
   optional ChromeProfileReportResponse chrome_profile_report_response = 38;
 
-  // Next id: 40.
+  // Response to a token-based register request.
+  optional TokenBasedDeviceRegisterResponse
+      token_based_device_register_response = 40;
+
+  // Next id: 41.
 }
 
 // Device State Information stored in the server is retrieval at
diff --git a/components/policy/resources/templates/policies.yaml b/components/policy/resources/templates/policies.yaml
index f2ead13..50b0e059 100644
--- a/components/policy/resources/templates/policies.yaml
+++ b/components/policy/resources/templates/policies.yaml
@@ -1211,6 +1211,7 @@
   1210: RemoteAccessHostAllowUrlForwarding
   1211: ScreenCaptureLocation
   1212: AllowedDomainsForAppsList
+  1213: ChromeForTestingAllowed
 atomic_groups:
   1: Homepage
   2: RemoteAccess
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/ChromeForTestingAllowed.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/ChromeForTestingAllowed.yaml
new file mode 100644
index 0000000..7a9d06ba
--- /dev/null
+++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/ChromeForTestingAllowed.yaml
@@ -0,0 +1,25 @@
+caption: Allow Chrome for Testing
+default: true
+desc: |-
+  Controls whether users may use Chrome for Testing.
+
+        If this policy is set to Enabled or not set, users may install and run Chrome for Testing.
+
+        If this policy is set to Disabled, users are not allowed to run Chrome for Testing. Users will still be able to install Chrome for Testing, however it will not run with the profiles where this policy is set to Disabled.
+example_value: true
+features:
+  dynamic_refresh: false
+  per_profile: false
+items:
+- caption: Allow use of the Chrome for Testing
+  value: true
+- caption: Do not allow use of the Chrome for Testing
+  value: false
+owners:
+- file://components/policy/OWNERS
+schema:
+  type: boolean
+supported_on:
+- chrome.*:123-
+tags: []
+type: main
diff --git a/components/policy/test/data/pref_mapping/ChromeForTestingAllowed.json b/components/policy/test/data/pref_mapping/ChromeForTestingAllowed.json
new file mode 100644
index 0000000..97b18a5
--- /dev/null
+++ b/components/policy/test/data/pref_mapping/ChromeForTestingAllowed.json
@@ -0,0 +1,22 @@
+[
+  {
+    "os": [
+      "linux",
+      "mac",
+      "win"
+    ],
+    "policy_pref_mapping_tests": [
+      {
+        "policies": {
+          "ChromeForTestingAllowed": true
+        },
+        "prefs": {
+          "chrome_for_testing.allowed": {
+            "location": "local_state",
+            "value": true
+          }
+        }
+      }
+    ]
+  }
+]
diff --git a/components/privacy_sandbox_strings.grdp b/components/privacy_sandbox_strings.grdp
index 0d1ac52..3ec0c14 100644
--- a/components/privacy_sandbox_strings.grdp
+++ b/components/privacy_sandbox_strings.grdp
@@ -239,6 +239,9 @@
   <message name="IDS_SETTINGS_TOPICS_PAGE_ALLOW_TOPIC_A11Y_LABEL" desc="A button label read by screen readers that indicates which topic will get added back to the pool of potential topics.">
     Add <ph name="TOPIC">$1<ex>Movies</ex></ph>
   </message>
+  <message name="IDS_SETTINGS_TOPICS_PAGE_UNBLOCK_TOPIC_A11Y_LABEL" desc="A button label read by screen readers that indicates which topic will get unblocked back to the pool of potential topics." translateable="false">
+    Unblock <ph name="TOPIC">$1<ex>Movies</ex></ph>
+  </message>
   <message name="IDS_SETTINGS_TOPICS_PAGE_LEARN_MORE_HEADING" desc="The title of a dialog box that offers more information about the Topics setting." formatter_data="android_java">
     More about ad topics
   </message>
diff --git a/components/qr_code_generator/qr_code_generator.cc b/components/qr_code_generator/qr_code_generator.cc
index 522c9a5..d0575ef 100644
--- a/components/qr_code_generator/qr_code_generator.cc
+++ b/components/qr_code_generator/qr_code_generator.cc
@@ -14,11 +14,13 @@
 
 namespace qr_code_generator {
 
-namespace {
+GeneratedCode::GeneratedCode() = default;
+GeneratedCode::~GeneratedCode() = default;
+GeneratedCode::GeneratedCode(GeneratedCode&&) = default;
+GeneratedCode& GeneratedCode::operator=(GeneratedCode&&) = default;
 
-std::optional<QRCodeGenerator::GeneratedCode> GenerateQrCodeUsingRust(
-    base::span<const uint8_t> in,
-    std::optional<int> min_version) {
+std::optional<GeneratedCode> Generate(base::span<const uint8_t> in,
+                                      std::optional<int> min_version) {
   rust::Slice<const uint8_t> rs_in = base::SpanToRustSlice(in);
 
   // `min_version` might come from a fuzzer and therefore we use a lenient
@@ -34,30 +36,11 @@
   if (!result_is_success) {
     return std::nullopt;
   }
-  QRCodeGenerator::GeneratedCode code;
+  GeneratedCode code;
   code.data = std::move(result_pixels);
   code.qr_size = base::checked_cast<int>(result_width);
   CHECK_EQ(code.data.size(), static_cast<size_t>(code.qr_size * code.qr_size));
   return code;
 }
 
-}  // namespace
-
-QRCodeGenerator::QRCodeGenerator() = default;
-
-QRCodeGenerator::~QRCodeGenerator() = default;
-
-QRCodeGenerator::GeneratedCode::GeneratedCode() = default;
-QRCodeGenerator::GeneratedCode::GeneratedCode(
-    QRCodeGenerator::GeneratedCode&&) = default;
-QRCodeGenerator::GeneratedCode& QRCodeGenerator::GeneratedCode::operator=(
-    QRCodeGenerator::GeneratedCode&&) = default;
-QRCodeGenerator::GeneratedCode::~GeneratedCode() = default;
-
-std::optional<QRCodeGenerator::GeneratedCode> QRCodeGenerator::Generate(
-    base::span<const uint8_t> in,
-    std::optional<int> min_version) {
-  return GenerateQrCodeUsingRust(in, min_version);
-}
-
 }  // namespace qr_code_generator
diff --git a/components/qr_code_generator/qr_code_generator.h b/components/qr_code_generator/qr_code_generator.h
index fbefb8ad..971abf7 100644
--- a/components/qr_code_generator/qr_code_generator.h
+++ b/components/qr_code_generator/qr_code_generator.h
@@ -17,56 +17,45 @@
 
 namespace qr_code_generator {
 
-// QRCodeGenerator generates class M QR codes of various versions.
-// References in the following comments refer to ISO 18004 (3rd edition).
-class QRCodeGenerator {
+// Contains output data for Generate().
+// The default state contains no data.
+struct GeneratedCode {
  public:
-  // Contains output data for Generate().
-  // The default state contains no data.
-  struct GeneratedCode {
-   public:
-    GeneratedCode();
-    GeneratedCode(GeneratedCode&&);
-    GeneratedCode& operator=(GeneratedCode&&);
+  GeneratedCode();
+  GeneratedCode(GeneratedCode&&);
+  GeneratedCode& operator=(GeneratedCode&&);
 
-    GeneratedCode(const GeneratedCode&) = delete;
-    GeneratedCode& operator=(const GeneratedCode&) = delete;
+  GeneratedCode(const GeneratedCode&) = delete;
+  GeneratedCode& operator=(const GeneratedCode&) = delete;
 
-    ~GeneratedCode();
+  ~GeneratedCode();
 
-    // Pixel data.  The least-significant bit of each byte is set if that
-    // tile/module should be "black".
-    //
-    // Clients should ensure four tiles/modules of padding when rendering the
-    // code.
-    //
-    // On error, will not be populated, and will contain an empty vector.
-    std::vector<uint8_t> data;
-
-    // Width and height (which are equal) of the generated data, in
-    // tiles/modules.
-    //
-    // The following invariant holds: `qr_size * qr_size == data.size()`.
-    //
-    // On error, will not be populated, and will contain 0.
-    int qr_size = 0;
-  };
-
-  QRCodeGenerator();
-  ~QRCodeGenerator();
-
-  // Generates a QR code containing the given data.
-  // The generator will attempt to choose a version that fits the data and which
-  // is >= |min_version|, if given. The returned span's length is
-  // input-dependent and not known at compile-time.
+  // Pixel data.  The least-significant bit of each byte is set if that
+  // tile/module should be "black".
   //
-  // TODO(https://crbug.com/1431991): Make this a free function + delete the
-  // `QRCodeGenerator` class.
-  std::optional<GeneratedCode> Generate(
-      base::span<const uint8_t> in,
-      std::optional<int> min_version = std::nullopt);
+  // Clients should ensure four tiles/modules of padding when rendering the
+  // code.
+  //
+  // On error, will not be populated, and will contain an empty vector.
+  std::vector<uint8_t> data;
+
+  // Width and height (which are equal) of the generated data, in
+  // tiles/modules.
+  //
+  // The following invariant holds: `qr_size * qr_size == data.size()`.
+  //
+  // On error, will not be populated, and will contain 0.
+  int qr_size = 0;
 };
 
+// Generates a QR code containing the given data.
+// The generator will attempt to choose a version that fits the data and which
+// is >= |min_version|, if given. The returned span's length is
+// input-dependent and not known at compile-time.
+std::optional<GeneratedCode> Generate(
+    base::span<const uint8_t> in,
+    std::optional<int> min_version = std::nullopt);
+
 }  // namespace qr_code_generator
 
 #endif  // COMPONENTS_QR_CODE_GENERATOR_QR_CODE_GENERATOR_H_
diff --git a/components/qr_code_generator/qr_code_generator_fuzzer.cc b/components/qr_code_generator/qr_code_generator_fuzzer.cc
index f4425db..7237fc0 100644
--- a/components/qr_code_generator/qr_code_generator_fuzzer.cc
+++ b/components/qr_code_generator/qr_code_generator_fuzzer.cc
@@ -16,7 +16,6 @@
   int min_version = provider.ConsumeIntegral<int>();
   auto qr_data = provider.ConsumeRemainingBytes<uint8_t>();
 
-  qr_code_generator::QRCodeGenerator qr;
-  std::ignore = qr.Generate(qr_data, min_version);
+  std::ignore = qr_code_generator::Generate(qr_data, min_version);
   return 0;
 }
diff --git a/components/qr_code_generator/qr_code_generator_unittest.cc b/components/qr_code_generator/qr_code_generator_unittest.cc
index e37b84b92..d1390ee 100644
--- a/components/qr_code_generator/qr_code_generator_unittest.cc
+++ b/components/qr_code_generator/qr_code_generator_unittest.cc
@@ -23,7 +23,6 @@
 
   constexpr size_t kMaxInputLen = 210;
   uint8_t input[kMaxInputLen];
-  QRCodeGenerator qr;
   std::optional<int> smallest_size;
   std::optional<int> largest_size;
 
@@ -35,8 +34,8 @@
     for (size_t input_len = 30; input_len < kMaxInputLen; input_len += 10) {
       SCOPED_TRACE(input_len);
 
-      std::optional<QRCodeGenerator::GeneratedCode> qr_code =
-          qr.Generate(base::span<const uint8_t>(input, input_len));
+      std::optional<GeneratedCode> qr_code =
+          Generate(base::span<const uint8_t>(input, input_len));
       ASSERT_NE(qr_code, std::nullopt);
       auto& qr_data = qr_code->data;
 
@@ -68,7 +67,6 @@
   // implementation. We are now shipping a memory-safe Rust implementation so we
   // are now testing only sizes up to 90 - this helps to avoid flaky test
   // timeouts.
-  QRCodeGenerator qr;
   std::string input = "";
   std::map<int, size_t> max_input_length_for_qr_size;
 
@@ -78,8 +76,7 @@
       break;
     }
 
-    std::optional<QRCodeGenerator::GeneratedCode> code =
-        qr.Generate(base::as_byte_span(input));
+    std::optional<GeneratedCode> code = Generate(base::as_byte_span(input));
     ASSERT_TRUE(code);
     max_input_length_for_qr_size[code->qr_size] = input.size();
   }
@@ -100,11 +97,9 @@
 // Test helper that returns `GeneratedCode::qr_size` or -1 if there was a
 // failure.
 int GenerateAndGetQrCodeSize(size_t input_size) {
-  QRCodeGenerator qr;
   std::string input(input_size, '!');
 
-  std::optional<QRCodeGenerator::GeneratedCode> code =
-      qr.Generate(base::as_byte_span(input));
+  std::optional<GeneratedCode> code = Generate(base::as_byte_span(input));
   return code.has_value() ? code->qr_size : -1;
 }
 
@@ -140,28 +135,26 @@
   std::vector<uint8_t> huge_binary_input(kMaxInputSizeForBinaryInputVersion40,
                                          '\0');
 
-  QRCodeGenerator qr;
 
   // The Rust implementation can generate QR codes up to version 40.
-  ASSERT_TRUE(qr.Generate(huge_numeric_input));
-  ASSERT_TRUE(qr.Generate(huge_binary_input));
+  ASSERT_TRUE(Generate(huge_numeric_input));
+  ASSERT_TRUE(Generate(huge_binary_input));
 
   // Adding another character means that the inputs will no longer fit into QR
   // code version 40 (as of year 2023 there are no further versions defined by
   // the spec).
   huge_numeric_input.push_back('0');
   huge_binary_input.push_back('\0');
-  ASSERT_FALSE(qr.Generate(huge_numeric_input));
-  ASSERT_FALSE(qr.Generate(huge_binary_input));
+  ASSERT_FALSE(Generate(huge_numeric_input));
+  ASSERT_FALSE(Generate(huge_binary_input));
 }
 
 TEST(QRCodeGeneratorTest, InvalidMinVersion) {
   std::vector<uint8_t> input(123);  // Arbitrary valid input.
-  QRCodeGenerator qr;
-  ASSERT_FALSE(qr.Generate(input, std::make_optional(41)));
+  ASSERT_FALSE(Generate(input, std::make_optional(41)));
   ASSERT_FALSE(
-      qr.Generate(input, std::make_optional(std::numeric_limits<int>::max())));
-  ASSERT_FALSE(qr.Generate(input, std::make_optional(-1)));
+      Generate(input, std::make_optional(std::numeric_limits<int>::max())));
+  ASSERT_FALSE(Generate(input, std::make_optional(-1)));
 }
 
 }  // namespace qr_code_generator
diff --git a/components/qr_code_generator/qr_print.cc b/components/qr_code_generator/qr_print.cc
index 7e7db597..0b33565 100644
--- a/components/qr_code_generator/qr_print.cc
+++ b/components/qr_code_generator/qr_print.cc
@@ -60,9 +60,9 @@
     std::swap(black, white);
   }
 
-  qr_code_generator::QRCodeGenerator generator;
-  std::optional<qr_code_generator::QRCodeGenerator::GeneratedCode> code =
-      generator.Generate(base::span<const uint8_t>(input, input_len), mask);
+  std::optional<qr_code_generator::GeneratedCode> code =
+      qr_code_generator::Generate(base::span<const uint8_t>(input, input_len),
+                                  mask);
   if (!code) {
     fprintf(STDERR, "Input too long to be encoded.\n");
     return 2;
diff --git a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h
index 085d9821..25672be 100644
--- a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h
+++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h
@@ -70,6 +70,10 @@
 // screen bounds.
 - (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen*)screen;
 
+// Is the window a part of a browser window tree that is currently in an
+// immersive fullscreen session.
+- (BOOL)immersiveFullscreen;
+
 // Identifier for the NativeWidgetMac from which this window was created. This
 // may be used to look up the NativeWidgetMacNSWindowHost in the browser process
 // or the NativeWidgetNSWindowBridge in a display process.
diff --git a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
index eeeaec7..4c60785c 100644
--- a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
+++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
@@ -793,19 +793,8 @@
     return;
   }
 
-  // Find the root window.
-  NSWindow* root = self;
-  while (root.parentWindow) {
-    root = root.parentWindow;
-  }
-
   // Only remove from groups if the browser is in immersive fullscreen.
-  NativeWidgetMacNSWindow* rootWidgetWindow =
-      base::apple::ObjCCast<NativeWidgetMacNSWindow>(root);
-  if (!rootWidgetWindow ||
-      !(rootWidgetWindow.styleMask & NSWindowStyleMaskFullScreen) ||
-      !rootWidgetWindow.bridge ||
-      !rootWidgetWindow.bridge->ImmersiveFullscreenEnabled()) {
+  if (![self immersiveFullscreen]) {
     return;
   }
 
@@ -820,7 +809,7 @@
   // Iterate instead of recurse. There are other NSWindow types in the tree
   // besides NativeWidgetMacNSWindow that would not implement our recursion.
   NSMutableArray* nextWindows = [NSMutableArray array];
-  [nextWindows addObject:root];
+  [nextWindows addObject:[self rootWindow]];
   while (nextWindows.count) {
     NSWindow* currentWindow = nextWindows.lastObject;
     [nextWindows removeLastObject];
@@ -831,4 +820,24 @@
   }
 }
 
+- (NSWindow*)rootWindow {
+  NSWindow* root = self;
+  while (root.parentWindow) {
+    root = root.parentWindow;
+  }
+  return root;
+}
+
+- (BOOL)immersiveFullscreen {
+  NativeWidgetMacNSWindow* rootWidgetWindow =
+      base::apple::ObjCCast<NativeWidgetMacNSWindow>([self rootWindow]);
+  if (rootWidgetWindow &&
+      (rootWidgetWindow.styleMask & NSWindowStyleMaskFullScreen) &&
+      rootWidgetWindow.bridge &&
+      rootWidgetWindow.bridge->ImmersiveFullscreenEnabled()) {
+    return YES;
+  }
+  return NO;
+}
+
 @end
diff --git a/components/remote_cocoa/app_shim/select_file_dialog_bridge.mm b/components/remote_cocoa/app_shim/select_file_dialog_bridge.mm
index 645f8df..824cfaa 100644
--- a/components/remote_cocoa/app_shim/select_file_dialog_bridge.mm
+++ b/components/remote_cocoa/app_shim/select_file_dialog_bridge.mm
@@ -20,6 +20,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/hang_watcher.h"
 #include "base/threading/thread_restrictions.h"
+#import "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 #include "ui/strings/grit/ui_strings.h"
 
@@ -410,6 +411,18 @@
   // Ensure that |callback| (rather than |this|) be retained by the block.
   auto ended_callback = base::BindRepeating(
       &SelectFileDialogBridge::OnPanelEnded, weak_factory_.GetWeakPtr());
+
+  // If the owning_window_ widget is currently in an immersive fullscreen
+  // session, add then remove the panel as a child. Otherwise the following
+  // -beginSheetModalForWindow:completionHandler: call will place the panel
+  // z-order behind the owning widget. See http://crbug/40282144.
+  NativeWidgetMacNSWindow* owning_window_widget =
+      base::apple::ObjCCast<NativeWidgetMacNSWindow>(owning_window_);
+  if (owning_window_widget && [owning_window_widget immersiveFullscreen]) {
+    [owning_window_ addChildWindow:panel_ ordered:NSWindowAbove];
+    [owning_window_ removeChildWindow:panel_];
+  }
+
   [panel_ beginSheetModalForWindow:owning_window_
                  completionHandler:^(NSInteger result) {
                    ended_callback.Run(result != NSModalResponseOK);
diff --git a/components/services/screen_ai/BUILD.gn b/components/services/screen_ai/BUILD.gn
index 03ab41eb..230b3c4f 100644
--- a/components/services/screen_ai/BUILD.gn
+++ b/components/services/screen_ai/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//components/services/screen_ai/buildflags/features.gni")
 import("//third_party/protobuf/proto_library.gni")
 
 source_set("screen_ai") {
@@ -13,7 +14,7 @@
     "screen_ai_service_impl.h",
   ]
 
-  if (is_msan) {
+  if (use_fake_screen_ai) {
     sources += [
       "screen_ai_library_wrapper_fake.cc",
       "screen_ai_library_wrapper_fake.h",
@@ -28,6 +29,7 @@
   public_deps = [ "//components/services/screen_ai/proto:proto_convertors" ]
 
   deps = [
+    "//components/services/screen_ai/buildflags",
     "//components/services/screen_ai/proto",
     "//components/services/screen_ai/public/cpp:utilities",
     "//components/services/screen_ai/public/mojom:factory",
diff --git a/components/services/screen_ai/buildflags/BUILD.gn b/components/services/screen_ai/buildflags/BUILD.gn
index dcddc5f..689f53e0 100644
--- a/components/services/screen_ai/buildflags/BUILD.gn
+++ b/components/services/screen_ai/buildflags/BUILD.gn
@@ -7,5 +7,9 @@
 
 buildflag_header("buildflags") {
   header = "buildflags.h"
-  flags = [ "ENABLE_SCREEN_AI_SERVICE=$enable_screen_ai_service" ]
+  flags = [
+    "ENABLE_SCREEN_AI_BROWSERTESTS=$enable_screen_ai_browsertests",
+    "ENABLE_SCREEN_AI_SERVICE=$enable_screen_ai_service",
+    "USE_FAKE_SCREEN_AI=$use_fake_screen_ai",
+  ]
 }
diff --git a/components/services/screen_ai/buildflags/features.gni b/components/services/screen_ai/buildflags/features.gni
index 2a9746d..a77599dc7 100644
--- a/components/services/screen_ai/buildflags/features.gni
+++ b/components/services/screen_ai/buildflags/features.gni
@@ -4,8 +4,16 @@
 
 import("//build/config/cast.gni")
 import("//build/config/chromeos/ui_mode.gni")
+import("//build/config/sanitizers/sanitizers.gni")
 
 declare_args() {
   # Screen AI service is only supported on desktop platforms.
   enable_screen_ai_service = is_linux || is_mac || is_chromeos || is_win
+
+  # Screen AI library is not yet available for browser tests on Windows and
+  #  ChromeOS.
+  enable_screen_ai_browsertests = is_linux || is_mac
+
+  # Screen AI library is not available for MSAN and UBSAN.
+  use_fake_screen_ai = is_msan || is_ubsan || is_ubsan_vptr || is_ubsan_security
 }
diff --git a/components/services/screen_ai/public/cpp/BUILD.gn b/components/services/screen_ai/public/cpp/BUILD.gn
index 21ace58b..c0ee600 100644
--- a/components/services/screen_ai/public/cpp/BUILD.gn
+++ b/components/services/screen_ai/public/cpp/BUILD.gn
@@ -11,6 +11,7 @@
   deps = [
     "//base",
     "//components/component_updater",
+    "//components/services/screen_ai/buildflags",
     "//ui/accessibility:ax_base",
   ]
 
diff --git a/components/services/screen_ai/public/cpp/utilities.cc b/components/services/screen_ai/public/cpp/utilities.cc
index baa110c3..c800fc1 100644
--- a/components/services/screen_ai/public/cpp/utilities.cc
+++ b/components/services/screen_ai/public/cpp/utilities.cc
@@ -11,6 +11,7 @@
 #include "base/path_service.h"
 #include "build/build_config.h"
 #include "components/component_updater/component_updater_paths.h"
+#include "components/services/screen_ai/buildflags/buildflags.h"
 #include "ui/accessibility/accessibility_features.h"
 
 namespace screen_ai {
@@ -32,11 +33,7 @@
     "/run/imageloader/screen-ai/package/root/";
 #endif
 
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC)
-#define PLATFORM_SUPPORTS_BROWSER_TESTS
-#endif
-
-#if defined(PLATFORM_SUPPORTS_BROWSER_TESTS)
+#if BUILDFLAG(ENABLE_SCREEN_AI_BROWSERTESTS)
 #if BUILDFLAG(IS_LINUX)
 constexpr base::FilePath::CharType kScreenAIResourcePathForTests[] =
     FILE_PATH_LITERAL("third_party/screen-ai/linux/resources");
@@ -72,7 +69,7 @@
   CHECK(base::PathExists(screenai_library_path));
   return screenai_library_path;
 }
-#endif  // defined(PLATFORM_SUPPORTS_BROWSER_TESTS)
+#endif  // BUILDFLAG(ENABLE_SCREEN_AI_BROWSERTESTS)
 
 }  // namespace
 
@@ -85,14 +82,14 @@
 }
 
 base::FilePath GetComponentDir() {
-#if defined(PLATFORM_SUPPORTS_BROWSER_TESTS)
+#if BUILDFLAG(ENABLE_SCREEN_AI_BROWSERTESTS)
   // When in `ScreenAITestMode`, return the path that contains the screen-ai
   // binary downloaded from CIPD.
   if (features::IsScreenAITestModeEnabled()) {
     CHECK_IS_TEST();
     return GetTestComponentDir();
   }
-#endif  // defined(PLATFORM_SUPPORTS_BROWSER_TESTS)
+#endif  // BUILDFLAG(ENABLE_SCREEN_AI_BROWSERTESTS)
 
   base::FilePath components_dir;
   if (!base::PathService::Get(component_updater::DIR_COMPONENT_USER,
@@ -105,12 +102,12 @@
 }
 
 base::FilePath GetLatestComponentBinaryPath() {
-#if defined(PLATFORM_SUPPORTS_BROWSER_TESTS)
+#if BUILDFLAG(ENABLE_SCREEN_AI_BROWSERTESTS)
   if (features::IsScreenAITestModeEnabled()) {
     CHECK_IS_TEST();
     return GetTestComponentBinaryPath();
   }
-#endif  // defined(PLATFORM_SUPPORTS_BROWSER_TESTS)
+#endif  // BUILDFLAG(ENABLE_SCREEN_AI_BROWSERTESTS)
 
   base::FilePath latest_version_dir;
 #if BUILDFLAG(IS_CHROMEOS)
@@ -143,12 +140,4 @@
   return component_path;
 }
 
-bool PlatformSupportsBrowserTests() {
-#if defined(PLATFORM_SUPPORTS_BROWSER_TESTS)
-  return true;
-#else
-  return false;
-#endif
-}
-
 }  // namespace screen_ai
diff --git a/components/services/screen_ai/public/cpp/utilities.h b/components/services/screen_ai/public/cpp/utilities.h
index 7f2df40..ce04784 100644
--- a/components/services/screen_ai/public/cpp/utilities.h
+++ b/components/services/screen_ai/public/cpp/utilities.h
@@ -21,8 +21,5 @@
 // Returns the file name of component binary.
 base::FilePath GetComponentBinaryFileName();
 
-// Returns true if the binary exists for browser tests on current platform.
-bool PlatformSupportsBrowserTests();
-
 }  // namespace screen_ai
 #endif  // COMPONENTS_SERVICES_SCREEN_AI_PUBLIC_CPP_UTILITIES_H_
diff --git a/components/services/screen_ai/screen_ai_service_impl.cc b/components/services/screen_ai/screen_ai_service_impl.cc
index 1b60536..96ea3f9 100644
--- a/components/services/screen_ai/screen_ai_service_impl.cc
+++ b/components/services/screen_ai/screen_ai_service_impl.cc
@@ -16,6 +16,7 @@
 #include "base/process/process.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/task/thread_pool.h"
+#include "components/services/screen_ai/buildflags/buildflags.h"
 #include "components/services/screen_ai/proto/main_content_extractor_proto_convertor.h"
 #include "components/services/screen_ai/proto/visual_annotator_proto_convertor.h"
 #include "components/services/screen_ai/public/cpp/utilities.h"
@@ -27,7 +28,7 @@
 #include "ui/accessibility/ax_tree_id.h"
 #include "ui/gfx/geometry/rect_f.h"
 
-#if defined(MEMORY_SANITIZER)
+#if BUILDFLAG(USE_FAKE_SCREEN_AI)
 #include "components/services/screen_ai/screen_ai_library_wrapper_fake.h"
 #else
 #include "components/services/screen_ai/screen_ai_library_wrapper_impl.h"
@@ -136,7 +137,7 @@
 void ScreenAIService::LoadLibrary(const base::FilePath& library_path) {
   DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
 
-#if defined(MEMORY_SANITIZER)
+#if BUILDFLAG(USE_FAKE_SCREEN_AI)
   library_ = std::make_unique<ScreenAILibraryWrapperFake>();
 #else
   library_ = std::make_unique<ScreenAILibraryWrapperImpl>();
diff --git a/components/tpcd/metadata/metadata.proto b/components/tpcd/metadata/metadata.proto
index 4cf4335..f459542 100644
--- a/components/tpcd/metadata/metadata.proto
+++ b/components/tpcd/metadata/metadata.proto
@@ -12,6 +12,8 @@
   // secondary_pattern_spec represents the pattern specifications to match
   // against first-party contexts url.
   optional string secondary_pattern_spec = 2;
+  // The origination of these patterns pair.
+  optional string source = 7;
 }
 
 // Metadata holds a list of url patterns pair aka `MetadataEntry`.
diff --git a/components/tpcd/metadata/parser.cc b/components/tpcd/metadata/parser.cc
index 655c7107..be230172 100644
--- a/components/tpcd/metadata/parser.cc
+++ b/components/tpcd/metadata/parser.cc
@@ -95,6 +95,7 @@
     entry.set_primary_pattern_spec(
         base::StrCat({"http://", hostname, ".test"}));
     entry.set_secondary_pattern_spec("*");
+    entry.set_source(Parser::kSourceTest);
     entries.emplace_back(entry);
   }
   return entries;
diff --git a/components/tpcd/metadata/parser.h b/components/tpcd/metadata/parser.h
index 94bca60..1e36261 100644
--- a/components/tpcd/metadata/parser.h
+++ b/components/tpcd/metadata/parser.h
@@ -51,6 +51,9 @@
   MetadataEntries GetMetadata();
 
   static constexpr char const* kMetadataFeatureParamName = "Metadata";
+  static constexpr char const* kSourceTest = "SOURCE_TEST";
+  static constexpr char const* kSource1pDt = "SOURCE_1P_DT";
+  static constexpr char const* kSource3pDt = "SOURCE_3P_DT";
 
   // Start Parser testing methods:
   MetadataEntries GetInstalledMetadataForTesting();
diff --git a/components/tpcd/metadata/parser_test_helper.cc b/components/tpcd/metadata/parser_test_helper.cc
index f49002a..8a353e2 100644
--- a/components/tpcd/metadata/parser_test_helper.cc
+++ b/components/tpcd/metadata/parser_test_helper.cc
@@ -6,6 +6,7 @@
 
 #include "base/base64.h"
 #include "components/tpcd/metadata/metadata.pb.h"
+#include "components/tpcd/metadata/parser.h"
 #include "third_party/zlib/google/compression_utils.h"
 
 namespace tpcd::metadata {
@@ -17,6 +18,7 @@
     MetadataEntry* me = metadata.add_metadata_entries();
     me->set_primary_pattern_spec(metadata_pair.first);
     me->set_secondary_pattern_spec(metadata_pair.second);
+    me->set_source(Parser::kSourceTest);
   }
   return metadata;
 }
diff --git a/components/tpcd/metadata/parser_unittest.cc b/components/tpcd/metadata/parser_unittest.cc
index 6891863..aaf0d3e 100644
--- a/components/tpcd/metadata/parser_unittest.cc
+++ b/components/tpcd/metadata/parser_unittest.cc
@@ -211,6 +211,7 @@
   ASSERT_EQ(me.size(), 1u);
   ASSERT_EQ(me.front().primary_pattern_spec(), primary_pattern_spec);
   ASSERT_EQ(me.front().secondary_pattern_spec(), secondary_pattern_spec);
+  ASSERT_EQ(me.front().source(), Parser::kSourceTest);
 }
 
 TEST_F(ParserTest, GetMetadata_ComponentUpdaterOnly) {
diff --git a/components/translate/content/android/translate_message.cc b/components/translate/content/android/translate_message.cc
index 9fc8acf..1e649dd7 100644
--- a/components/translate/content/android/translate_message.cc
+++ b/components/translate/content/android/translate_message.cc
@@ -124,7 +124,7 @@
 // Features
 BASE_FEATURE(kTranslateMessageUI,
              "TranslateMessageUI",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 // Params
 const char kTranslateMessageUISnackbarParam[] = "use_snackbar";
 
diff --git a/components/webapps/browser/android/BUILD.gn b/components/webapps/browser/android/BUILD.gn
index b46f469..ab36182 100644
--- a/components/webapps/browser/android/BUILD.gn
+++ b/components/webapps/browser/android/BUILD.gn
@@ -207,6 +207,7 @@
     "//components/browser_ui/bottomsheet/android:java",
     "//components/browser_ui/share/android:java",
     "//components/feature_engagement/public:public_java",
+    "//content/public/android:content_java",
     "//content/public/test/android:content_java_test_support",
     "//third_party/androidx:androidx_core_core_java",
     "//third_party/androidx:androidx_test_runner_java",
diff --git a/components/webapps/browser/android/app_banner_manager_android.cc b/components/webapps/browser/android/app_banner_manager_android.cc
index b2632d5..d019319 100644
--- a/components/webapps/browser/android/app_banner_manager_android.cc
+++ b/components/webapps/browser/android/app_banner_manager_android.cc
@@ -46,6 +46,7 @@
 #include "skia/ext/skia_utils_base.h"
 #include "third_party/blink/public/mojom/manifest/manifest.mojom.h"
 #include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/android/java_bitmap.h"
 
 using base::android::ConvertJavaStringToUTF16;
 using base::android::ConvertJavaStringToUTF8;
@@ -696,6 +697,32 @@
 }
 
 // static
+base::android::ScopedJavaLocalRef<jobject>
+JNI_AppBannerManager_GetInstallableWebAppIcon(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& java_web_contents) {
+  auto* manager =
+      static_cast<AppBannerManagerAndroid*>(AppBannerManager::FromWebContents(
+          content::WebContents::FromJavaWebContents(java_web_contents)));
+  if (!manager) {
+    return nullptr;
+  }
+
+  return gfx::ConvertToJavaBitmap(manager->primary_icon(),
+                                  gfx::OomBehavior::kReturnNullOnOom);
+}
+
+// static
+jboolean JNI_AppBannerManager_GetInstallableWebAppIconHasMaskable(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& java_web_contents) {
+  auto* manager =
+      static_cast<AppBannerManagerAndroid*>(AppBannerManager::FromWebContents(
+          content::WebContents::FromJavaWebContents(java_web_contents)));
+  return manager ? manager->has_maskable_primary_icon() : false;
+}
+
+// static
 void JNI_AppBannerManager_IgnoreChromeChannelForTesting(JNIEnv*) {
   gIgnoreChromeChannelForTesting = true;
 }
diff --git a/components/webapps/browser/android/app_banner_manager_android.h b/components/webapps/browser/android/app_banner_manager_android.h
index 98f391a5..a7664a7 100644
--- a/components/webapps/browser/android/app_banner_manager_android.h
+++ b/components/webapps/browser/android/app_banner_manager_android.h
@@ -113,6 +113,16 @@
   base::android::ScopedJavaLocalRef<jstring> GetInstallableWebAppManifestId(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& java_web_contents);
+  // Returns the primary icon for the installable web app, if the icon has been
+  // retrieved (and null if not).
+  base::android::ScopedJavaLocalRef<jstring> GetInstallableWebAppIcon(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& java_web_contents);
+  // Returns whether the primary icon for the installable web app is maskable,
+  // if the icon has been fetched (and false if not).
+  jboolean GetInstallableWebAppIconHasMaskable(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& java_web_contents);
 
   // Returns true if the banner pipeline is currently running.
   bool IsRunningForTesting(JNIEnv* env,
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 e9ae096..7aceae9 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
@@ -5,7 +5,9 @@
 package org.chromium.components.webapps;
 
 import android.content.Context;
+import android.graphics.Bitmap;
 import android.text.TextUtils;
+import android.util.Pair;
 
 import androidx.annotation.StringRes;
 import androidx.annotation.VisibleForTesting;
@@ -230,6 +232,12 @@
         return AppBannerManagerJni.get().getInstallableWebAppManifestId(contents);
     }
 
+    public Pair<Bitmap, Boolean> getIcon(WebContents contents) {
+        return Pair.create(
+                AppBannerManagerJni.get().getInstallableWebAppIcon(contents),
+                AppBannerManagerJni.get().getInstallableWebAppIconHasMaskable(contents));
+    }
+
     @NativeMethods
     public interface Natives {
         AppBannerManager getJavaBannerManagerForWebContents(WebContents webContents);
@@ -238,6 +246,10 @@
 
         String getInstallableWebAppManifestId(WebContents webContents);
 
+        Bitmap getInstallableWebAppIcon(WebContents webContents);
+
+        boolean getInstallableWebAppIconHasMaskable(WebContents webContents);
+
         boolean onAppDetailsRetrieved(
                 long nativeAppBannerManagerAndroid,
                 AppBannerManager caller,
diff --git a/components/webapps/browser/android/java/src/org/chromium/components/webapps/pwa_universal_install/PwaUniversalInstallBottomSheetContentTest.java b/components/webapps/browser/android/java/src/org/chromium/components/webapps/pwa_universal_install/PwaUniversalInstallBottomSheetContentTest.java
index dff26e1..55a6a1a 100644
--- a/components/webapps/browser/android/java/src/org/chromium/components/webapps/pwa_universal_install/PwaUniversalInstallBottomSheetContentTest.java
+++ b/components/webapps/browser/android/java/src/org/chromium/components/webapps/pwa_universal_install/PwaUniversalInstallBottomSheetContentTest.java
@@ -42,7 +42,7 @@
                 Mockito.mock(
                         PwaUniversalInstallBottomSheetView.class,
                         Mockito.withSettings()
-                                .useConstructor(mActivity)
+                                .useConstructor()
                                 .defaultAnswer(Mockito.RETURNS_MOCKS));
         PwaUniversalInstallBottomSheetContent pwaUniversalInstallBottomSheetContent =
                 new PwaUniversalInstallBottomSheetContent(
diff --git a/components/webapps/browser/android/java/src/org/chromium/components/webapps/pwa_universal_install/PwaUniversalInstallBottomSheetCoordinator.java b/components/webapps/browser/android/java/src/org/chromium/components/webapps/pwa_universal_install/PwaUniversalInstallBottomSheetCoordinator.java
index 22e1b8d4..33d8f35 100644
--- a/components/webapps/browser/android/java/src/org/chromium/components/webapps/pwa_universal_install/PwaUniversalInstallBottomSheetCoordinator.java
+++ b/components/webapps/browser/android/java/src/org/chromium/components/webapps/pwa_universal_install/PwaUniversalInstallBottomSheetCoordinator.java
@@ -5,28 +5,47 @@
 package org.chromium.components.webapps.pwa_universal_install;
 
 import android.app.Activity;
+import android.graphics.Bitmap;
+import android.util.Pair;
 import android.view.View;
 
 import androidx.annotation.MainThread;
 
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+import org.chromium.components.webapps.AppBannerManager;
+import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
+import java.util.concurrent.Callable;
+
 /** The Coordinator for managing the Pwa Universal Install bottom sheet experience. */
 public class PwaUniversalInstallBottomSheetCoordinator {
+    // If set, this swaps out the icon fetching callback for testing.
+    private static Callable<Pair<Bitmap, Boolean>> sIconCall;
+
+    public static void setIconCallForTesting(Callable<Pair<Bitmap, Boolean>> iconCall) {
+        sIconCall = iconCall;
+    }
+
     private final BottomSheetController mController;
     private final PwaUniversalInstallBottomSheetView mView;
     private final PwaUniversalInstallBottomSheetContent mContent;
     private final PwaUniversalInstallBottomSheetMediator mMediator;
+    private final WebContents mWebContents;
 
     /** Constructs the PwaUniversalInstallBottomSheetCoordinator. */
     @MainThread
     public PwaUniversalInstallBottomSheetCoordinator(
-            Activity activity, BottomSheetController bottomSheetController, int arrowId) {
+            Activity activity,
+            WebContents webContents,
+            BottomSheetController bottomSheetController,
+            int arrowId) {
+        mWebContents = webContents;
         mController = bottomSheetController;
 
-        mView = new PwaUniversalInstallBottomSheetView(activity);
-        mView.initialize(arrowId);
+        mView = new PwaUniversalInstallBottomSheetView();
+        mView.initialize(
+                activity, webContents, sIconCall != null ? sIconCall : this::getIcon, arrowId);
         mContent = new PwaUniversalInstallBottomSheetContent(mView);
         mMediator = new PwaUniversalInstallBottomSheetMediator(activity);
 
@@ -43,6 +62,11 @@
         return mController.requestShowContent(mContent, true);
     }
 
+    private Pair<Bitmap, Boolean> getIcon() {
+        AppBannerManager manager = AppBannerManager.forWebContents(mWebContents);
+        return manager != null ? manager.getIcon(mWebContents) : null;
+    }
+
     public View getBottomSheetViewForTesting() {
         return mView.getContentView();
     }
diff --git a/components/webapps/browser/android/java/src/org/chromium/components/webapps/pwa_universal_install/PwaUniversalInstallBottomSheetCoordinatorTest.java b/components/webapps/browser/android/java/src/org/chromium/components/webapps/pwa_universal_install/PwaUniversalInstallBottomSheetCoordinatorTest.java
index ef02dd34..4c5eb6d 100644
--- a/components/webapps/browser/android/java/src/org/chromium/components/webapps/pwa_universal_install/PwaUniversalInstallBottomSheetCoordinatorTest.java
+++ b/components/webapps/browser/android/java/src/org/chromium/components/webapps/pwa_universal_install/PwaUniversalInstallBottomSheetCoordinatorTest.java
@@ -4,7 +4,11 @@
 
 package org.chromium.components.webapps.pwa_universal_install;
 
+import static org.mockito.Mockito.mock;
+
 import android.app.Activity;
+import android.graphics.Bitmap;
+import android.util.Pair;
 import android.view.View;
 import android.widget.TextView;
 
@@ -22,6 +26,7 @@
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.webapps.R;
+import org.chromium.content_public.browser.test.mock.MockWebContents;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
 /** Instrumentation tests for PWA Universal Install bottom sheet. */
@@ -37,13 +42,29 @@
         MockitoAnnotations.initMocks(this);
     }
 
+    private Pair<Bitmap, Boolean> constructTestIconData() {
+        Bitmap allBlack2x2 =
+                Bitmap.createBitmap(
+                        new int[] {0x00000000, 0x00000000, 0x00000000, 0x00000000},
+                        2,
+                        2,
+                        Bitmap.Config.ARGB_8888);
+        return Pair.create(allBlack2x2, /* maskable= */ false);
+    }
+
     @Test
     @MediumTest
     public void testShowing() {
         final Activity activity = Robolectric.buildActivity(Activity.class).create().get();
+
+        PwaUniversalInstallBottomSheetCoordinator.setIconCallForTesting(
+                this::constructTestIconData);
+
+        // Setup the coordinator with a mocked WebContents object.
+        MockWebContents webContents = mock(MockWebContents.class);
         PwaUniversalInstallBottomSheetCoordinator coordinator =
                 new PwaUniversalInstallBottomSheetCoordinator(
-                        activity, mBottomSheetControllerMock, 0);
+                        activity, webContents, mBottomSheetControllerMock, 0);
 
         View view = coordinator.getBottomSheetViewForTesting();
         TestThreadUtils.runOnUiThreadBlocking(
diff --git a/components/webapps/browser/android/java/src/org/chromium/components/webapps/pwa_universal_install/PwaUniversalInstallBottomSheetView.java b/components/webapps/browser/android/java/src/org/chromium/components/webapps/pwa_universal_install/PwaUniversalInstallBottomSheetView.java
index 0b259f7..569b412f 100644
--- a/components/webapps/browser/android/java/src/org/chromium/components/webapps/pwa_universal_install/PwaUniversalInstallBottomSheetView.java
+++ b/components/webapps/browser/android/java/src/org/chromium/components/webapps/pwa_universal_install/PwaUniversalInstallBottomSheetView.java
@@ -6,6 +6,9 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.graphics.drawable.Icon;
+import android.os.Build;
+import android.util.Pair;
 import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -15,6 +18,9 @@
 
 import org.chromium.components.browser_ui.widget.RoundedIconGenerator;
 import org.chromium.components.webapps.R;
+import org.chromium.content_public.browser.WebContents;
+
+import java.util.concurrent.Callable;
 
 /** The view portion of the PWA Universal Install bottom sheet. */
 public class PwaUniversalInstallBottomSheetView {
@@ -22,19 +28,18 @@
     private static final int APP_ICON_CORNER_RADIUS_DP = 20;
     private static final int APP_ICON_TEXT_SIZE_DP = 24;
 
-    // The current context.
-    private final Context mContext;
-
     // The details of the bottom sheet.
     private View mContentView;
 
-    public PwaUniversalInstallBottomSheetView(Context context) {
-        mContext = context;
-    }
+    public PwaUniversalInstallBottomSheetView() {}
 
-    public void initialize(int arrowId) {
+    public void initialize(
+            Context context,
+            WebContents webContents,
+            Callable<Pair<Bitmap, Boolean>> iconCall,
+            int arrowId) {
         mContentView =
-                LayoutInflater.from(mContext)
+                LayoutInflater.from(context)
                         .inflate(
                                 R.layout.pwa_universal_install_bottom_sheet_content,
                                 /* root= */ null);
@@ -57,21 +62,42 @@
                                 TypedValue.applyDimension(
                                         TypedValue.COMPLEX_UNIT_DIP,
                                         4,
-                                        mContext.getResources().getDisplayMetrics())));
+                                        context.getResources().getDisplayMetrics())));
 
-        // TODO(finnur): Replace with the actual app icon.
-        int iconColor = mContext.getColor(R.color.default_favicon_background_color);
-        RoundedIconGenerator iconGenerator =
-                new RoundedIconGenerator(
-                        mContext.getResources(),
-                        APP_ICON_SIZE_DP,
-                        APP_ICON_SIZE_DP,
-                        APP_ICON_CORNER_RADIUS_DP,
-                        iconColor,
-                        APP_ICON_TEXT_SIZE_DP);
-        Bitmap placeholder = iconGenerator.generateIconForText("AppName");
-        ((ImageView) mContentView.findViewById(R.id.app_icon_install)).setImageBitmap(placeholder);
-        ((ImageView) mContentView.findViewById(R.id.app_icon_shortcut)).setImageBitmap(placeholder);
+        // Setup the app icon, with a placeholder as fallback in case of an error.
+        Pair<Bitmap, Boolean> iconWithMetadata;
+        try {
+            iconWithMetadata = iconCall.call();
+        } catch (Exception exception) {
+            iconWithMetadata = null;
+        }
+        Bitmap appIcon = iconWithMetadata != null ? iconWithMetadata.first : null;
+        boolean isAdaptive = iconWithMetadata != null ? iconWithMetadata.second : false;
+        assert (!isAdaptive || Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+                : "Adaptive icons should not be provided pre-Android O.";
+
+        if (appIcon == null) {
+            int iconColor = context.getColor(R.color.default_favicon_background_color);
+            RoundedIconGenerator iconGenerator =
+                    new RoundedIconGenerator(
+                            context.getResources(),
+                            APP_ICON_SIZE_DP,
+                            APP_ICON_SIZE_DP,
+                            APP_ICON_CORNER_RADIUS_DP,
+                            iconColor,
+                            APP_ICON_TEXT_SIZE_DP);
+            appIcon = iconGenerator.generateIconForText("?");
+        }
+
+        ImageView app_icon_install = mContentView.findViewById(R.id.app_icon_install);
+        ImageView app_icon_shortcut = mContentView.findViewById(R.id.app_icon_shortcut);
+        if (isAdaptive) {
+            app_icon_install.setImageIcon(Icon.createWithAdaptiveBitmap(appIcon));
+            app_icon_shortcut.setImageIcon(Icon.createWithAdaptiveBitmap(appIcon));
+        } else {
+            app_icon_install.setImageBitmap(appIcon);
+            app_icon_shortcut.setImageBitmap(appIcon);
+        }
 
         if (arrowId != 0) {
             ((ImageView) mContentView.findViewById(R.id.arrow_install))
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index f3989b3..860dbc9 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -41,6 +41,7 @@
     "//content/app:*",
     "//content/public/browser:browser_sources",
     "//content/test/fuzzer:ad_auction_headers_util_fuzzer",
+    "//content/test/fuzzer:ad_auction_service_mojolpm_fuzzer",
     "//content/test/fuzzer:browser_accessibility_fuzzer",
     "//content/test/fuzzer:clipboard_host_mojolpm_fuzzer",
     "//content/test/fuzzer:first_party_set_parser_fuzzer_support",
@@ -2317,6 +2318,8 @@
     "webauth/webauth_request_security_checker.h",
     "webid/digital_credentials/digital_credential_provider.cc",
     "webid/digital_credentials/digital_credential_provider.h",
+    "webid/digital_credentials/digital_identity_request_impl.cc",
+    "webid/digital_credentials/digital_identity_request_impl.h",
     "webid/fake_identity_request_dialog_controller.cc",
     "webid/fake_identity_request_dialog_controller.h",
     "webid/fedcm_metrics.cc",
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 7526c1ff..019506a 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -1814,6 +1814,11 @@
   RunHtmlTest(FILE_PATH_LITERAL("disabled.html"));
 }
 
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
+                       AccessibilityDisabledWithSubtree) {
+  RunHtmlTest(FILE_PATH_LITERAL("disabled-with-subtree.html"));
+}
+
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityDiv) {
   RunHtmlTest(FILE_PATH_LITERAL("div.html"));
 }
diff --git a/content/browser/aggregation_service/aggregation_service_storage_sql.cc b/content/browser/aggregation_service/aggregation_service_storage_sql.cc
index b009a77..d293eea 100644
--- a/content/browser/aggregation_service/aggregation_service_storage_sql.cc
+++ b/content/browser/aggregation_service/aggregation_service_storage_sql.cc
@@ -644,9 +644,9 @@
     if (!not_after_time.is_max()) {
       base::UmaHistogramCustomTimes(
           "PrivacySandbox.AggregationService.Storage.Sql."
-          "RequestDelayFromUpdatedReportTime",
+          "RequestDelayFromUpdatedReportTime2",
           not_after_time - get_requests_statement.ColumnTime(1),
-          /*min=*/base::Seconds(1),
+          /*min=*/base::Milliseconds(1),
           /*max=*/base::Days(24),
           /*buckets=*/50);
     }
diff --git a/content/browser/aggregation_service/aggregation_service_storage_sql_unittest.cc b/content/browser/aggregation_service/aggregation_service_storage_sql_unittest.cc
index 4cffa85..2d7ed4f 100644
--- a/content/browser/aggregation_service/aggregation_service_storage_sql_unittest.cc
+++ b/content/browser/aggregation_service/aggregation_service_storage_sql_unittest.cc
@@ -498,7 +498,7 @@
 
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       0);
 }
 
@@ -519,7 +519,7 @@
 
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       0);
 }
 
@@ -561,7 +561,7 @@
 
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       2);
 }
 
@@ -595,7 +595,7 @@
 
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       0);
 }
 
@@ -620,7 +620,7 @@
 
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       0);
 }
 
@@ -652,7 +652,7 @@
 
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       2);
 }
 
@@ -677,7 +677,7 @@
 
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       0);
 }
 
@@ -767,7 +767,7 @@
             kExampleTime);
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       0);
 
   std::vector<AggregationServiceStorage::RequestAndId> example_time_reports =
@@ -784,7 +784,7 @@
             kExampleTime + base::Hours(1));
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       2);
 
   EXPECT_EQ(storage_
@@ -794,7 +794,7 @@
             2u);
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       4);
 
   std::vector<AggregationServiceStorage::RequestAndId> all_reports =
@@ -803,7 +803,7 @@
   EXPECT_EQ(all_reports[2].id, RequestId(3));
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       7);
 
   EXPECT_FALSE(
@@ -812,7 +812,7 @@
             3u);
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       7);
 }
 
@@ -833,7 +833,7 @@
 
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       0);
 }
 
@@ -869,7 +869,7 @@
 
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       0);
 }
 
@@ -914,7 +914,7 @@
 
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       0);
 }
 
@@ -1082,7 +1082,7 @@
 
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       0);
 }
 
@@ -1162,7 +1162,7 @@
 
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       0);
   histograms_.ExpectBucketCount(
       "PrivacySandbox.AggregationService.Storage.Sql.StoreRequestHasCapacity",
@@ -1224,7 +1224,7 @@
 
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       0);
   histograms_.ExpectBucketCount(
       "PrivacySandbox.AggregationService.Storage.Sql.StoreRequestHasCapacity",
@@ -1269,7 +1269,7 @@
 
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       0);
 }
 
@@ -1304,7 +1304,7 @@
 
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       0);
 }
 
@@ -1347,7 +1347,7 @@
       stored_requests_and_ids[0].request, request));
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       0);
 }
 
@@ -1372,7 +1372,7 @@
       storage_->GetRequestsReportingOnOrBefore(base::Time::Max()).empty());
   histograms.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       0);
 }
 
@@ -1439,7 +1439,7 @@
             "https://aws.example.test/");
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       0);
 }
 
@@ -1489,7 +1489,7 @@
 
   histograms_.ExpectTotalCount(
       "PrivacySandbox.AggregationService.Storage.Sql."
-      "RequestDelayFromUpdatedReportTime",
+      "RequestDelayFromUpdatedReportTime2",
       0);
 }
 
diff --git a/content/browser/browser_interface_binders.cc b/content/browser/browser_interface_binders.cc
index 4a296c9..b466ae7 100644
--- a/content/browser/browser_interface_binders.cc
+++ b/content/browser/browser_interface_binders.cc
@@ -181,6 +181,7 @@
 #include "third_party/blink/public/mojom/webaudio/audio_context_manager.mojom.h"
 #include "third_party/blink/public/mojom/webauthn/authenticator.mojom.h"
 #include "third_party/blink/public/mojom/webauthn/virtual_authenticator.mojom.h"
+#include "third_party/blink/public/mojom/webid/digital_identity_request.mojom.h"
 #include "third_party/blink/public/mojom/webid/federated_auth_request.mojom.h"
 #include "third_party/blink/public/mojom/websockets/websocket_connector.mojom.h"
 #include "third_party/blink/public/mojom/webtransport/web_transport_connector.mojom.h"
@@ -861,6 +862,10 @@
                             base::Unretained(host)));
   }
 
+  map->Add<blink::mojom::DigitalIdentityRequest>(base::BindRepeating(
+      &RenderFrameHostImpl::BindDigitalIdentityRequestReceiver,
+      base::Unretained(host)));
+
   map->Add<blink::mojom::FederatedAuthRequest>(base::BindRepeating(
       &RenderFrameHostImpl::BindFederatedAuthRequestReceiver,
       base::Unretained(host)));
diff --git a/content/browser/devtools/protocol/device_orientation_handler.h b/content/browser/devtools/protocol/device_orientation_handler.h
index ef34333..3855db0 100644
--- a/content/browser/devtools/protocol/device_orientation_handler.h
+++ b/content/browser/devtools/protocol/device_orientation_handler.h
@@ -48,6 +48,9 @@
       double gamma,
       std::unique_ptr<SetDeviceOrientationOverrideCallback> callback) override;
 
+  // This is safe to store because it is updated by SetRenderer(). It is
+  // guaranteed that SetRenderer() is called before a RFH is deleted.
+  // See bug 323904196.
   raw_ptr<RenderFrameHostImpl> frame_host_ = nullptr;
 
   std::unique_ptr<ScopedVirtualSensorForDevTools> virtual_sensor_;
diff --git a/content/browser/interest_group/DEPS b/content/browser/interest_group/DEPS
index db49c22..0b6cd71 100644
--- a/content/browser/interest_group/DEPS
+++ b/content/browser/interest_group/DEPS
@@ -9,4 +9,7 @@
   ".*_unittest\.cc": [
     "+content/services/auction_worklet",
   ],
+  ".*_fuzzer.cc": [
+    "+third_party/libprotobuf-mutator/src/src/libfuzzer",
+  ],
 }
diff --git a/content/browser/interest_group/ad_auction_service_mojolpm_fuzzer.cc b/content/browser/interest_group/ad_auction_service_mojolpm_fuzzer.cc
new file mode 100644
index 0000000..8eda6ca
--- /dev/null
+++ b/content/browser/interest_group/ad_auction_service_mojolpm_fuzzer.cc
@@ -0,0 +1,347 @@
+// 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.
+
+// A MojoLPM fuzzer targeting the public API surfaces of the Protected Audiences
+// API.
+
+#include <stdint.h>
+
+#include <optional>
+#include <utility>
+
+#include "base/functional/bind.h"
+#include "base/functional/callback.h"
+#include "base/functional/callback_helpers.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/no_destructor.h"
+#include "base/run_loop.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/aggregation_service/features.h"
+#include "content/browser/interest_group/ad_auction_service_impl.h"
+#include "content/browser/interest_group/ad_auction_service_mojolpm_fuzzer.pb.h"
+#include "content/browser/interest_group/interest_group_features.h"
+#include "content/browser/renderer_host/render_frame_host_impl.h"
+#include "content/common/features.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/privacy_sandbox_invoking_api.h"
+#include "content/public/test/test_renderer_host.h"
+#include "content/public/test/url_loader_interceptor.h"
+#include "content/test/fuzzer/mojolpm_fuzzer_support.h"
+#include "content/test/test_content_browser_client.h"
+#include "content/test/test_render_frame_host.h"
+#include "content/test/test_web_contents.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
+#include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/mojom/interest_group/ad_auction_service.mojom-mojolpm.h"
+#include "third_party/blink/public/mojom/interest_group/ad_auction_service.mojom.h"
+#include "third_party/blink/public/mojom/interest_group/interest_group_types.mojom.h"
+#include "third_party/libprotobuf-mutator/src/src/libfuzzer/libfuzzer_macro.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+class AllowInterestGroupContentBrowserClient
+    : public content::TestContentBrowserClient {
+ public:
+  explicit AllowInterestGroupContentBrowserClient() = default;
+  ~AllowInterestGroupContentBrowserClient() override = default;
+
+  AllowInterestGroupContentBrowserClient(
+      const AllowInterestGroupContentBrowserClient&) = delete;
+  AllowInterestGroupContentBrowserClient& operator=(
+      const AllowInterestGroupContentBrowserClient&) = delete;
+
+  // ContentBrowserClient overrides:
+  bool IsInterestGroupAPIAllowed(content::RenderFrameHost* render_frame_host,
+                                 InterestGroupApiOperation operation,
+                                 const url::Origin& top_frame_origin,
+                                 const url::Origin& api_origin) override {
+    return true;
+  }
+
+  bool IsPrivacySandboxReportingDestinationAttested(
+      content::BrowserContext* browser_context,
+      const url::Origin& destination_origin,
+      content::PrivacySandboxInvokingAPI invoking_api,
+      bool post_impression_reporting) override {
+    return true;
+  }
+
+  bool IsCookieDeprecationLabelAllowed(
+      content::BrowserContext* browser_context) override {
+    return true;
+  }
+};
+
+constexpr char kFledgeUpdateHeaders[] =
+    "HTTP/1.1 200 OK\n"
+    "Content-type: Application/JSON\n"
+    "Ad-Auction-Allowed: true\n";
+
+constexpr char kInterestGroupUpdate[] = R"({
+"ads": [{"renderURL": "https://example.com/new_render"
+        }]
+})";
+
+// For handling network requests made by the Protected Audience API -- also
+// prevents those requests from being made to real servers.
+class NetworkResponder {
+ private:
+  bool RequestHandler(content::URLLoaderInterceptor::RequestParams* params) {
+    content::URLLoaderInterceptor::WriteResponse(
+        kFledgeUpdateHeaders, kInterestGroupUpdate, params->client.get());
+    return true;
+  }
+
+  // Handles network requests for interest group updates.
+  content::URLLoaderInterceptor network_interceptor_{
+      base::BindRepeating(&NetworkResponder::RequestHandler,
+                          base::Unretained(this))};
+};
+
+const char* const kCmdline[] = {"ad_auction_service_mojolpm_fuzzer", nullptr};
+
+content::mojolpm::FuzzerEnvironment& GetEnvironment() {
+  static base::NoDestructor<content::mojolpm::FuzzerEnvironment> environment(
+      1, kCmdline);
+  return *environment;
+}
+
+scoped_refptr<base::SequencedTaskRunner> GetFuzzerTaskRunner() {
+  return GetEnvironment().fuzzer_task_runner();
+}
+
+// Per-testcase state needed to run the interface being tested.
+//
+// The lifetime of this is scoped to a single testcase, and it is created and
+// destroyed from the fuzzer sequence (checked with `this->sequence_checker_`).
+//
+// Test cases may create one or more service instances, send Mojo messages to
+// remotes for those service instances, and run IO and UI thread tasks (the
+// fuzzer itself runs on its own thread, distinct from the UI and IO threads).
+//
+// For each input Testcase proto, SetUp() is run first. (This is why expensive
+// "stateless" initialization happens just once, in GetEnvironment(), before
+// SetUp() is run). Then, "new service" actions from the Testcase proto instruct
+// the fuzzer to create new service implementation instances; they are owned by
+// the RFH through DocumentService, and the RFH is owned by `test_adapter_`.
+// Actions use an ID to determine which service instance to use, allowing
+// control over which remote to use when running remote actions.  When all the
+// actions in the current Testcase proto have been executed, TearDown() is
+// called, and then this AdAuctionServiceTestcase is destroyed.  After that, the
+// process repeats with the next Testcase proto input.
+class AdAuctionServiceTestcase
+    : public ::mojolpm::Testcase<
+          content::fuzzing::ad_auction_service::proto::Testcase,
+          content::fuzzing::ad_auction_service::proto::Action> {
+ public:
+  using ProtoTestcase = content::fuzzing::ad_auction_service::proto::Testcase;
+  using ProtoAction = content::fuzzing::ad_auction_service::proto::Action;
+  explicit AdAuctionServiceTestcase(
+      const content::fuzzing::ad_auction_service::proto::Testcase& testcase);
+  ~AdAuctionServiceTestcase();
+
+  void SetUp(base::OnceClosure done_closure) override;
+  void TearDown(base::OnceClosure done_closure) override;
+
+  void RunAction(const ProtoAction& action,
+                 base::OnceClosure done_closure) override;
+
+ private:
+  void SetUpOnUIThread();
+  void TearDownOnUIThread();
+
+  // Create and bind a new AdAuctionServiceImpl instance, and register the
+  // remote with MojoLPM.
+  //
+  // Runs on fuzzer thread, calling CreateAdAuctionServiceImplOnUIThread() on
+  // the UI thread to create the implementation.
+  void AddAdAuctionService(uint32_t id, base::OnceClosure done_closure);
+  void CreateAdAuctionServiceImplOnUIThread(
+      mojo::PendingReceiver<blink::mojom::AdAuctionService>&& receiver);
+
+  // This is run every time we run the RunUntilIdle action -- this ensures that,
+  // for instance, completion callbacks posted from the AdAuctionService
+  // implementation's database thread are run on the UI thread.
+  void RunUntilIdleOnUIThread();
+
+  // All the below fields must be accessed on the UI thread.
+  base::test::ScopedFeatureList feature_list_;
+  base::test::ScopedFeatureList fenced_frame_feature_list_;
+
+  AllowInterestGroupContentBrowserClient content_browser_client_;
+  raw_ptr<content::ContentBrowserClient> old_content_browser_client_ = nullptr;
+  content::mojolpm::RenderViewHostTestHarnessAdapter test_adapter_;
+  raw_ptr<content::TestRenderFrameHost> render_frame_host_ = nullptr;
+
+  // Must be destroyed before test_adapter_::TearDown().
+  std::optional<NetworkResponder> network_responder_;
+
+  data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
+};
+
+AdAuctionServiceTestcase::AdAuctionServiceTestcase(
+    const ProtoTestcase& testcase)
+    : Testcase<ProtoTestcase, ProtoAction>(testcase) {
+  feature_list_.InitWithFeatures(
+      /*enabled_features=*/
+      {blink::features::kInterestGroupStorage,
+       blink::features::kAdInterestGroupAPI, blink::features::kFledge,
+       blink::features::kFledgeClearOriginJoinedAdInterestGroups,
+       blink::features::kFledgeNegativeTargeting,
+       blink::features::kPrivateAggregationApiMultipleCloudProviders,
+       aggregation_service::kAggregationServiceMultipleCloudProviders,
+       features::kEnableUpdatingUserBiddingSignals,
+       features::kEnableUpdatingExecutionModeToFrozenContext},
+      /*disabled_features=*/{});
+  fenced_frame_feature_list_.InitAndEnableFeatureWithParameters(
+      blink::features::kFencedFrames, {{"implementation_type", "mparch"}});
+  test_adapter_.SetUp();
+  network_responder_.emplace();
+}
+
+AdAuctionServiceTestcase::~AdAuctionServiceTestcase() {
+  network_responder_.reset();
+  test_adapter_.TearDown();
+}
+
+void AdAuctionServiceTestcase::RunAction(const ProtoAction& action,
+                                         base::OnceClosure run_closure) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(this->sequence_checker_);
+  const auto ThreadId_UI =
+      content::fuzzing::ad_auction_service::proto::RunThreadAction_ThreadId_UI;
+  const auto ThreadId_IO =
+      content::fuzzing::ad_auction_service::proto::RunThreadAction_ThreadId_IO;
+  switch (action.action_case()) {
+    case ProtoAction::kRunThread:
+      // These actions ensure that any tasks currently queued on the named
+      // thread have chance to run before the fuzzer continues.
+      //
+      // We don't provide any particular guarantees here; this does not mean
+      // that the named thread is idle, nor does it prevent any other threads
+      // from running (or the consequences of any resulting callbacks, for
+      // example).
+      if (action.run_thread().id() == ThreadId_UI) {
+        content::GetUIThreadTaskRunner({})->PostTaskAndReply(
+            FROM_HERE, base::DoNothing(), std::move(run_closure));
+      } else if (action.run_thread().id() == ThreadId_IO) {
+        content::GetIOThreadTaskRunner({})->PostTaskAndReply(
+            FROM_HERE, base::DoNothing(), std::move(run_closure));
+      }
+      return;
+    case ProtoAction::kNewAdAuctionService:
+      // Create and bind a new AdAuctionServiceImpl instance, and register the
+      // remote with MojoLPM.
+      AddAdAuctionService(action.new_ad_auction_service().id(),
+                          std::move(run_closure));
+      return;
+    case ProtoAction::kRunUntilIdle:
+      // On the UI thread, call RunUntilIdle() -- this is often needed to wait
+      // for other threads, like the AdAuctionService API implementation's
+      // database thread -- that thread posts results to the UI thread, the UI
+      // thread needs to be able to run the completion callbacks that receive
+      // the database results.
+      content::GetUIThreadTaskRunner({})->PostTaskAndReply(
+          FROM_HERE,
+          base::BindOnce(&AdAuctionServiceTestcase::RunUntilIdleOnUIThread,
+                         base::Unretained(this)),
+          std::move(run_closure));
+      return;
+    case ProtoAction::kAdAuctionServiceRemoteAction:
+      // Invoke one of the service methods on AdAuctionService, with parameters
+      // specified in the ad_auction_service_remote_action() proto, on the
+      // remote given by the id in the proto.
+      mojolpm::HandleRemoteAction(action.ad_auction_service_remote_action());
+      break;
+    case ProtoAction::ACTION_NOT_SET:
+      break;
+  }
+  GetFuzzerTaskRunner()->PostTask(FROM_HERE, std::move(run_closure));
+}
+
+void AdAuctionServiceTestcase::SetUp(base::OnceClosure done_closure) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(this->sequence_checker_);
+
+  content::GetUIThreadTaskRunner({})->PostTaskAndReply(
+      FROM_HERE,
+      base::BindOnce(&AdAuctionServiceTestcase::SetUpOnUIThread,
+                     base::Unretained(this)),
+      std::move(done_closure));
+}
+
+void AdAuctionServiceTestcase::SetUpOnUIThread() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  test_adapter_.NavigateAndCommit(GURL("https://owner.test:443"));
+  render_frame_host_ =
+      static_cast<content::TestWebContents*>(test_adapter_.web_contents())
+          ->GetPrimaryMainFrame();
+  render_frame_host_->InitializeRenderFrameIfNeeded();
+  old_content_browser_client_ =
+      SetBrowserClientForTesting(&content_browser_client_);
+}
+
+void AdAuctionServiceTestcase::TearDown(base::OnceClosure done_closure) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(this->sequence_checker_);
+  content::GetUIThreadTaskRunner({})->PostTaskAndReply(
+      FROM_HERE,
+      base::BindOnce(&AdAuctionServiceTestcase::TearDownOnUIThread,
+                     base::Unretained(this)),
+      std::move(done_closure));
+}
+
+void AdAuctionServiceTestcase::TearDownOnUIThread() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  SetBrowserClientForTesting(old_content_browser_client_);
+}
+
+void AdAuctionServiceTestcase::AddAdAuctionService(
+    uint32_t id,
+    base::OnceClosure run_closure) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(this->sequence_checker_);
+  mojo::Remote<blink::mojom::AdAuctionService> remote;
+  auto receiver = remote.BindNewPipeAndPassReceiver();
+  mojolpm::GetContext()->AddInstance(id, std::move(remote));
+  content::GetUIThreadTaskRunner({})->PostTaskAndReply(
+      FROM_HERE,
+      base::BindOnce(
+          &AdAuctionServiceTestcase::CreateAdAuctionServiceImplOnUIThread,
+          base::Unretained(this), std::move(receiver)),
+      std::move(run_closure));
+}
+
+void AdAuctionServiceTestcase::CreateAdAuctionServiceImplOnUIThread(
+    mojo::PendingReceiver<blink::mojom::AdAuctionService>&& receiver) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  content::AdAuctionServiceImpl::CreateMojoService(render_frame_host_,
+                                                   std::move(receiver));
+}
+
+void AdAuctionServiceTestcase::RunUntilIdleOnUIThread() {
+  test_adapter_.task_environment()->RunUntilIdle();
+}
+
+DEFINE_BINARY_PROTO_FUZZER(
+    const content::fuzzing::ad_auction_service::proto::Testcase&
+        proto_testcase) {
+  if (!proto_testcase.actions_size() || !proto_testcase.sequences_size() ||
+      !proto_testcase.sequence_indexes_size()) {
+    return;
+  }
+
+  GetEnvironment();
+
+  AdAuctionServiceTestcase testcase(proto_testcase);
+
+  base::RunLoop main_run_loop;
+  GetFuzzerTaskRunner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&mojolpm::RunTestcase<AdAuctionServiceTestcase>,
+                     base::Unretained(&testcase), GetFuzzerTaskRunner(),
+                     main_run_loop.QuitClosure()));
+  main_run_loop.Run();
+}
diff --git a/content/browser/interest_group/ad_auction_service_mojolpm_fuzzer.proto b/content/browser/interest_group/ad_auction_service_mojolpm_fuzzer.proto
new file mode 100644
index 0000000..d1f6bb61
--- /dev/null
+++ b/content/browser/interest_group/ad_auction_service_mojolpm_fuzzer.proto
@@ -0,0 +1,58 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Message format for the MojoLPM fuzzer for the AdAuctionService interface.
+
+syntax = "proto2";
+
+package content.fuzzing.ad_auction_service.proto;
+
+import "third_party/blink/public/mojom/interest_group/ad_auction_service.mojom.mojolpm.proto";
+
+// Bind a new AdAuctionService remote
+message NewAdAuctionServiceAction {
+  required uint32 id = 1;
+}
+
+// Run the specific sequence for (an indeterminate) period. This is not
+// intended to create a specific ordering, but to allow the fuzzer to delay a
+// later task until previous tasks have completed.
+message RunThreadAction {
+  enum ThreadId {
+    IO = 0;
+    UI = 1;
+  }
+
+  required ThreadId id = 1;
+}
+
+// Runs RunUntilIdle() on the test adapter task environment. This is often
+// necessary to wait for completion of AdAuctionService API implementation
+// threads, like the database thread, so that result callbacks from the database
+// can run on the UI thread.
+message RunUntilIdle {}
+
+// Actions that can be performed by the fuzzer.
+message Action {
+  oneof action {
+    NewAdAuctionServiceAction new_ad_auction_service = 1;
+    RunThreadAction run_thread = 2;
+    RunUntilIdle run_until_idle = 4;
+    mojolpm.blink.mojom.AdAuctionService.RemoteAction
+        ad_auction_service_remote_action = 3;
+  }
+}
+
+// Sequence provides a level of indirection which allows Testcase to compactly
+// express repeated sequences of actions.
+message Sequence {
+  repeated uint32 action_indexes = 1 [packed = true];
+}
+
+// Testcase is the top-level message type interpreted by the fuzzer.
+message Testcase {
+  repeated Action actions = 1;
+  repeated Sequence sequences = 2;
+  repeated uint32 sequence_indexes = 3 [packed = true];
+}
diff --git a/content/browser/message_port_close_event_browsertest.cc b/content/browser/message_port_close_event_browsertest.cc
index e385e2c71..f53e5a4d 100644
--- a/content/browser/message_port_close_event_browsertest.cc
+++ b/content/browser/message_port_close_event_browsertest.cc
@@ -34,6 +34,12 @@
   }
 
  protected:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
+                                    "MessagePortCloseEvent");
+    ContentBrowserTest::SetUpCommandLine(command_line);
+  }
+
   RenderFrameHost* GetPrimaryMainFrame() {
     return shell()->web_contents()->GetPrimaryMainFrame();
   }
diff --git a/content/browser/renderer_host/clipboard_host_impl_browsertest.cc b/content/browser/renderer_host/clipboard_host_impl_browsertest.cc
index 95c9403b..a07d690 100644
--- a/content/browser/renderer_host/clipboard_host_impl_browsertest.cc
+++ b/content/browser/renderer_host/clipboard_host_impl_browsertest.cc
@@ -40,7 +40,7 @@
   };
 
   void SetUp() override {
-    ASSERT_TRUE(embedded_test_server()->Start());
+    ASSERT_TRUE(embedded_https_test_server().Start());
     ui::TestClipboard::CreateForCurrentThread();
     ContentBrowserTest::SetUp();
   }
@@ -52,8 +52,8 @@
   }
 
   void CopyPasteFiles(std::vector<File> files) {
-    ASSERT_TRUE(
-        NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
+    ASSERT_TRUE(NavigateToURL(
+        shell(), embedded_https_test_server().GetURL("/title1.html")));
     // Create a promise that will resolve on paste with comma-separated
     // '<name>:<type>:<b64-content>' of each file on the clipboard.
     ASSERT_TRUE(
@@ -152,7 +152,7 @@
                          testing::Values(true, false));
 
 IN_PROC_BROWSER_TEST_P(ClipboardDocUrlBrowserTestP, HtmlUrl) {
-  GURL main_url(embedded_test_server()->GetURL("/title1.html"));
+  GURL main_url(embedded_https_test_server().GetURL("/title1.html"));
   ASSERT_TRUE(NavigateToURL(shell(), main_url));
   PermissionController* permission_controller =
       GetRenderFrameHost()->GetBrowserContext()->GetPermissionController();
@@ -189,20 +189,42 @@
         blink::features::kEmptyClipboardRead, /*enabled*/ true);
   }
 
+  void SetPermissionOverrideForAsyncClipboardTests(
+      blink::mojom::PermissionStatus status) {
+    content::PermissionController* permission_controller =
+        GetRenderFrameHost()->GetBrowserContext()->GetPermissionController();
+    url::Origin origin = url::Origin::Create(
+        embedded_https_test_server().GetURL("/title1.html"));
+    SetPermissionControllerOverrideForDevTools(
+        permission_controller, origin,
+        blink::PermissionType::CLIPBOARD_READ_WRITE, status);
+  }
+
+  void SetPermissionOverrideForSanitizedWriteTests(
+      blink::mojom::PermissionStatus status) {
+    content::PermissionController* permission_controller =
+        GetRenderFrameHost()->GetBrowserContext()->GetPermissionController();
+    url::Origin origin = url::Origin::Create(
+        embedded_https_test_server().GetURL("/title1.html"));
+    SetPermissionControllerOverrideForDevTools(
+        permission_controller, origin,
+        blink::PermissionType::CLIPBOARD_SANITIZED_WRITE, status);
+  }
+
+  void NavigateAndSetFocusToPage() {
+    ASSERT_TRUE(NavigateToURL(
+        shell(), embedded_https_test_server().GetURL("/title1.html")));
+    shell()->web_contents()->Focus();
+  }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_F(ClipboardBrowserTest, EmptyClipboard) {
   base::HistogramTester histogram_tester;
-  GURL main_url(embedded_test_server()->GetURL("/title1.html"));
-  ASSERT_TRUE(NavigateToURL(shell(), main_url));
-  PermissionController* permission_controller =
-      GetRenderFrameHost()->GetBrowserContext()->GetPermissionController();
-  url::Origin origin = url::Origin::Create(main_url);
-  SetPermissionControllerOverrideForDevTools(
-      permission_controller, origin,
-      blink::PermissionType::CLIPBOARD_READ_WRITE,
+  NavigateAndSetFocusToPage();
+  SetPermissionOverrideForAsyncClipboardTests(
       blink::mojom::PermissionStatus::GRANTED);
   ui::Clipboard::GetForCurrentThread()->Clear(ui::ClipboardBuffer::kCopyPaste);
   ASSERT_TRUE(ExecJs(shell(), " navigator.clipboard.read()"));
@@ -213,20 +235,12 @@
 
 IN_PROC_BROWSER_TEST_F(ClipboardBrowserTest, NumberOfFormatsOnRead) {
   base::HistogramTester histogram_tester;
-  GURL main_url(embedded_test_server()->GetURL("/title1.html"));
-  ASSERT_TRUE(NavigateToURL(shell(), main_url));
-  PermissionController* permission_controller =
-      GetRenderFrameHost()->GetBrowserContext()->GetPermissionController();
-  url::Origin origin = url::Origin::Create(main_url);
-  SetPermissionControllerOverrideForDevTools(
-      permission_controller, origin,
-      blink::PermissionType::CLIPBOARD_READ_WRITE,
+  NavigateAndSetFocusToPage();
+  SetPermissionOverrideForAsyncClipboardTests(
       blink::mojom::PermissionStatus::GRANTED);
   ui::Clipboard::GetForCurrentThread()->Clear(ui::ClipboardBuffer::kCopyPaste);
   ASSERT_TRUE(ExecJs(shell(), " navigator.clipboard.read()"));
-  SetPermissionControllerOverrideForDevTools(
-      permission_controller, origin,
-      blink::PermissionType::CLIPBOARD_SANITIZED_WRITE,
+  SetPermissionOverrideForSanitizedWriteTests(
       blink::mojom::PermissionStatus::GRANTED);
   ASSERT_TRUE(ExecJs(
       shell(),
@@ -243,4 +257,30 @@
                                      1);
 }
 
+IN_PROC_BROWSER_TEST_F(ClipboardBrowserTest, CopyPasteHtmlOnMac) {
+  NavigateAndSetFocusToPage();
+  base::RunLoop loop;
+  // Execute copy command to select the content of the body element.
+  ASSERT_TRUE(ExecJs(shell(),
+                     " document.execCommand('selectAll');"
+                     " document.execCommand('copy')"));
+  loop.RunUntilIdle();
+  // Get the HTML content from the clipboard and check that meta tag is added.
+  std::u16string html;
+  std::string url;
+  uint32_t ignore_offsets;
+  ui::Clipboard::GetForCurrentThread()->ReadHTML(
+      ui::ClipboardBuffer::kCopyPaste, nullptr, &html, &url, &ignore_offsets,
+      &ignore_offsets);
+// The meta tag is added only on Mac. See AddMetaCharsetTagToHtmlOnMac for
+// details.
+#if BUILDFLAG(IS_MAC)
+  EXPECT_TRUE(base::UTF16ToUTF8(html).find("<meta charset=\"utf-8\">") !=
+              std::string::npos);
+#else
+  EXPECT_TRUE(base::UTF16ToUTF8(html).find("<meta charset=\"utf-8\">") ==
+              std::string::npos);
+#endif
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/navigation_controller_android.cc b/content/browser/renderer_host/navigation_controller_android.cc
index f93fe2a..d39bc233 100644
--- a/content/browser/renderer_host/navigation_controller_android.cc
+++ b/content/browser/renderer_host/navigation_controller_android.cc
@@ -257,7 +257,8 @@
     jboolean should_clear_history_list,
     const base::android::JavaParamRef<jobject>& j_additional_navigation_params,
     jlong input_start,
-    jlong navigation_ui_data_ptr) {
+    jlong navigation_ui_data_ptr,
+    jboolean is_pdf) {
   DCHECK(url);
   NavigationController::LoadURLParams params(
       GURL(ConvertJavaStringToUTF8(env, url)));
@@ -275,6 +276,7 @@
   params.should_replace_current_entry = should_replace_current_entry;
   params.has_user_gesture = has_user_gesture;
   params.should_clear_history_list = should_clear_history_list;
+  params.is_pdf = is_pdf;
 
   if (j_additional_navigation_params) {
     params.initiator_frame_token =
diff --git a/content/browser/renderer_host/navigation_controller_android.h b/content/browser/renderer_host/navigation_controller_android.h
index 46cc0a0..b1af9f51 100644
--- a/content/browser/renderer_host/navigation_controller_android.h
+++ b/content/browser/renderer_host/navigation_controller_android.h
@@ -93,7 +93,8 @@
       const base::android::JavaParamRef<jobject>&
           j_additional_navigation_params,
       jlong input_start,
-      jlong navigation_ui_data_ptr);
+      jlong navigation_ui_data_ptr,
+      jboolean is_pdf);
   void ClearSslPreferences(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& /* obj */);
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 180bd0a..a82a50d 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -152,6 +152,7 @@
 #include "content/browser/web_package/prefetched_signed_exchange_cache.h"
 #include "content/browser/webauth/authenticator_impl.h"
 #include "content/browser/webauth/webauth_request_security_checker.h"
+#include "content/browser/webid/digital_credentials/digital_identity_request_impl.h"
 #include "content/browser/webid/federated_auth_request_impl.h"
 #include "content/browser/webid/flags.h"
 #include "content/browser/websockets/websocket_connector_impl.h"
@@ -12209,6 +12210,11 @@
     document_used_web_otp_ = true;
 }
 
+void RenderFrameHostImpl::BindDigitalIdentityRequestReceiver(
+    mojo::PendingReceiver<blink::mojom::DigitalIdentityRequest> receiver) {
+  DigitalIdentityRequestImpl::Create(*this, std::move(receiver));
+}
+
 void RenderFrameHostImpl::BindFederatedAuthRequestReceiver(
     mojo::PendingReceiver<blink::mojom::FederatedAuthRequest> receiver) {
   FederatedAuthRequestImpl::Create(this, std::move(receiver));
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index 52161f6f..6065b7a 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -169,6 +169,7 @@
 #include "third_party/blink/public/mojom/webaudio/audio_context_manager.mojom-forward.h"
 #include "third_party/blink/public/mojom/webauthn/authenticator.mojom-forward.h"
 #include "third_party/blink/public/mojom/webauthn/virtual_authenticator.mojom-forward.h"
+#include "third_party/blink/public/mojom/webid/digital_identity_request.mojom-forward.h"
 #include "third_party/blink/public/mojom/webid/federated_auth_request.mojom-forward.h"
 #include "third_party/blink/public/mojom/websockets/websocket_connector.mojom-forward.h"
 #include "third_party/blink/public/mojom/webtransport/web_transport_connector.mojom-forward.h"
@@ -2057,6 +2058,9 @@
   void BindWebOTPServiceReceiver(
       mojo::PendingReceiver<blink::mojom::WebOTPService> receiver);
 
+  void BindDigitalIdentityRequestReceiver(
+      mojo::PendingReceiver<blink::mojom::DigitalIdentityRequest> receiver);
+
   void BindFederatedAuthRequestReceiver(
       mojo::PendingReceiver<blink::mojom::FederatedAuthRequest> receiver);
 
diff --git a/content/browser/webid/digital_credentials/digital_credential_provider.h b/content/browser/webid/digital_credentials/digital_credential_provider.h
index a698f8c..7a0145dc 100644
--- a/content/browser/webid/digital_credentials/digital_credential_provider.h
+++ b/content/browser/webid/digital_credentials/digital_credential_provider.h
@@ -34,7 +34,8 @@
 
   static std::unique_ptr<DigitalCredentialProvider> Create();
 
-  using DigitalCredentialCallback = base::OnceCallback<void(std::string)>;
+  using DigitalCredentialCallback =
+      base::OnceCallback<void(const std::string&)>;
   virtual void RequestDigitalCredential(WebContents* web_contents,
                                         const url::Origin& origin,
                                         const base::Value::Dict& request,
diff --git a/content/browser/webid/digital_credentials/digital_credential_provider_android.h b/content/browser/webid/digital_credentials/digital_credential_provider_android.h
index 2f93a67..99b471e7 100644
--- a/content/browser/webid/digital_credentials/digital_credential_provider_android.h
+++ b/content/browser/webid/digital_credentials/digital_credential_provider_android.h
@@ -14,7 +14,6 @@
 #include "url/origin.h"
 
 #include <jni.h>
-#include <string>
 
 using blink::mojom::DigitalCredentialFieldRequirementPtr;
 
@@ -36,7 +35,6 @@
   DigitalCredentialProviderAndroid& operator=(
       const DigitalCredentialProviderAndroid&) = delete;
 
-  using DigitalCredentialCallback = base::OnceCallback<void(std::string)>;
   // Implementation of corresponding JNI methods in
   // DigitalCredentialProviderAndroid.Natives.*
   void OnReceive(JNIEnv*, jstring vc);
diff --git a/content/browser/webid/digital_credentials/digital_identity_request_impl.cc b/content/browser/webid/digital_credentials/digital_identity_request_impl.cc
new file mode 100644
index 0000000..806fae6
--- /dev/null
+++ b/content/browser/webid/digital_credentials/digital_identity_request_impl.cc
@@ -0,0 +1,169 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/webid/digital_credentials/digital_identity_request_impl.h"
+
+#include "base/functional/callback.h"
+#include "base/values.h"
+#include "content/browser/renderer_host/render_frame_host_impl.h"
+#include "content/browser/webid/digital_credentials/digital_credential_provider.h"
+#include "content/browser/webid/flags.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "third_party/blink/public/mojom/webid/federated_auth_request.mojom.h"
+
+using base::Value;
+using blink::mojom::RequestDigitalIdentityStatus;
+
+namespace content {
+
+// static
+void DigitalIdentityRequestImpl::Create(
+    RenderFrameHost& host,
+    mojo::PendingReceiver<blink::mojom::DigitalIdentityRequest> receiver) {
+  // DigitalIdentityRequestImpl owns itself. It will self-destruct when a mojo
+  // interface error occurs, the RenderFrameHost is deleted, or the
+  // RenderFrameHost navigates to a new document.
+  new DigitalIdentityRequestImpl(host, std::move(receiver));
+}
+
+DigitalIdentityRequestImpl::DigitalIdentityRequestImpl(
+    RenderFrameHost& host,
+    mojo::PendingReceiver<blink::mojom::DigitalIdentityRequest> receiver)
+    : DocumentService(host, std::move(receiver)) {}
+
+DigitalIdentityRequestImpl::~DigitalIdentityRequestImpl() = default;
+
+void DigitalIdentityRequestImpl::CompleteRequest(const std::string& response) {
+  if (!provider_) {
+    std::move(callback_).Run(RequestDigitalIdentityStatus::kError,
+                             absl::nullopt);
+    return;
+  }
+
+  if (!response.empty()) {
+    std::move(callback_).Run(RequestDigitalIdentityStatus::kSuccess, response);
+  } else {
+    std::move(callback_).Run(RequestDigitalIdentityStatus::kError,
+                             absl::nullopt);
+  }
+}
+
+base::Value::Dict BuildRequest(
+    blink::mojom::DigitalCredentialProviderPtr provider) {
+  auto result = Value::Dict();
+
+  if (provider->params) {
+    auto params = Value::Dict();
+    for (const auto& pair : *provider->params) {
+      params.Set(pair.first, pair.second);
+    }
+    result.Set("params", std::move(params));
+  }
+
+  if (provider->selector) {
+    auto formats = Value::List();
+    for (auto& format : provider->selector->format) {
+      formats.Append(format);
+    }
+
+    auto fields = Value::List();
+
+    if (provider->selector->doctype) {
+      auto doctype = Value::Dict();
+      doctype.Set("name", "doctype");
+      doctype.Set("equals", provider->selector->doctype.value());
+      fields.Append(std::move(doctype));
+    }
+
+    for (auto& value : provider->selector->fields) {
+      auto field = Value::Dict();
+      field.Set("name", value->name);
+      if (value->equals) {
+        field.Set("equals", value->equals.value());
+      }
+      fields.Append(std::move(field));
+    }
+
+    result.Set("selector", Value::Dict().Set("fields", std::move(fields)));
+    result.Set("responseFormat", std::move(formats));
+  }
+
+  if (provider->protocol) {
+    result.Set("protocol", *provider->protocol);
+  }
+
+  if (provider->request) {
+    result.Set("request", *provider->request);
+  }
+
+  if (provider->publicKey) {
+    result.Set("publicKey", *provider->publicKey);
+  }
+
+  return Value::Dict().Set("providers",
+                           Value::List().Append(std::move(result)));
+}
+
+void DigitalIdentityRequestImpl::Request(
+    blink::mojom::DigitalCredentialProviderPtr digital_credential_provider,
+    RequestCallback callback) {
+  if (!IsWebIdentityDigitalCredentialsEnabled()) {
+    std::move(callback).Run(RequestDigitalIdentityStatus::kError,
+                            absl::nullopt);
+    return;
+  }
+
+  if (render_frame_host().IsNestedWithinFencedFrame()) {
+    mojo::ReportBadMessage(
+        "DigitalIdentityRequest should not be allowed in fenced frame "
+        "trees.");
+    return;
+  }
+
+  if (callback_) {
+    // Only allow one in-flight wallet request.
+    std::move(callback).Run(RequestDigitalIdentityStatus::kErrorTooManyRequests,
+                            absl::nullopt);
+    return;
+  }
+
+  callback_ = std::move(callback);
+  // provider_ is not destroyed after a successful wallet request so we need to
+  // have the nullcheck to avoid duplicated creation.
+  if (!provider_) {
+    provider_ = CreateProvider();
+  }
+  if (!provider_) {
+    std::move(callback_).Run(RequestDigitalIdentityStatus::kError,
+                             absl::nullopt);
+    return;
+  }
+
+  auto request = BuildRequest(std::move(digital_credential_provider));
+
+  provider_->RequestDigitalCredential(
+      WebContents::FromRenderFrameHost(&render_frame_host()), origin(), request,
+      base::BindOnce(&DigitalIdentityRequestImpl::CompleteRequest,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void DigitalIdentityRequestImpl::Abort() {
+  // TODO(https://crbug.com/1416939): Implement.
+}
+
+std::unique_ptr<DigitalCredentialProvider>
+DigitalIdentityRequestImpl::CreateProvider() {
+  // A provider may only be created in browser tests by this moment.
+  std::unique_ptr<DigitalCredentialProvider> provider =
+      GetContentClient()->browser()->CreateDigitalCredentialProvider();
+
+  if (!provider) {
+    return DigitalCredentialProvider::Create();
+  }
+  return provider;
+}
+
+}  // namespace content
diff --git a/content/browser/webid/digital_credentials/digital_identity_request_impl.h b/content/browser/webid/digital_credentials/digital_identity_request_impl.h
new file mode 100644
index 0000000..d8b60b4
--- /dev/null
+++ b/content/browser/webid/digital_credentials/digital_identity_request_impl.h
@@ -0,0 +1,67 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_WEBID_DIGITAL_CREDENTIALS_DIGITAL_IDENTITY_REQUEST_IMPL_H_
+#define CONTENT_BROWSER_WEBID_DIGITAL_CREDENTIALS_DIGITAL_IDENTITY_REQUEST_IMPL_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "content/common/content_export.h"
+#include "content/public/browser/document_service.h"
+#include "third_party/blink/public/mojom/webid/digital_identity_request.mojom.h"
+#include "third_party/blink/public/mojom/webid/federated_auth_request.mojom.h"
+#include "url/gurl.h"
+
+namespace content {
+
+class DigitalCredentialProvider;
+class RenderFrameHost;
+
+// DigitalIdentityRequestImpl handles mojo connections from the renderer to
+// fulfill digital identity requests.
+//
+// In practice, it is owned and managed by a RenderFrameHost. It accomplishes
+// that via subclassing DocumentService, which observes the lifecycle of a
+// RenderFrameHost and manages its own memory.
+// Create() creates a self-managed instance of DigitalIdentityRequestImpl and
+// binds it to the receiver.
+class CONTENT_EXPORT DigitalIdentityRequestImpl
+    : public DocumentService<blink::mojom::DigitalIdentityRequest> {
+ public:
+  static void Create(
+      RenderFrameHost&,
+      mojo::PendingReceiver<blink::mojom::DigitalIdentityRequest>);
+
+  DigitalIdentityRequestImpl(const DigitalIdentityRequestImpl&) = delete;
+  DigitalIdentityRequestImpl& operator=(const DigitalIdentityRequestImpl&) =
+      delete;
+
+  ~DigitalIdentityRequestImpl() override;
+
+  // blink::mojom::DigitalIdentityRequest:
+  void Request(blink::mojom::DigitalCredentialProviderPtr provider,
+               RequestCallback) override;
+  void Abort() override;
+
+ private:
+  DigitalIdentityRequestImpl(
+      RenderFrameHost&,
+      mojo::PendingReceiver<blink::mojom::DigitalIdentityRequest>);
+
+  void CompleteRequest(const std::string& response);
+
+  std::unique_ptr<DigitalCredentialProvider> CreateProvider();
+
+  std::unique_ptr<DigitalCredentialProvider> provider_;
+  RequestCallback callback_;
+
+  base::WeakPtrFactory<DigitalIdentityRequestImpl> weak_ptr_factory_{this};
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_WEBID_DIGITAL_CREDENTIALS_DIGITAL_IDENTITY_REQUEST_IMPL_H_
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc
index 4d7c6228..cf463ed 100644
--- a/content/browser/webid/federated_auth_request_impl.cc
+++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -23,7 +23,6 @@
 #include "content/browser/devtools/devtools_instrumentation.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
-#include "content/browser/webid/digital_credentials/digital_credential_provider.h"
 #include "content/browser/webid/fake_identity_request_dialog_controller.h"
 #include "content/browser/webid/federated_auth_disconnect_request.h"
 #include "content/browser/webid/federated_auth_request_page_data.h"
@@ -602,83 +601,6 @@
       permission_context, identity_registry, std::move(receiver));
 }
 
-void FederatedAuthRequestImpl::CompleteDigitalCredentialRequest(
-    std::string response) {
-  if (!digital_credential_provider_) {
-    std::move(digital_credential_request_callback_)
-        .Run(RequestTokenStatus::kError, std::nullopt, "", /*error=*/nullptr,
-             /*is_auto_selected=*/false);
-    return;
-  }
-
-  if (!response.empty()) {
-    std::move(digital_credential_request_callback_)
-        .Run(RequestTokenStatus::kSuccess, std::nullopt, response,
-             /*error=*/nullptr,
-             /*is_auto_selected=*/false);
-  } else {
-    std::move(digital_credential_request_callback_)
-        .Run(RequestTokenStatus::kError, std::nullopt, "", /*error=*/nullptr,
-             /*is_auto_selected=*/false);
-  }
-}
-
-base::Value::Dict BuildDigitalCredentialRequest(
-    blink::mojom::DigitalCredentialProviderPtr provider) {
-  auto result = Value::Dict();
-
-  if (provider->params) {
-    auto params = Value::Dict();
-    for (const auto& pair : *provider->params) {
-      params.Set(pair.first, pair.second);
-    }
-    result.Set("params", std::move(params));
-  }
-
-  if (provider->selector) {
-    auto formats = Value::List();
-    for (auto& format : provider->selector->format) {
-      formats.Append(format);
-    }
-
-    auto fields = Value::List();
-
-    if (provider->selector->doctype) {
-      auto doctype = Value::Dict();
-      doctype.Set("name", "doctype");
-      doctype.Set("equals", provider->selector->doctype.value());
-      fields.Append(std::move(doctype));
-    }
-
-    for (auto& value : provider->selector->fields) {
-      auto field = Value::Dict();
-      field.Set("name", value->name);
-      if (value->equals) {
-        field.Set("equals", value->equals.value());
-      }
-      fields.Append(std::move(field));
-    }
-
-    result.Set("selector", Value::Dict().Set("fields", std::move(fields)));
-    result.Set("responseFormat", std::move(formats));
-  }
-
-  if (provider->protocol) {
-    result.Set("protocol", *provider->protocol);
-  }
-
-  if (provider->request) {
-    result.Set("request", *provider->request);
-  }
-
-  if (provider->publicKey) {
-    result.Set("publicKey", *provider->publicKey);
-  }
-
-  return Value::Dict().Set("providers",
-                           Value::List().Append(std::move(result)));
-}
-
 std::vector<blink::mojom::IdentityProviderPtr>
 FederatedAuthRequestImpl::MaybeAddRegisteredProviders(
     std::vector<blink::mojom::IdentityProviderPtr>& providers) {
@@ -799,60 +721,6 @@
     return;
   }
 
-  if (idp_get_params_ptrs[0]->providers[0]->is_holder()) {
-    if (!IsWebIdentityDigitalCredentialsEnabled() ||
-        IsFedCmMultipleIdentityProvidersEnabled()) {
-      // TODO(https://crbug.com/1416939): Support calling the Digital
-      // Credentials
-      //  API with the Multi IdP API support.
-      std::move(callback).Run(RequestTokenStatus::kError, std::nullopt, "",
-                              /*error=*/nullptr,
-                              /*is_auto_selected=*/false);
-      return;
-    }
-
-    if (digital_credential_request_callback_) {
-      // Similar to the token request, only allow one in-flight wallet request.
-      // TODO(https://crbug.com/1416939): Reconcile with federated identity
-      // requests.
-      std::move(callback).Run(RequestTokenStatus::kErrorTooManyRequests,
-                              std::nullopt, "", /*error=*/nullptr,
-                              /*is_auto_selected=*/false);
-      return;
-    }
-
-    digital_credential_request_callback_ = std::move(callback);
-    // digital_credential_provider_ is not destroyed after a successful wallet
-    // request so we need to have the nullcheck to avoid duplicated creation.
-    if (!digital_credential_provider_) {
-      digital_credential_provider_ = CreateDigitalCredentialProvider();
-    }
-    if (!digital_credential_provider_) {
-      std::move(digital_credential_request_callback_)
-          .Run(RequestTokenStatus::kError, std::nullopt, "", /*error=*/nullptr,
-               /*is_auto_selected=*/false);
-      return;
-    }
-
-    auto digital_credential =
-        std::move(idp_get_params_ptrs[0]->providers[0]->get_holder());
-
-    auto request = BuildDigitalCredentialRequest(std::move(digital_credential));
-
-    digital_credential_provider_->RequestDigitalCredential(
-        WebContents::FromRenderFrameHost(&render_frame_host()), origin(),
-        request,
-        base::BindOnce(
-            &FederatedAuthRequestImpl::CompleteDigitalCredentialRequest,
-            weak_ptr_factory_.GetWeakPtr()));
-
-    // TODO(https://crbug.com/1416939): rather than returning early,
-    // we would ultimately like to make the wallet response reconcile with the
-    // federated identities, so that they can be presented to the user in an
-    // unified manner.
-    return;
-  }
-
   if (!fedcm_metrics_) {
     // Ensure the lifecycle state as GetPageUkmSourceId doesn't support the
     // prerendering page. As FederatedAithRequest runs behind the
@@ -2651,18 +2519,6 @@
       web_contents);
 }
 
-std::unique_ptr<DigitalCredentialProvider>
-FederatedAuthRequestImpl::CreateDigitalCredentialProvider() {
-  // A provider may only be created in browser tests by this moment.
-  std::unique_ptr<DigitalCredentialProvider> provider =
-      GetContentClient()->browser()->CreateDigitalCredentialProvider();
-
-  if (!provider) {
-    return DigitalCredentialProvider::Create();
-  }
-  return provider;
-}
-
 void FederatedAuthRequestImpl::SetNetworkManagerForTests(
     std::unique_ptr<IdpNetworkRequestManager> manager) {
   mock_network_manager_ = std::move(manager);
diff --git a/content/browser/webid/federated_auth_request_impl.h b/content/browser/webid/federated_auth_request_impl.h
index 0c474ab..fef254c 100644
--- a/content/browser/webid/federated_auth_request_impl.h
+++ b/content/browser/webid/federated_auth_request_impl.h
@@ -36,7 +36,6 @@
 class FederatedIdentityApiPermissionContextDelegate;
 class FederatedIdentityAutoReauthnPermissionContextDelegate;
 class FederatedIdentityPermissionContextDelegate;
-class DigitalCredentialProvider;
 class RenderFrameHost;
 
 using MediationRequirement = ::password_manager::CredentialMediationRequirement;
@@ -317,7 +316,6 @@
       RequestUserInfoCallback callback,
       blink::mojom::RequestUserInfoStatus status,
       std::optional<std::vector<blink::mojom::IdentityUserInfoPtr>> user_info);
-  void CompleteDigitalCredentialRequest(std::string response);
 
   // Notifies metrics endpoint that either the user did not select the IDP in
   // the prompt or that there was an error in fetching data for the IDP.
@@ -329,7 +327,6 @@
 
   std::unique_ptr<IdpNetworkRequestManager> CreateNetworkManager();
   std::unique_ptr<IdentityRequestDialogController> CreateDialogController();
-  std::unique_ptr<DigitalCredentialProvider> CreateDigitalCredentialProvider();
 
   // Creates an inspector issue related to a federated authentication request to
   // the Issues panel in DevTools.
@@ -489,9 +486,6 @@
   IdentitySelectionType identity_selection_type_ = kExplicit;
   RpMode rp_mode_{RpMode::kWidget};
 
-  std::unique_ptr<DigitalCredentialProvider> digital_credential_provider_;
-  RequestTokenCallback digital_credential_request_callback_;
-
   // Time when the accounts dialog is last shown for metrics purposes.
   std::optional<base::TimeTicks> accounts_dialog_shown_time_;
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java b/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java
index daf37a2..b2fce5be 100644
--- a/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java
@@ -232,7 +232,8 @@
                                     inputStart,
                                     params.getNavigationUIDataSupplier() == null
                                             ? 0
-                                            : params.getNavigationUIDataSupplier().get());
+                                            : params.getNavigationUIDataSupplier().get(),
+                                    params.getIsPdf());
             // Use the navigation handle object to store user data passed in.
             if (navigationHandle != null) {
                 navigationHandle.setUserDataHost(params.takeNavigationHandleUserData());
@@ -508,7 +509,8 @@
                 boolean shouldClearHistoryList,
                 AdditionalNavigationParams additionalNavigationParams,
                 long inputStart,
-                long navigationUIDataPtr);
+                long navigationUIDataPtr,
+                boolean isPdf);
 
         void clearHistory(long nativeNavigationControllerAndroid, NavigationControllerImpl caller);
 
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/LoadUrlParams.java b/content/public/android/java/src/org/chromium/content_public/browser/LoadUrlParams.java
index c527d605..3f1fbeb4 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/LoadUrlParams.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/LoadUrlParams.java
@@ -55,6 +55,7 @@
     private boolean mShouldClearHistoryList;
     @Nullable private AdditionalNavigationParams mAdditionalNavigationParams;
     private Supplier<Long> mNavigationUIDataSupplier;
+    private boolean mIsPdf;
 
     /**
      * Creates an instance with default page transition type.
@@ -632,6 +633,18 @@
         return mNavigationUIDataSupplier;
     }
 
+    /**
+     * @return Whether the URL is a pdf file.
+     */
+    public boolean getIsPdf() {
+        return mIsPdf;
+    }
+
+    /** Sets whether the URL is a pdf file. */
+    public void setIsPdf(boolean isPdf) {
+        mIsPdf = isPdf;
+    }
+
     @NativeMethods
     interface Natives {
         /**
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index af6bf31..ea419d7 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -4293,16 +4293,23 @@
 void CookieChangeObserver::OnCookiesAccessed(
     content::RenderFrameHost* render_frame_host,
     const content::CookieAccessDetails& details) {
-  OnCookieAccessed();
+  OnCookieAccessed(details);
 }
 
 void CookieChangeObserver::OnCookiesAccessed(
     content::NavigationHandle* navigation,
     const content::CookieAccessDetails& details) {
-  OnCookieAccessed();
+  OnCookieAccessed(details);
 }
 
-void CookieChangeObserver::OnCookieAccessed() {
+void CookieChangeObserver::OnCookieAccessed(
+    const content::CookieAccessDetails& details) {
+  if (details.type == CookieAccessDetails::Type::kRead) {
+    num_read_seen_++;
+  } else if (details.type == CookieAccessDetails::Type::kChange) {
+    num_write_seen_++;
+  }
+
   if (++num_seen_ == num_expected_calls_) {
     run_loop_.Quit();
   }
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index b2b85e6..76b211d 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -2363,17 +2363,22 @@
 
   void Wait();
 
+  int num_read_seen() const { return num_read_seen_; }
+  int num_write_seen() const { return num_write_seen_; }
+
  private:
   void OnCookiesAccessed(content::RenderFrameHost* render_frame_host,
                          const content::CookieAccessDetails& details) override;
   void OnCookiesAccessed(content::NavigationHandle* navigation,
                          const content::CookieAccessDetails& details) override;
 
-  void OnCookieAccessed();
+  void OnCookieAccessed(const content::CookieAccessDetails& details);
 
   base::RunLoop run_loop_;
   int num_seen_ = 0;
   int num_expected_calls_;
+  int num_read_seen_ = 0;
+  int num_write_seen_ = 0;
 };
 
 [[nodiscard]] base::CallbackListSubscription
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 8d978ba..9bf9cbba1 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -34,14 +34,14 @@
   ]
 
   sources = [
+    "accessibility/annotations/ax_image_annotator.cc",
+    "accessibility/annotations/ax_image_annotator.h",
+    "accessibility/annotations/ax_image_stopwords.cc",
+    "accessibility/annotations/ax_image_stopwords.h",
     "accessibility/aom_content_ax_tree.cc",
     "accessibility/aom_content_ax_tree.h",
     "accessibility/ax_action_target_factory.cc",
     "accessibility/ax_action_target_factory.h",
-    "accessibility/ax_image_annotator.cc",
-    "accessibility/ax_image_annotator.h",
-    "accessibility/ax_image_stopwords.cc",
-    "accessibility/ax_image_stopwords.h",
     "accessibility/ax_tree_snapshotter_impl.cc",
     "accessibility/ax_tree_snapshotter_impl.h",
     "accessibility/blink_ax_action_target.cc",
diff --git a/content/renderer/accessibility/ax_image_annotator.cc b/content/renderer/accessibility/annotations/ax_image_annotator.cc
similarity index 98%
rename from content/renderer/accessibility/ax_image_annotator.cc
rename to content/renderer/accessibility/annotations/ax_image_annotator.cc
index 4a4e2c8..324066b 100644
--- a/content/renderer/accessibility/ax_image_annotator.cc
+++ b/content/renderer/accessibility/annotations/ax_image_annotator.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 "content/renderer/accessibility/ax_image_annotator.h"
+#include "content/renderer/accessibility/annotations/ax_image_annotator.h"
 
 #include <utility>
 #include <vector>
@@ -19,7 +19,7 @@
 #include "build/build_config.h"
 #include "content/public/common/content_client.h"
 #include "content/public/renderer/render_frame.h"
-#include "content/renderer/accessibility/ax_image_stopwords.h"
+#include "content/renderer/accessibility/annotations/ax_image_stopwords.h"
 #include "crypto/sha2.h"
 #include "services/metrics/public/cpp/mojo_ukm_recorder.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
@@ -286,7 +286,7 @@
 }
 
 void AXImageAnnotator::MarkDirty(const blink::WebAXObject& image) const {
-  render_accessibility_->MarkWebAXObjectDirty(image, false /* subtree */);
+  render_accessibility_->MarkWebAXObjectDirty(image);
 
   // Check two unignored parents. If either of them is a link or root web area,
   // mark it dirty too, because we want a link or document containing exactly
@@ -300,8 +300,7 @@
     if (!parent.AccessibilityIsIgnored()) {
       ++ancestor_count;
       if (ui::IsLink(parent.Role()) || ui::IsPlatformDocument(parent.Role())) {
-        render_accessibility_->MarkWebAXObjectDirty(parent,
-                                                    false /* subtree */);
+        render_accessibility_->MarkWebAXObjectDirty(parent);
         return;
       }
     }
diff --git a/content/renderer/accessibility/ax_image_annotator.h b/content/renderer/accessibility/annotations/ax_image_annotator.h
similarity index 95%
rename from content/renderer/accessibility/ax_image_annotator.h
rename to content/renderer/accessibility/annotations/ax_image_annotator.h
index c687d67..78c8082 100644
--- a/content/renderer/accessibility/ax_image_annotator.h
+++ b/content/renderer/accessibility/annotations/ax_image_annotator.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_RENDERER_ACCESSIBILITY_AX_IMAGE_ANNOTATOR_H_
-#define CONTENT_RENDERER_ACCESSIBILITY_AX_IMAGE_ANNOTATOR_H_
+#ifndef CONTENT_RENDERER_ACCESSIBILITY_ANNOTATIONS_AX_IMAGE_ANNOTATOR_H_
+#define CONTENT_RENDERER_ACCESSIBILITY_ANNOTATIONS_AX_IMAGE_ANNOTATOR_H_
 
 #include <optional>
 #include <string>
@@ -134,4 +134,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_RENDERER_ACCESSIBILITY_AX_IMAGE_ANNOTATOR_H_
+#endif  // CONTENT_RENDERER_ACCESSIBILITY_ANNOTATIONS_AX_IMAGE_ANNOTATOR_H_
diff --git a/content/renderer/accessibility/ax_image_stopwords.cc b/content/renderer/accessibility/annotations/ax_image_stopwords.cc
similarity index 99%
rename from content/renderer/accessibility/ax_image_stopwords.cc
rename to content/renderer/accessibility/annotations/ax_image_stopwords.cc
index d76e0f04..0187f8a 100644
--- a/content/renderer/accessibility/ax_image_stopwords.cc
+++ b/content/renderer/accessibility/annotations/ax_image_stopwords.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 "content/renderer/accessibility/annotations/ax_image_stopwords.h"
+
 #include <string>
 #include <vector>
 
@@ -13,7 +15,6 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
-#include "content/renderer/accessibility/ax_image_stopwords.h"
 #include "third_party/icu/source/common/unicode/uchar.h"
 
 namespace content {
diff --git a/content/renderer/accessibility/ax_image_stopwords.h b/content/renderer/accessibility/annotations/ax_image_stopwords.h
similarity index 92%
rename from content/renderer/accessibility/ax_image_stopwords.h
rename to content/renderer/accessibility/annotations/ax_image_stopwords.h
index 55a75ab..02fbd118 100644
--- a/content/renderer/accessibility/ax_image_stopwords.h
+++ b/content/renderer/accessibility/annotations/ax_image_stopwords.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_RENDERER_ACCESSIBILITY_AX_IMAGE_STOPWORDS_H_
-#define CONTENT_RENDERER_ACCESSIBILITY_AX_IMAGE_STOPWORDS_H_
+#ifndef CONTENT_RENDERER_ACCESSIBILITY_ANNOTATIONS_AX_IMAGE_STOPWORDS_H_
+#define CONTENT_RENDERER_ACCESSIBILITY_ANNOTATIONS_AX_IMAGE_STOPWORDS_H_
 
 #include "base/containers/flat_set.h"
 #include "base/no_destructor.h"
@@ -67,4 +67,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_RENDERER_ACCESSIBILITY_AX_IMAGE_STOPWORDS_H_
+#endif  // CONTENT_RENDERER_ACCESSIBILITY_ANNOTATIONS_AX_IMAGE_STOPWORDS_H_
diff --git a/content/renderer/accessibility/ax_image_stopwords_unittest.cc b/content/renderer/accessibility/annotations/ax_image_stopwords_unittest.cc
similarity index 98%
rename from content/renderer/accessibility/ax_image_stopwords_unittest.cc
rename to content/renderer/accessibility/annotations/ax_image_stopwords_unittest.cc
index 2b849597..d4bbe18 100644
--- a/content/renderer/accessibility/ax_image_stopwords_unittest.cc
+++ b/content/renderer/accessibility/annotations/ax_image_stopwords_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/accessibility/ax_image_stopwords.h"
+#include "content/renderer/accessibility/annotations/ax_image_stopwords.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace content {
diff --git a/content/renderer/accessibility/render_accessibility_impl.cc b/content/renderer/accessibility/render_accessibility_impl.cc
index dd73101..c02016d 100644
--- a/content/renderer/accessibility/render_accessibility_impl.cc
+++ b/content/renderer/accessibility/render_accessibility_impl.cc
@@ -29,8 +29,8 @@
 #include "build/chromeos_buildflags.h"
 #include "content/public/common/content_features.h"
 #include "content/public/renderer/render_thread.h"
+#include "content/renderer/accessibility/annotations/ax_image_annotator.h"
 #include "content/renderer/accessibility/ax_action_target_factory.h"
-#include "content/renderer/accessibility/ax_image_annotator.h"
 #include "content/renderer/accessibility/ax_tree_snapshotter_impl.h"
 #include "content/renderer/accessibility/blink_ax_action_target.h"
 #include "content/renderer/accessibility/render_accessibility_manager.h"
@@ -279,7 +279,7 @@
     if (event_to_fire != ax::mojom::Event::kNone) {
       const std::vector<ui::AXEventIntent> intents;
       // Marking dirty ensures that a lifecycle update will be scheduled.
-      MarkWebAXObjectDirty(ax_object, /*subtree*/ false);
+      MarkWebAXObjectDirty(ax_object);
       HandleAXEvent(ui::AXEvent(
           ax_object.AxID(), event_to_fire, ax::mojom::EventFrom::kAction,
           ax::mojom::Action::kHitTest, intents, request_id));
@@ -433,7 +433,6 @@
 
 void RenderAccessibilityImpl::MarkWebAXObjectDirty(
     const WebAXObject& obj,
-    bool subtree,
     ax::mojom::EventFrom event_from,
     ax::mojom::Action event_from_action,
     std::vector<ui::AXEventIntent> event_intents,
@@ -441,7 +440,7 @@
   DCHECK(obj.AccessibilityIsIncludedInTree())
       << "Cannot serialize unincluded object: " << obj.ToString(true).Utf8();
 
-  obj.AddDirtyObjectToSerializationQueue(subtree, event_from, event_from_action,
+  obj.AddDirtyObjectToSerializationQueue(event_from, event_from_action,
                                          event_intents);
 }
 
@@ -515,7 +514,7 @@
   if (obj.IsNull())
     return;
 
-  MarkWebAXObjectDirty(obj, /* subtree */ false);
+  MarkWebAXObjectDirty(obj);
   // Schedule an update immediately whenever the PDF root in PDF accessibility
   // tree changes. It is needed to ensure that changes (e.g. bounds) in PDF
   // accessibility tree are serialized.
@@ -1011,7 +1010,7 @@
     return;
   }
 
-  obj.MarkSerializerSubtreeDirty();
+  MarkWebAXObjectDirty(obj);
   HandleAXEvent(ui::AXEvent(obj.AxID(), ax::mojom::Event::kImageFrameUpdated));
 }
 
@@ -1033,6 +1032,11 @@
       node.child_ids.push_back(root->id());
 
       ui::AXTreeUpdate plugin_update;
+
+      // TODO(crbug.com/324124958): meant as a short-term workaround for
+      // instability.
+      plugin_serializer_->Reset();
+
       plugin_serializer_->SerializeChanges(root, &plugin_update);
 
       size_t old_count = update->nodes.size();
diff --git a/content/renderer/accessibility/render_accessibility_impl.h b/content/renderer/accessibility/render_accessibility_impl.h
index 7ef4d59..63de94d 100644
--- a/content/renderer/accessibility/render_accessibility_impl.h
+++ b/content/renderer/accessibility/render_accessibility_impl.h
@@ -126,7 +126,6 @@
   // An AXObject should be serialized at the next available opportunity.
   void MarkWebAXObjectDirty(
       const blink::WebAXObject& obj,
-      bool subtree,
       ax::mojom::EventFrom event_from = ax::mojom::EventFrom::kNone,
       ax::mojom::Action event_from_action = ax::mojom::Action::kNone,
       std::vector<ui::AXEventIntent> event_intents = {},
diff --git a/content/renderer/accessibility/render_accessibility_impl_browsertest.cc b/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
index 7cb3b0e..0fd39485 100644
--- a/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
+++ b/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
@@ -20,8 +20,8 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "content/public/test/render_view_test.h"
+#include "content/renderer/accessibility/annotations/ax_image_annotator.h"
 #include "content/renderer/accessibility/ax_action_target_factory.h"
-#include "content/renderer/accessibility/ax_image_annotator.h"
 #include "content/renderer/accessibility/render_accessibility_manager.h"
 #include "content/renderer/render_frame_impl.h"
 #include "content/test/test_render_frame.h"
@@ -273,6 +273,15 @@
     return accessibility_manager->GetRenderAccessibilityImpl();
   }
 
+  void MarkSubtreeDirty(const WebAXObject& obj) {
+    unsigned num_children = obj.ChildCount();
+    for (unsigned child_index = 0; child_index < num_children; child_index++) {
+      const WebAXObject& child = obj.ChildAt(child_index);
+      MarkSubtreeDirty(child);
+    }
+    GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(obj);
+  }
+
   // Loads a page given an HTML snippet and initializes its accessibility tree.
   //
   // Consolidates the initialization code required by all tests into a single
@@ -377,7 +386,7 @@
   ClearHandledUpdates();
   WebDocument document = GetMainFrame()->GetDocument();
   WebAXObject root_obj = WebAXObject::FromWebDocument(document);
-  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(root_obj, false);
+  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(root_obj);
   SendPendingAccessibilityEvents();
   EXPECT_EQ(1, CountAccessibilityNodesSentToBrowser());
   {
@@ -443,7 +452,7 @@
 
   // Send a childrenChanged on "A".
   ClearHandledUpdates();
-  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(node_a, false);
+  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(node_a);
 
   // Hide node "B" ("C" stays visible).
   ExecuteJavaScriptForTests(
@@ -491,7 +500,7 @@
   WebAXObject node_c = node_b.ChildAt(0);
 
   // Send a childrenChanged on "A" and show node "B",
-  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(node_a, false);
+  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(node_a);
   ExecuteJavaScriptForTests(
       "document.getElementById('B').style.visibility = 'visible';");
 
@@ -1005,14 +1014,14 @@
       "Accessibility.Performance.SendPendingAccessibilityEvents.PostLoad2", 0);
 
   // Now we start logging.
-  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(root_obj, false);
+  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(root_obj);
   SendPendingAccessibilityEvents();
   histogram_tester.ExpectTotalCount(
       "Accessibility.Performance.SendPendingAccessibilityEvents2", 3);
   histogram_tester.ExpectTotalCount(
       "Accessibility.Performance.SendPendingAccessibilityEvents.PostLoad2", 1);
 
-  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(root_obj, false);
+  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(root_obj);
   SendPendingAccessibilityEvents();
   histogram_tester.ExpectTotalCount(
       "Accessibility.Performance.SendPendingAccessibilityEvents2", 4);
@@ -1354,8 +1363,7 @@
 
   // This should update the annotations of all images on the page, including the
   // already visible one.
-  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(root_obj,
-                                                     true /* subtree */);
+  MarkSubtreeDirty(root_obj);
   SendPendingAccessibilityEvents();
 
   EXPECT_THAT(mock_annotator().image_ids_,
@@ -1391,9 +1399,9 @@
   WebDocument document = GetMainFrame()->GetDocument();
   WebAXObject root_obj = WebAXObject::FromWebDocument(document);
   ASSERT_FALSE(root_obj.IsNull());
+
   // This should update the annotations of all images on the page.
-  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(root_obj,
-                                                     true /* subtree */);
+  MarkSubtreeDirty(root_obj);
   SendPendingAccessibilityEvents();
 
   EXPECT_THAT(mock_annotator().image_ids_,
@@ -1410,8 +1418,7 @@
   ClearHandledUpdates();
   // This should update the annotations of all images on the page, including the
   // now updated image src.
-  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(root_obj,
-                                                     true /* subtree */);
+  MarkSubtreeDirty(root_obj);
   SendPendingAccessibilityEvents();
 
   EXPECT_THAT(mock_annotator().image_ids_,
@@ -1477,7 +1484,7 @@
   // No URL-keyed metrics should be fired after we send one event.
   WebDocument document = GetMainFrame()->GetDocument();
   WebAXObject root_obj = WebAXObject::FromWebDocument(document);
-  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(root_obj, false);
+  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(root_obj);
   SendPendingAccessibilityEvents();
   EXPECT_EQ(0, ukm_recorder()->calls());
   histogram_tester.ExpectTotalCount(
@@ -1486,7 +1493,7 @@
   // No URL-keyed metrics should be fired even after an event that takes
   // 300 ms, but we should now have something to send.
   // This must be >= kMinSerializationTimeToSendInMS
-  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(root_obj, false);
+  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(root_obj);
   SendPendingAccessibilityEvents();
   SetTimeDelayForNextSerialize(base::Milliseconds(300));
   EXPECT_EQ(0, ukm_recorder()->calls());
@@ -1496,7 +1503,7 @@
   // After 1000 seconds have passed, the next time we send an event we should
   // send URL-keyed metrics.
   task_environment_.FastForwardBy(base::Seconds(1000));
-  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(root_obj, false);
+  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(root_obj);
   SendPendingAccessibilityEvents();
   EXPECT_EQ(1, ukm_recorder()->calls());
   histogram_tester.ExpectTotalCount(
@@ -1505,7 +1512,7 @@
   // Send another event that takes a long (simulated) time to serialize.
   // This must be >= kMinSerializationTimeToSend
   SetTimeDelayForNextSerialize(base::Milliseconds(200));
-  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(root_obj, false);
+  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(root_obj);
   SendPendingAccessibilityEvents();
   histogram_tester.ExpectTotalCount(
       "Accessibility.Performance.SendPendingAccessibilityEvents2", 4);
diff --git a/content/services/auction_worklet/bidder_worklet.cc b/content/services/auction_worklet/bidder_worklet.cc
index 1155d13..0de4ce2 100644
--- a/content/services/auction_worklet/bidder_worklet.cc
+++ b/content/services/auction_worklet/bidder_worklet.cc
@@ -28,7 +28,9 @@
 #include "base/trace_event/trace_event.h"
 #include "base/types/optional_util.h"
 #include "content/services/auction_worklet/auction_v8_helper.h"
+#include "content/services/auction_worklet/auction_v8_logger.h"
 #include "content/services/auction_worklet/bidder_lazy_filler.h"
+#include "content/services/auction_worklet/deprecated_url_lazy_filler.h"
 #include "content/services/auction_worklet/direct_from_seller_signals_requester.h"
 #include "content/services/auction_worklet/for_debugging_only_bindings.h"
 #include "content/services/auction_worklet/private_aggregation_bindings.h"
@@ -135,17 +137,6 @@
   return !result.IsNothing() && result.FromJust();
 }
 
-bool SetRenderUrl(v8::Isolate* isolate,
-                  v8::Local<v8::Object> object,
-                  const std::string& val) {
-  v8::Local<v8::Value> v8_value;
-  if (!gin::TryConvertToV8(isolate, val, &v8_value)) {
-    return false;
-  }
-  return SetDictMember(isolate, object, "renderURL", v8_value) &&
-         SetDictMember(isolate, object, "renderUrl", v8_value);
-}
-
 bool CanSetRequestedAdSize(
     const std::optional<blink::AdSize>& requested_ad_size) {
   return requested_ad_size.has_value() &&
@@ -678,6 +669,7 @@
 
   ContextRecyclerScope context_recycler_scope(context_recycler);
   v8::Local<v8::Context> context = context_recycler_scope.GetContext();
+  AuctionV8Logger v8_logger(v8_helper_.get(), context);
 
   v8::LocalVector<v8::Value> args(isolate);
   if (!AppendJsonValueOrNull(v8_helper_.get(), context,
@@ -733,15 +725,21 @@
       break;
   }
 
+  DeprecatedUrlLazyFiller deprecated_render_url(
+      v8_helper_.get(), &v8_logger, &browser_signal_render_url,
+      "browserSignals.renderUrl is deprecated."
+      " Please use browserSignals.renderURL instead.");
   if (!browser_signals_dict.Set("topWindowHostname",
                                 top_window_origin_.host()) ||
       !browser_signals_dict.Set(
           "interestGroupOwner",
           url::Origin::Create(script_source_url_).Serialize()) ||
       !browser_signals_dict.Set(reporting_id_field_name, reporting_id) ||
+      !browser_signals_dict.Set("renderURL",
+                                browser_signal_render_url.spec()) ||
       // TODO(crbug.com/1441988): Remove deprecated `renderUrl` alias.
-      !SetRenderUrl(isolate, browser_signals,
-                    browser_signal_render_url.spec()) ||
+      !deprecated_render_url.AddDeprecatedUrlGetter(browser_signals,
+                                                    "renderUrl") ||
       !browser_signals_dict.Set("bid", browser_signal_bid) ||
       !browser_signals_dict.Set(
           "bidCurrency",
diff --git a/content/services/auction_worklet/bidder_worklet_unittest.cc b/content/services/auction_worklet/bidder_worklet_unittest.cc
index c7a83a19..294c1381 100644
--- a/content/services/auction_worklet/bidder_worklet_unittest.cc
+++ b/content/services/auction_worklet/bidder_worklet_unittest.cc
@@ -6390,6 +6390,87 @@
       "sendReportTo(browserSignals.renderURL)", browser_signal_render_url_);
 }
 
+// Check that accessing `renderUrl` of browserSignals displays a warning.
+//
+// TODO(https://crbug.com/1441988): Remove this test when the field itself is
+// removed.
+TEST_F(BidderWorkletTest, ReportWinBrowserSignalRenderUrlDeprecationWarning) {
+  ScopedInspectorSupport inspector_support(v8_helper_.get());
+
+  AddJavascriptResponse(
+      &url_loader_factory_, interest_group_bidding_url_,
+      CreateReportWinScript("sendReportTo(browserSignals.renderUrl);"));
+
+  BidderWorklet* worklet_impl;
+  auto worklet =
+      CreateWorklet(interest_group_bidding_url_,
+                    /*pause_for_debugger_on_start=*/false, &worklet_impl);
+
+  int id = worklet_impl->context_group_id_for_testing();
+  TestChannel* channel =
+      inspector_support.ConnectDebuggerSessionAndRuntimeEnable(id);
+
+  base::RunLoop run_loop;
+  RunReportWinExpectingResultAsync(worklet_impl, browser_signal_render_url_,
+                                   /*expected_ad_beacon_map=*/{},
+                                   /*expected_ad_macro_map=*/{},
+                                   /*expected_pa_requests=*/{},
+                                   /*expected_reporting_latency_timeout=*/false,
+                                   /*expected_errors=*/{},
+                                   run_loop.QuitClosure());
+  run_loop.Run();
+
+  channel->WaitForAndValidateConsoleMessage(
+      "warning", /*json_args=*/
+      "[{\"type\":\"string\", "
+      "\"value\":\"browserSignals.renderUrl is deprecated. Please use "
+      "browserSignals.renderURL instead.\"}]",
+      /*stack_trace_size=*/1, /*function=*/"reportWin",
+      interest_group_bidding_url_, /*line_number=*/10);
+}
+
+// Check that accessing `renderURL` of browserSignals does not display a
+// warning.
+//
+// TODO(https://crbug.com/1441988): Remove this test when renderUrl is removed.
+TEST_F(BidderWorkletTest, ReportWinBrowserSignalRenderUrlNoDeprecationWarning) {
+  ScopedInspectorSupport inspector_support(v8_helper_.get());
+
+  AddJavascriptResponse(
+      &url_loader_factory_, interest_group_bidding_url_,
+      CreateReportWinScript("sendReportTo(browserSignals.renderURL);"));
+
+  BidderWorklet* worklet_impl;
+  auto worklet =
+      CreateWorklet(interest_group_bidding_url_,
+                    /*pause_for_debugger_on_start=*/false, &worklet_impl);
+
+  int id = worklet_impl->context_group_id_for_testing();
+  TestChannel* channel =
+      inspector_support.ConnectDebuggerSessionAndRuntimeEnable(id);
+
+  base::RunLoop run_loop;
+  RunReportWinExpectingResultAsync(worklet_impl, browser_signal_render_url_,
+                                   /*expected_ad_beacon_map=*/{},
+                                   /*expected_ad_macro_map=*/{},
+                                   /*expected_pa_requests=*/{},
+                                   /*expected_reporting_latency_timeout=*/false,
+                                   /*expected_errors=*/{},
+                                   run_loop.QuitClosure());
+  run_loop.Run();
+
+  // Make sure all events have been received.
+  task_environment_.RunUntilIdle();
+
+  std::list<TestChannel::Event> events = channel->TakeAllEvents();
+  for (const auto& event : events) {
+    if (event.type == TestChannel::Event::Type::Notification) {
+      EXPECT_NE(*event.value.GetDict().FindString("method"),
+                "Runtime.consoleAPICalled");
+    }
+  }
+}
+
 TEST_F(BidderWorkletTest, ReportWinBrowserSignalBid) {
   browser_signal_bid_ = 4;
   RunReportWinWithFunctionBodyExpectingResult(
diff --git a/content/services/auction_worklet/deprecated_url_lazy_filler.h b/content/services/auction_worklet/deprecated_url_lazy_filler.h
index ae54454a..3b791d0 100644
--- a/content/services/auction_worklet/deprecated_url_lazy_filler.h
+++ b/content/services/auction_worklet/deprecated_url_lazy_filler.h
@@ -24,13 +24,13 @@
 class DeprecatedUrlLazyFiller : public LazyFiller {
  public:
   // Creates an object that can set a field on passed in objects to a string
-  // containing `url`, and display `warning` on first access. If `value` is
-  // nullptr / nullopt, does nothing. Name of the field is specified when
-  // calling SetLazyUrl().
+  // containing `url`, and display `warning` on first access. If `url` is
+  // nullptr, does nothing. Name of the field is specified when calling
+  // SetLazyUrl().
   //
   // All passed in pointers must outlive the created
-  // DeprecatedUrlLazyFiller. Additionally, `value` and `warning`
-  // must not be modified until the DeprecatedUrlLazyFiller is destroyed.
+  // DeprecatedUrlLazyFiller. Additionally, `url` and `warning` must not be
+  // modified until the DeprecatedUrlLazyFiller is destroyed.
   DeprecatedUrlLazyFiller(AuctionV8Helper* v8_helper,
                           AuctionV8Logger* v8_logger,
                           const GURL* url,
diff --git a/content/services/auction_worklet/lazy_filler.h b/content/services/auction_worklet/lazy_filler.h
index a6ccd6f..a77e72b 100644
--- a/content/services/auction_worklet/lazy_filler.h
+++ b/content/services/auction_worklet/lazy_filler.h
@@ -21,9 +21,9 @@
 
 // Helper class to populate fields of one or more v8::Objects on first access.
 // The associated v8::Context must be destroyed immediately after the LazyFiller
-// to avoid a UAF. Before would be better, but LazyFillers often have raw
-// pointers to other context-scoped variables (e.g., AuctionV8Loggers) which
-// must be destroyed before the LazyFiller.
+// to avoid a UAF. Before would be better, but LazyFiller subclasses often have
+// raw pointers to other context-scoped variables (e.g., an AuctionV8Logger)
+// which must be destroyed after the LazyFiller.
 //
 // API for implementers is as follows:
 //
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 3deb834..90ef83d 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -2797,7 +2797,7 @@
     "../public/test/permission_type_unittest.cc",
     "../public/test/referrer_unittest.cc",
     "../public/test/test_utils_unittest.cc",
-    "../renderer/accessibility/ax_image_stopwords_unittest.cc",
+    "../renderer/accessibility/annotations/ax_image_stopwords_unittest.cc",
     "../renderer/content_security_policy_util_unittest.cc",
     "../renderer/media/batching_media_log_unittest.cc",
     "../renderer/media/gpu/gpu_video_accelerator_factories_impl_unittest.cc",
diff --git a/content/test/content_test_bundle_data.filelist b/content/test/content_test_bundle_data.filelist
index 141c1e6..75117198 100644
--- a/content/test/content_test_bundle_data.filelist
+++ b/content/test/content_test_bundle_data.filelist
@@ -3194,6 +3194,8 @@
 data/accessibility/html/disabled-expected-android-external.txt
 data/accessibility/html/disabled-expected-android.txt
 data/accessibility/html/disabled-expected-blink.txt
+data/accessibility/html/disabled-with-subtree-expected-blink.txt
+data/accessibility/html/disabled-with-subtree.html
 data/accessibility/html/disabled.html
 data/accessibility/html/div-expected-android-assist-data.txt
 data/accessibility/html/div-expected-android-external.txt
diff --git a/content/test/data/accessibility/html/abbr-expected-uia-win.txt b/content/test/data/accessibility/html/abbr-expected-uia-win.txt
index 489fed7..b91fa13 100644
--- a/content/test/data/accessibility/html/abbr-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/abbr-expected-uia-win.txt
@@ -1,6 +1,6 @@
 Document
 ++Group IsControlElement=false
 ++++Text Name='The '
-++++Text Name='World Health Organization'
+++++Group Name='World Health Organization'
 ++++++Text Name='WHO'
 ++++Text Name=' was founded in 1948.'
diff --git a/content/test/data/accessibility/html/disabled-with-subtree-expected-blink.txt b/content/test/data/accessibility/html/disabled-with-subtree-expected-blink.txt
new file mode 100644
index 0000000..bce04cf
--- /dev/null
+++ b/content/test/data/accessibility/html/disabled-with-subtree-expected-blink.txt
@@ -0,0 +1,8 @@
+rootWebArea name='done'
+++genericContainer ignored
+++++genericContainer
+++++++button name='new' restriction=disabled
+++++++++staticText name='new'
+++++++++++inlineTextBox name='new'
+++++++++genericContainer ignored
+
diff --git a/content/test/data/accessibility/html/disabled-with-subtree.html b/content/test/data/accessibility/html/disabled-with-subtree.html
new file mode 100644
index 0000000..3260779c
--- /dev/null
+++ b/content/test/data/accessibility/html/disabled-with-subtree.html
@@ -0,0 +1,13 @@
+<!--
+@WAIT-FOR:done
+-->
+<button id="button1" disabled>old<div></div></button>
+<script>
+function update() {
+  document.getElementById("button1").childNodes[0].textContent = "new";
+  document.title="done";
+}
+document.addEventListener('DOMContentLoaded', () => {
+  setTimeout(update, 0);
+});
+</script>
diff --git a/content/test/data/accessibility/html/fieldset-expected-uia-win.txt b/content/test/data/accessibility/html/fieldset-expected-uia-win.txt
index 534a4dcc..d47d4489 100644
--- a/content/test/data/accessibility/html/fieldset-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/fieldset-expected-uia-win.txt
@@ -1,9 +1,9 @@
 Document
 ++Group
 ++++Group Name='Browser Engines:'
-++++++Text IsControlElement=false
+++++++Group IsControlElement=false
 ++++++++Text Name='Browser Engines:'
 ++Group Name='Which cake do you prefer?'
-++++Text IsControlElement=false
+++++Group IsControlElement=false
 ++++++Text Name='Which cake do you prefer?'
 ++++++++Text Name='Which cake do you prefer?' IsControlElement=false
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/figcaption-expected-uia-win.txt b/content/test/data/accessibility/html/figcaption-expected-uia-win.txt
index b496700..d75e01b3 100644
--- a/content/test/data/accessibility/html/figcaption-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/figcaption-expected-uia-win.txt
@@ -1,9 +1,9 @@
 Document
 ++Group
 ++++Image Name='This is a green box.'
-++++Text IsControlElement=false
+++++Group IsControlElement=false
 ++++++Text Name='Fig.1 - A green Box'
 ++Group
 ++++Image Name='This is a blue box.'
-++++Text IsControlElement=false
+++++Group IsControlElement=false
 ++++++Text Name='Fig.2 - A blue Box'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/legend-expected-uia-win.txt b/content/test/data/accessibility/html/legend-expected-uia-win.txt
index 1b2cf6a..553633d 100644
--- a/content/test/data/accessibility/html/legend-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/legend-expected-uia-win.txt
@@ -1,7 +1,7 @@
 Document
 ++Group
 ++++Group Name='Browser Engines:'
-++++++Text IsControlElement=false
+++++++Group IsControlElement=false
 ++++++++Text Name='Browser Engines:'
 ++++++Text Name='Browser: '
 ++++++Edit
diff --git a/content/test/data/fuzzer_corpus/ad_auction_service_mojolpm_fuzzer/basic_join.textproto b/content/test/data/fuzzer_corpus/ad_auction_service_mojolpm_fuzzer/basic_join.textproto
new file mode 100644
index 0000000..396b84b
--- /dev/null
+++ b/content/test/data/fuzzer_corpus/ad_auction_service_mojolpm_fuzzer/basic_join.textproto
@@ -0,0 +1,51 @@
+actions {
+  new_ad_auction_service {
+    id: 1
+  }
+}
+actions {
+  ad_auction_service_remote_action {
+    id: 1
+    m_join_interest_group {
+      m_group {
+        new {
+          id: 1
+          m_name: "shoes"
+          m_owner: {
+            new {
+              id: 1
+              m_scheme: "https"
+              m_host: "owner.test"
+              m_port: 443
+            }
+          }
+          m_expiry {
+            new {
+              id: 1
+              # Set a large time that's far in the future (int64 max) -- the
+              # browser will clamp this to the max expiration.
+              m_internal_value: 0x7FFFFFFFFFFFFFFF
+            }
+          }
+
+          # The default values of required fields need to be explicitly set to
+          # avoid build warnings.
+          m_priority: 0.0
+          m_enable_bidding_signals_prioritization: false
+          m_all_sellers_capabilities {
+          }
+          m_execution_mode: InterestGroup_ExecutionMode_kCompatibilityMode
+          m_trusted_bidding_signals_slot_size_mode: InterestGroup_TrustedBiddingSignalsSlotSizeMode_kNone
+          m_auction_server_request_flags {
+          }
+          m_max_trusted_bidding_signals_url_length: 0
+        }
+      }
+    }
+  }
+}
+sequences {
+  action_indexes: 0
+  action_indexes: 1
+}
+sequence_indexes: 0
diff --git a/content/test/data/fuzzer_corpus/ad_auction_service_mojolpm_fuzzer/basic_update.textproto b/content/test/data/fuzzer_corpus/ad_auction_service_mojolpm_fuzzer/basic_update.textproto
new file mode 100644
index 0000000..869e239
--- /dev/null
+++ b/content/test/data/fuzzer_corpus/ad_auction_service_mojolpm_fuzzer/basic_update.textproto
@@ -0,0 +1,70 @@
+actions {
+  new_ad_auction_service {
+    id: 1
+  }
+}
+actions {
+  ad_auction_service_remote_action {
+    id: 1
+    m_join_interest_group {
+      m_group {
+        new {
+          id: 1
+          m_name: "shoes"
+          m_owner: {
+            new {
+              id: 1
+              m_scheme: "https"
+              m_host: "owner.test"
+              m_port: 443
+            }
+          }
+          m_expiry {
+            new {
+              id: 1
+              # Set a large time that's far in the future (int64 max) -- the
+              # browser will clamp this to the max expiration.
+              m_internal_value: 0x7FFFFFFFFFFFFFFF
+            }
+          }
+          m_update_url {
+            new {
+              id: 1
+              m_url: "https://owner.test:443/update"
+            }
+          }
+
+          # The default values of required fields need to be explicitly set to
+          # avoid build warnings.
+          m_priority: 0.0
+          m_enable_bidding_signals_prioritization: false
+          m_all_sellers_capabilities {
+          }
+          m_execution_mode: InterestGroup_ExecutionMode_kCompatibilityMode
+          m_trusted_bidding_signals_slot_size_mode: InterestGroup_TrustedBiddingSignalsSlotSizeMode_kNone
+          m_auction_server_request_flags {
+          }
+          m_max_trusted_bidding_signals_url_length: 0
+        }
+      }
+    }
+  }
+}
+actions {
+  ad_auction_service_remote_action {
+    id: 1
+    m_update_ad_interest_groups {
+    }
+  }
+}
+actions {
+  run_until_idle {
+  }
+}
+sequences {
+  action_indexes: 0
+  action_indexes: 1
+  action_indexes: 2
+  action_indexes: 3
+}
+sequence_indexes: 0
diff --git a/content/test/fuzzer/BUILD.gn b/content/test/fuzzer/BUILD.gn
index 5fcfd38..9969832 100644
--- a/content/test/fuzzer/BUILD.gn
+++ b/content/test/fuzzer/BUILD.gn
@@ -423,6 +423,30 @@
   proto_deps = [ "//third_party/blink/public/mojom:mojom_platform_mojolpm" ]
 }
 
+mojolpm_fuzzer_test("ad_auction_service_mojolpm_fuzzer") {
+  sources =
+      [ "../../browser/interest_group/ad_auction_service_mojolpm_fuzzer.cc" ]
+
+  proto_source =
+      "../../browser/interest_group/ad_auction_service_mojolpm_fuzzer.proto"
+  testcase_proto_kind = "content.fuzzing.ad_auction_service.proto.Testcase"
+
+  deps = [
+    ":mojolpm_fuzzer_support",
+    "//content/browser:browser",
+    "//content/browser:for_content_tests",
+    "//content/public/browser:browser_sources",
+    "//services/data_decoder/public/cpp:test_support",
+  ]
+
+  proto_deps = [ "//third_party/blink/public/mojom:mojom_platform_mojolpm" ]
+
+  seed_corpus_sources = [
+    "//content/test/data/fuzzer_corpus/ad_auction_service_mojolpm_fuzzer/basic_join.textproto",
+    "//content/test/data/fuzzer_corpus/ad_auction_service_mojolpm_fuzzer/basic_update.textproto",
+  ]
+}
+
 if (enable_mojom_fuzzer) {
   static_library("controller_presentation_service_delegate_for_fuzzing") {
     # Should only be used in the fuzzer this was made for
diff --git a/content/test/fuzzer/mojolpm_fuzzer_support.cc b/content/test/fuzzer/mojolpm_fuzzer_support.cc
index 4c562f2..5e081810 100644
--- a/content/test/fuzzer/mojolpm_fuzzer_support.cc
+++ b/content/test/fuzzer/mojolpm_fuzzer_support.cc
@@ -91,5 +91,10 @@
 void RenderViewHostTestHarnessAdapter::TearDown() {
   RenderViewHostTestHarness::TearDown();
 }
+
+BrowserTaskEnvironment* RenderViewHostTestHarnessAdapter::task_environment() {
+  return RenderViewHostTestHarness::task_environment();
+}
+
 }  // namespace mojolpm
-}  // namespace content
\ No newline at end of file
+}  // namespace content
diff --git a/content/test/fuzzer/mojolpm_fuzzer_support.h b/content/test/fuzzer/mojolpm_fuzzer_support.h
index 349a3109..ae73dc5 100644
--- a/content/test/fuzzer/mojolpm_fuzzer_support.h
+++ b/content/test/fuzzer/mojolpm_fuzzer_support.h
@@ -77,6 +77,8 @@
   void SetUp() override;
   void TearDown() override;
 
+  BrowserTaskEnvironment* task_environment();
+
  private:
   void TestBody() override {}
 };
diff --git a/content/test/gpu/gpu_tests/pixel_test_pages.py b/content/test/gpu/gpu_tests/pixel_test_pages.py
index a586c4d3..295517cb 100644
--- a/content/test/gpu/gpu_tests/pixel_test_pages.py
+++ b/content/test/gpu/gpu_tests/pixel_test_pages.py
@@ -44,10 +44,11 @@
 
 # The optimizer script spat out pretty similar values for most MP4 tests, so
 # combine into a single set of parameters.
-GENERAL_MP4_ALGO = algo.SobelMatchingAlgorithm(max_different_pixels=56300,
-                                               pixel_delta_threshold=35,
-                                               edge_threshold=80,
-                                               ignored_border_thickness=1)
+GENERAL_MP4_ALGO = algo.SobelMatchingAlgorithm(
+    max_different_pixels=56300,
+    pixel_per_channel_delta_threshold=15,
+    edge_threshold=80,
+    ignored_border_thickness=1)
 
 ROUNDING_ERROR_ALGO = algo.FuzzyMatchingAlgorithm(
     max_different_pixels=100000000, pixel_per_channel_delta_threshold=1)
@@ -281,7 +282,8 @@
                       base_name + '_Canvas2DRedBox',
                       test_rect=[0, 0, 300, 300],
                       matching_algorithm=algo.FuzzyMatchingAlgorithm(
-                          max_different_pixels=130, pixel_delta_threshold=3)),
+                          max_different_pixels=130,
+                          pixel_per_channel_delta_threshold=2)),
         PixelTestPage('pixel_canvas2d_untagged.html',
                       base_name + '_Canvas2DUntagged',
                       test_rect=[0, 0, 257, 257]),
@@ -356,7 +358,7 @@
             test_rect=[0, 0, 240, 135],
             matching_algorithm=algo.SobelMatchingAlgorithm(
                 max_different_pixels=41700,
-                pixel_delta_threshold=15,
+                pixel_per_channel_delta_threshold=5,
                 edge_threshold=40,
                 ignored_border_thickness=1)),
         PixelTestPage(
@@ -384,7 +386,7 @@
             test_rect=[0, 0, 240, 135],
             matching_algorithm=algo.SobelMatchingAlgorithm(
                 max_different_pixels=30500,
-                pixel_delta_threshold=15,
+                pixel_per_channel_delta_threshold=5,
                 edge_threshold=70,
                 ignored_border_thickness=1)),
         PixelTestPage('pixel_video_vp9.html?width=240&height=135&use_timer=1',
@@ -392,7 +394,7 @@
                       test_rect=[0, 0, 240, 135],
                       matching_algorithm=algo.SobelMatchingAlgorithm(
                           max_different_pixels=114000,
-                          pixel_delta_threshold=30,
+                          pixel_per_channel_delta_threshold=15,
                           edge_threshold=20,
                           ignored_border_thickness=1)),
         PixelTestPage('pixel_video_av1.html?width=240&height=135&use_timer=1',
@@ -400,7 +402,7 @@
                       test_rect=[0, 0, 240, 135],
                       matching_algorithm=algo.SobelMatchingAlgorithm(
                           max_different_pixels=114000,
-                          pixel_delta_threshold=30,
+                          pixel_per_channel_delta_threshold=15,
                           edge_threshold=20,
                           ignored_border_thickness=1)),
         PixelTestPage('pixel_video_hevc.html?width=240&height=135&use_timer=1',
@@ -408,7 +410,7 @@
                       test_rect=[0, 0, 240, 135],
                       matching_algorithm=algo.SobelMatchingAlgorithm(
                           max_different_pixels=114000,
-                          pixel_delta_threshold=30,
+                          pixel_per_channel_delta_threshold=15,
                           edge_threshold=20,
                           ignored_border_thickness=1)),
         PixelTestPage(
@@ -429,7 +431,8 @@
             # threshold, so use fuzzy for now since it's slightly more
             # efficient.
             matching_algorithm=algo.FuzzyMatchingAlgorithm(
-                max_different_pixels=31700, pixel_delta_threshold=20),
+                max_different_pixels=31700,
+                pixel_per_channel_delta_threshold=10),
             expected_per_process_crashes={
                 CRASH_TYPE_GPU: 1,
             }),
@@ -441,7 +444,7 @@
                       test_rect=[0, 0, 240, 135],
                       matching_algorithm=algo.SobelMatchingAlgorithm(
                           max_different_pixels=54400,
-                          pixel_delta_threshold=30,
+                          pixel_per_channel_delta_threshold=15,
                           edge_threshold=250,
                           ignored_border_thickness=1),
                       expected_per_process_crashes={
@@ -453,7 +456,7 @@
             test_rect=[0, 0, 240, 135],
             matching_algorithm=algo.SobelMatchingAlgorithm(
                 max_different_pixels=1000,
-                pixel_delta_threshold=20,
+                pixel_per_channel_delta_threshold=10,
                 edge_threshold=40,
                 ignored_border_thickness=1)),
         PixelTestPage('pixel_webgl_premultiplied_alpha_false.html',
@@ -702,7 +705,7 @@
             browser_args=browser_args,
             matching_algorithm=algo.SobelMatchingAlgorithm(
                 max_different_pixels=10,
-                pixel_delta_threshold=30,
+                pixel_per_channel_delta_threshold=15,
                 edge_threshold=100),
             # Small Fuchsia screens result in an incomplete capture
             # without this.
@@ -745,8 +748,8 @@
 
     # The sRGB tests have been observed to create a large number
     # (~15,000) of pixels with difference ~3.
-    srgb_fuzzy_algo = algo.FuzzyMatchingAlgorithm(max_different_pixels=20000,
-                                                  pixel_delta_threshold=3)
+    srgb_fuzzy_algo = algo.FuzzyMatchingAlgorithm(
+        max_different_pixels=20000, pixel_per_channel_delta_threshold=2)
 
     return [
         PixelTestPage('pixel_offscreenCanvas_transfer_after_style_resize.html',
@@ -955,7 +958,7 @@
     # portions of the image are prone to noise, hence the large max different
     # pixels value.
     filter_effect_fuzzy_algo = algo.FuzzyMatchingAlgorithm(
-        max_different_pixels=57500, pixel_delta_threshold=15)
+        max_different_pixels=57500, pixel_per_channel_delta_threshold=10)
 
     return [
         PixelTestPage('pixel_canvas2d_webgl.html',
@@ -1155,17 +1158,17 @@
     # value for each parameter in that tier.
     strict_dc_sobel_algorithm = algo.SobelMatchingAlgorithm(
         max_different_pixels=1000,
-        pixel_delta_threshold=5,
+        pixel_per_channel_delta_threshold=3,
         edge_threshold=250,
         ignored_border_thickness=1)
     permissive_dc_sobel_algorithm = algo.SobelMatchingAlgorithm(
         max_different_pixels=16800,
-        pixel_delta_threshold=20,
+        pixel_per_channel_delta_threshold=10,
         edge_threshold=30,
         ignored_border_thickness=1)
     very_permissive_dc_sobel_algorithm = algo.SobelMatchingAlgorithm(
         max_different_pixels=30400,
-        pixel_delta_threshold=45,
+        pixel_per_channel_delta_threshold=20,
         edge_threshold=10,
         ignored_border_thickness=1,
     )
@@ -1290,7 +1293,7 @@
             # Much larger image than other VP9 tests.
             matching_algorithm=algo.SobelMatchingAlgorithm(
                 max_different_pixels=504000,
-                pixel_delta_threshold=10,
+                pixel_per_channel_delta_threshold=5,
                 edge_threshold=10,
                 ignored_border_thickness=1,
             )),
diff --git a/device/base/event_utils_winrt.h b/device/base/event_utils_winrt.h
index d07b146..0ee96ea7 100644
--- a/device/base/event_utils_winrt.h
+++ b/device/base/event_utils_winrt.h
@@ -10,6 +10,7 @@
 #include <wrl/event.h>
 
 #include <ios>
+#include <optional>
 #include <utility>
 
 #include "base/functional/bind.h"
@@ -19,7 +20,6 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/win/windows_types.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -39,7 +39,7 @@
           typename Args,
           typename SenderAbi,
           typename ArgsAbi>
-absl::optional<EventRegistrationToken> AddEventHandler(
+std::optional<EventRegistrationToken> AddEventHandler(
     Interface* interface_called,
     internal::IMemberFunction<Interface,
                               ABI::Windows::Foundation::IEventHandler<Args*>*,
@@ -61,7 +61,7 @@
   if (FAILED(hr)) {
     DVLOG(2) << "Adding EventHandler failed: "
              << "0x" << std::hex << hr;
-    return absl::nullopt;
+    return std::nullopt;
   }
   return token;
 }
diff --git a/device/bluetooth/adapter.cc b/device/bluetooth/adapter.cc
index 2a7822b..5839eba9 100644
--- a/device/bluetooth/adapter.cc
+++ b/device/bluetooth/adapter.cc
@@ -242,7 +242,7 @@
   // to previously. Use the ConnectDevice() API, if available, to connect to it.
 #if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
   adapter_->ConnectDevice(
-      address, /*address_type=*/absl::nullopt,
+      address, /*address_type=*/std::nullopt,
       base::BindOnce(&Adapter::OnDeviceFetchedForInsecureServiceConnection,
                      weak_ptr_factory_.GetWeakPtr(), request_id),
       base::BindOnce(&Adapter::OnConnectToServiceError,
@@ -396,7 +396,7 @@
 void Adapter::OnGattConnect(
     ConnectToDeviceCallback callback,
     std::unique_ptr<device::BluetoothGattConnection> connection,
-    absl::optional<device::BluetoothDevice::ConnectErrorCode> error_code) {
+    std::optional<device::BluetoothDevice::ConnectErrorCode> error_code) {
   if (error_code.has_value()) {
     std::move(callback).Run(
         mojo::ConvertTo<mojom::ConnectResult>(error_code.value()),
@@ -510,7 +510,7 @@
   ExecuteConnectToServiceCallback(request_id, /*result=*/nullptr);
 
 #if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
-  absl::optional<ConnectToServiceInsecurelyResult> result =
+  std::optional<ConnectToServiceInsecurelyResult> result =
       ExtractResultFromErrorString(message);
   if (result) {
     RecordConnectToServiceInsecurelyResult(*result);
diff --git a/device/bluetooth/adapter.h b/device/bluetooth/adapter.h
index 19966bb..78bbd3f 100644
--- a/device/bluetooth/adapter.h
+++ b/device/bluetooth/adapter.h
@@ -111,7 +111,7 @@
   void OnGattConnect(
       ConnectToDeviceCallback callback,
       std::unique_ptr<device::BluetoothGattConnection> connection,
-      absl::optional<device::BluetoothDevice::ConnectErrorCode> error_code);
+      std::optional<device::BluetoothDevice::ConnectErrorCode> error_code);
 
   void OnRegisterAdvertisement(
       RegisterAdvertisementCallback callback,
diff --git a/device/bluetooth/adapter_unittest.cc b/device/bluetooth/adapter_unittest.cc
index cc43ba0..c25e2d3d 100644
--- a/device/bluetooth/adapter_unittest.cc
+++ b/device/bluetooth/adapter_unittest.cc
@@ -379,7 +379,7 @@
   // change where the device has no RSSI. This will result in a failed
   // connect-to-service result.
   EXPECT_CALL(*mock_unknown_bluetooth_device_, GetInquiryRSSI())
-      .WillRepeatedly(Return(absl::nullopt));
+      .WillRepeatedly(Return(std::nullopt));
   adapter_->DeviceChanged(mock_bluetooth_adapter_.get(),
                           mock_unknown_bluetooth_device_.get());
   run_loop.Run();
diff --git a/device/bluetooth/bluetooth_adapter.h b/device/bluetooth/bluetooth_adapter.h
index 786bc881..0e80e39 100644
--- a/device/bluetooth/bluetooth_adapter.h
+++ b/device/bluetooth/bluetooth_adapter.h
@@ -9,6 +9,7 @@
 
 #include <list>
 #include <memory>
+#include <optional>
 #include <set>
 #include <string>
 #include <unordered_map>
@@ -28,7 +29,6 @@
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/bluetooth_discovery_filter.h"
 #include "device/bluetooth/bluetooth_export.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_CHROMEOS)
 #include "device/bluetooth/bluetooth_low_energy_scan_session.h"
@@ -152,11 +152,11 @@
     // returns the raw values that have been parsed from EIR.
     virtual void DeviceAdvertisementReceived(
         const std::string& device_address,
-        const absl::optional<std::string>& device_name,
-        const absl::optional<std::string>& advertisement_name,
-        absl::optional<int8_t> rssi,
-        absl::optional<int8_t> tx_power,
-        absl::optional<uint16_t> appearance,
+        const std::optional<std::string>& device_name,
+        const std::optional<std::string>& advertisement_name,
+        std::optional<int8_t> rssi,
+        std::optional<int8_t> tx_power,
+        std::optional<uint16_t> appearance,
         const BluetoothDevice::UUIDList& advertised_uuids,
         const BluetoothDevice::ServiceDataMap& service_data_map,
         const BluetoothDevice::ManufacturerDataMap& manufacturer_data_map) {}
@@ -356,9 +356,9 @@
     ServiceOptions();
     ~ServiceOptions();
 
-    absl::optional<int> channel;
-    absl::optional<int> psm;
-    absl::optional<std::string> name;
+    std::optional<int> channel;
+    std::optional<int> psm;
+    std::optional<std::string> name;
 
     // Clients can configure this option to choose if they want to enforce
     // bonding with remote devices that connect to this device. Options:
@@ -368,7 +368,7 @@
     //     use this are responsible for securing their communication at the
     //     application level.
     //   * Set to true: bonding is enforced by the local device.
-    absl::optional<bool> require_authentication;
+    std::optional<bool> require_authentication;
   };
 
   // The ErrorCallback is used for methods that can fail in which case it is
@@ -665,7 +665,7 @@
   // a valid reference (in which case this method will fail).
   virtual void ConnectDevice(
       const std::string& address,
-      const absl::optional<BluetoothDevice::AddressType>& address_type,
+      const std::optional<BluetoothDevice::AddressType>& address_type,
       ConnectDeviceCallback callback,
       ConnectDeviceErrorCallback error_callback) = 0;
 #endif
diff --git a/device/bluetooth/bluetooth_adapter_android.cc b/device/bluetooth/bluetooth_adapter_android.cc
index 24f9835..36d09eb 100644
--- a/device/bluetooth/bluetooth_adapter_android.cc
+++ b/device/bluetooth/bluetooth_adapter_android.cc
@@ -241,18 +241,18 @@
       BluetoothDevice::ClampPower(rssi),
       // Android uses -1 to indicate no advertising flags.
       // https://developer.android.com/reference/android/bluetooth/le/ScanRecord.html#getAdvertiseFlags()
-      advertisement_flags == -1 ? absl::nullopt
-                                : absl::make_optional(advertisement_flags),
+      advertisement_flags == -1 ? std::nullopt
+                                : std::make_optional(advertisement_flags),
       advertised_bluetooth_uuids,
       // Android uses INT32_MIN to indicate no Advertised Tx Power.
       // https://developer.android.com/reference/android/bluetooth/le/ScanRecord.html#getTxPowerLevel()
-      tx_power == INT32_MIN ? absl::nullopt
-                            : absl::make_optional(clamped_tx_power),
+      tx_power == INT32_MIN ? std::nullopt
+                            : std::make_optional(clamped_tx_power),
       service_data_map, manufacturer_data_map);
 
   for (auto& observer : observers_) {
-    absl::optional<std::string> device_name_opt = device_android->GetName();
-    absl::optional<std::string> advertisement_name_opt;
+    std::optional<std::string> device_name_opt = device_android->GetName();
+    std::optional<std::string> advertisement_name_opt;
     if (local_name)
       advertisement_name_opt = ConvertJavaStringToUTF8(env, local_name);
 
@@ -261,9 +261,9 @@
         BluetoothDevice::ClampPower(rssi),
         // Android uses INT32_MIN to indicate no Advertised Tx Power.
         // https://developer.android.com/reference/android/bluetooth/le/ScanRecord.html#getTxPowerLevel()
-        tx_power == INT32_MIN ? absl::nullopt
-                              : absl::make_optional(clamped_tx_power),
-        absl::nullopt, /* TODO(crbug.com/588083) Implement appearance */
+        tx_power == INT32_MIN ? std::nullopt
+                              : std::make_optional(clamped_tx_power),
+        std::nullopt, /* TODO(crbug.com/588083) Implement appearance */
         advertised_bluetooth_uuids, service_data_map, manufacturer_data_map);
   }
 
diff --git a/device/bluetooth/bluetooth_adapter_mac.h b/device/bluetooth/bluetooth_adapter_mac.h
index 4f5d698..bd0ca77 100644
--- a/device/bluetooth/bluetooth_adapter_mac.h
+++ b/device/bluetooth/bluetooth_adapter_mac.h
@@ -147,7 +147,7 @@
 
   std::string address_;
   bool classic_powered_ = false;
-  absl::optional<bool> is_present_for_testing_;
+  std::optional<bool> is_present_for_testing_;
 
   // Function returning the state of the HostController. Can be overridden for
   // tests.
diff --git a/device/bluetooth/bluetooth_adapter_unittest.cc b/device/bluetooth/bluetooth_adapter_unittest.cc
index d38d24f..ea22e9a4 100644
--- a/device/bluetooth/bluetooth_adapter_unittest.cc
+++ b/device/bluetooth/bluetooth_adapter_unittest.cc
@@ -140,7 +140,7 @@
                         AdvertisementErrorCallback error_callback) override {}
   void ConnectDevice(
       const std::string& address,
-      const absl::optional<BluetoothDevice::AddressType>& address_type,
+      const std::optional<BluetoothDevice::AddressType>& address_type,
       ConnectDeviceCallback callback,
       ConnectDeviceErrorCallback error_callback) override {}
 #endif
diff --git a/device/bluetooth/bluetooth_adapter_winrt.cc b/device/bluetooth/bluetooth_adapter_winrt.cc
index 64df950..9be10888 100644
--- a/device/bluetooth/bluetooth_adapter_winrt.cc
+++ b/device/bluetooth/bluetooth_adapter_winrt.cc
@@ -151,7 +151,7 @@
   return true;
 }
 
-absl::optional<std::vector<uint8_t>> ExtractVector(IBuffer* buffer) {
+std::optional<std::vector<uint8_t>> ExtractVector(IBuffer* buffer) {
   ComPtr<IDataReaderStatics> data_reader_statics;
   HRESULT hr = base::win::GetActivationFactory<
       IDataReaderStatics, RuntimeClass_Windows_Storage_Streams_DataReader>(
@@ -160,7 +160,7 @@
     BLUETOOTH_LOG(ERROR)
         << "Getting DataReaderStatics Activation Factory failed: "
         << logging::SystemErrorCodeToString(hr);
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   ComPtr<IDataReader> data_reader;
@@ -168,7 +168,7 @@
   if (FAILED(hr)) {
     BLUETOOTH_LOG(ERROR) << "FromBuffer() failed: "
                          << logging::SystemErrorCodeToString(hr);
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   uint32_t buffer_length;
@@ -176,7 +176,7 @@
   if (FAILED(hr)) {
     BLUETOOTH_LOG(ERROR) << "get_Length() failed: "
                          << logging::SystemErrorCodeToString(hr);
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   std::vector<uint8_t> bytes(buffer_length);
@@ -184,27 +184,27 @@
   if (FAILED(hr)) {
     BLUETOOTH_LOG(ERROR) << "ReadBytes() failed: "
                          << logging::SystemErrorCodeToString(hr);
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return bytes;
 }
 
-absl::optional<uint8_t> ExtractFlags(IBluetoothLEAdvertisement* advertisement) {
+std::optional<uint8_t> ExtractFlags(IBluetoothLEAdvertisement* advertisement) {
   if (!advertisement)
-    return absl::nullopt;
+    return std::nullopt;
 
   ComPtr<IReference<BluetoothLEAdvertisementFlags>> flags_ref;
   HRESULT hr = advertisement->get_Flags(&flags_ref);
   if (FAILED(hr)) {
     BLUETOOTH_LOG(ERROR) << "get_Flags() failed: "
                          << logging::SystemErrorCodeToString(hr);
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   if (!flags_ref) {
     BLUETOOTH_LOG(DEBUG) << "No advertisement flags found.";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   BluetoothLEAdvertisementFlags flags;
@@ -212,7 +212,7 @@
   if (FAILED(hr)) {
     BLUETOOTH_LOG(ERROR) << "get_Value() failed: "
                          << logging::SystemErrorCodeToString(hr);
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return flags;
@@ -379,10 +379,9 @@
 // Similarly to extracting the service data Windows does not provide a specific
 // API to extract the tx power. Thus we also parse the raw data sections here.
 // If present, we expect a single entry for tx power with a blob of size 1 byte.
-absl::optional<int8_t> ExtractTxPower(
-    IBluetoothLEAdvertisement* advertisement) {
+std::optional<int8_t> ExtractTxPower(IBluetoothLEAdvertisement* advertisement) {
   if (!advertisement)
-    return absl::nullopt;
+    return std::nullopt;
 
   ComPtr<IVectorView<BluetoothLEAdvertisementDataSection*>> data_sections;
   HRESULT hr = advertisement->GetSectionsByType(
@@ -390,17 +389,17 @@
   if (FAILED(hr)) {
     BLUETOOTH_LOG(ERROR) << "GetSectionsByType() failed: "
                          << logging::SystemErrorCodeToString(hr);
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   std::vector<ComPtr<IBluetoothLEAdvertisementDataSection>> vector;
   if (!ToStdVector(data_sections.Get(), &vector) || vector.empty())
-    return absl::nullopt;
+    return std::nullopt;
 
   if (vector.size() != 1u) {
     BLUETOOTH_LOG(ERROR) << "Unexpected number of data sections: "
                          << vector.size();
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   ComPtr<IBuffer> buffer;
@@ -408,16 +407,16 @@
   if (FAILED(hr)) {
     BLUETOOTH_LOG(ERROR) << "get_Data() failed: "
                          << logging::SystemErrorCodeToString(hr);
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   auto bytes = ExtractVector(buffer.Get());
   if (!bytes)
-    return absl::nullopt;
+    return std::nullopt;
 
   if (bytes->size() != 1) {
     BLUETOOTH_LOG(ERROR) << "Unexpected number of bytes: " << bytes->size();
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return bytes->front();
@@ -435,22 +434,22 @@
   return advertisement;
 }
 
-absl::optional<std::string> ExtractDeviceName(
+std::optional<std::string> ExtractDeviceName(
     IBluetoothLEAdvertisement* advertisement) {
   if (!advertisement)
-    return absl::nullopt;
+    return std::nullopt;
 
   HSTRING local_name;
   HRESULT hr = advertisement->get_LocalName(&local_name);
   if (FAILED(hr)) {
     BLUETOOTH_LOG(ERROR) << "Getting Local Name failed: "
                          << logging::SystemErrorCodeToString(hr);
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   // Return early otherwise ScopedHString will create an empty string.
   if (!local_name)
-    return absl::nullopt;
+    return std::nullopt;
 
   return base::win::ScopedHString(local_name).GetAsUTF8();
 }
@@ -1323,9 +1322,9 @@
 
   // Extract the remaining advertisement data.
   ComPtr<IBluetoothLEAdvertisement> advertisement = GetAdvertisement(received);
-  absl::optional<std::string> device_name =
+  std::optional<std::string> device_name =
       ExtractDeviceName(advertisement.Get());
-  absl::optional<int8_t> tx_power = ExtractTxPower(advertisement.Get());
+  std::optional<int8_t> tx_power = ExtractTxPower(advertisement.Get());
   BluetoothDevice::UUIDList advertised_uuids =
       ExtractAdvertisedUUIDs(advertisement.Get());
   BluetoothDevice::ServiceDataMap service_data_map =
diff --git a/device/bluetooth/bluetooth_adapter_winrt.h b/device/bluetooth/bluetooth_adapter_winrt.h
index b87fcf6..4f9313f 100644
--- a/device/bluetooth/bluetooth_adapter_winrt.h
+++ b/device/bluetooth/bluetooth_adapter_winrt.h
@@ -230,21 +230,21 @@
       adapter_;
 
   Microsoft::WRL::ComPtr<ABI::Windows::Devices::Radios::IRadio> radio_;
-  absl::optional<EventRegistrationToken> radio_state_changed_token_;
+  std::optional<EventRegistrationToken> radio_state_changed_token_;
 
   Microsoft::WRL::ComPtr<ABI::Windows::Devices::Enumeration::IDeviceWatcher>
       powered_radio_watcher_;
-  absl::optional<EventRegistrationToken> powered_radio_added_token_;
-  absl::optional<EventRegistrationToken> powered_radio_removed_token_;
-  absl::optional<EventRegistrationToken> powered_radios_enumerated_token_;
+  std::optional<EventRegistrationToken> powered_radio_added_token_;
+  std::optional<EventRegistrationToken> powered_radio_removed_token_;
+  std::optional<EventRegistrationToken> powered_radios_enumerated_token_;
   size_t num_powered_radios_ = 0;
 
   bool radio_was_powered_ = false;
 
   std::vector<scoped_refptr<BluetoothAdvertisement>> pending_advertisements_;
 
-  absl::optional<EventRegistrationToken> advertisement_received_token_;
-  absl::optional<EventRegistrationToken> advertisement_watcher_stopped_token_;
+  std::optional<EventRegistrationToken> advertisement_received_token_;
+  std::optional<EventRegistrationToken> advertisement_watcher_stopped_token_;
   Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::Advertisement::
                              IBluetoothLEAdvertisementWatcher>
       ble_advertisement_watcher_;
diff --git a/device/bluetooth/bluetooth_advertisement.h b/device/bluetooth/bluetooth_advertisement.h
index c3722e3..9780713 100644
--- a/device/bluetooth/bluetooth_advertisement.h
+++ b/device/bluetooth/bluetooth_advertisement.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <map>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -17,7 +18,6 @@
 #include "base/observer_list.h"
 #include "build/build_config.h"
 #include "device/bluetooth/bluetooth_export.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -75,37 +75,37 @@
 
     AdvertisementType type() { return type_; }
 
-    absl::optional<UUIDList> service_uuids() {
+    std::optional<UUIDList> service_uuids() {
       return pass_value(service_uuids_);
     }
-    absl::optional<ManufacturerData> manufacturer_data() {
+    std::optional<ManufacturerData> manufacturer_data() {
       return pass_value(manufacturer_data_);
     }
-    absl::optional<UUIDList> solicit_uuids() {
+    std::optional<UUIDList> solicit_uuids() {
       return pass_value(solicit_uuids_);
     }
-    absl::optional<ServiceData> service_data() {
+    std::optional<ServiceData> service_data() {
       return pass_value(service_data_);
     }
-    absl::optional<ScanResponseData> scan_response_data() {
+    std::optional<ScanResponseData> scan_response_data() {
       return pass_value(scan_response_data_);
     }
 
-    void set_service_uuids(absl::optional<UUIDList> service_uuids) {
+    void set_service_uuids(std::optional<UUIDList> service_uuids) {
       service_uuids_ = std::move(service_uuids);
     }
     void set_manufacturer_data(
-        absl::optional<ManufacturerData> manufacturer_data) {
+        std::optional<ManufacturerData> manufacturer_data) {
       manufacturer_data_ = std::move(manufacturer_data);
     }
-    void set_solicit_uuids(absl::optional<UUIDList> solicit_uuids) {
+    void set_solicit_uuids(std::optional<UUIDList> solicit_uuids) {
       solicit_uuids_ = std::move(solicit_uuids);
     }
-    void set_service_data(absl::optional<ServiceData> service_data) {
+    void set_service_data(std::optional<ServiceData> service_data) {
       service_data_ = std::move(service_data);
     }
     void set_scan_response_data(
-        absl::optional<ScanResponseData> scan_response_data) {
+        std::optional<ScanResponseData> scan_response_data) {
       scan_response_data_ = std::move(scan_response_data);
     }
 
@@ -119,18 +119,18 @@
     // Passes the value along held by |from|, and restore the optional moved
     // from to nullopt.
     template <typename T>
-    static absl::optional<T> pass_value(absl::optional<T>& from) {
-      absl::optional<T> value = std::move(from);
-      from = absl::nullopt;
+    static std::optional<T> pass_value(std::optional<T>& from) {
+      std::optional<T> value = std::move(from);
+      from = std::nullopt;
       return value;
     }
 
     AdvertisementType type_;
-    absl::optional<UUIDList> service_uuids_;
-    absl::optional<ManufacturerData> manufacturer_data_;
-    absl::optional<UUIDList> solicit_uuids_;
-    absl::optional<ServiceData> service_data_;
-    absl::optional<ScanResponseData> scan_response_data_;
+    std::optional<UUIDList> service_uuids_;
+    std::optional<ManufacturerData> manufacturer_data_;
+    std::optional<UUIDList> solicit_uuids_;
+    std::optional<ServiceData> service_data_;
+    std::optional<ScanResponseData> scan_response_data_;
     bool include_tx_power_;
   };
 
diff --git a/device/bluetooth/bluetooth_advertisement_mac.h b/device/bluetooth/bluetooth_advertisement_mac.h
index f32691e..dbdf614 100644
--- a/device/bluetooth/bluetooth_advertisement_mac.h
+++ b/device/bluetooth/bluetooth_advertisement_mac.h
@@ -7,13 +7,14 @@
 
 #import <CoreBluetooth/CoreBluetooth.h>
 
+#include <optional>
+
 #include "base/memory/raw_ptr.h"
 #include "base/task/single_thread_task_runner.h"
 #include "dbus/object_path.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_advertisement.h"
 #include "device/bluetooth/bluetooth_export.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -33,7 +34,7 @@
   };
 
   BluetoothAdvertisementMac(
-      absl::optional<BluetoothAdvertisement::UUIDList> service_uuids,
+      std::optional<BluetoothAdvertisement::UUIDList> service_uuids,
       BluetoothAdapter::CreateAdvertisementCallback callback,
       BluetoothAdapter::AdvertisementErrorCallback error_callback,
       BluetoothLowEnergyAdvertisementManagerMac* advertisement_manager);
@@ -73,7 +74,7 @@
 
   void InvokeSuccessCallback();
 
-  absl::optional<BluetoothAdvertisement::UUIDList> service_uuids_;
+  std::optional<BluetoothAdvertisement::UUIDList> service_uuids_;
   BluetoothAdapter::CreateAdvertisementCallback success_callback_;
   BluetoothAdapter::AdvertisementErrorCallback error_callback_;
   raw_ptr<BluetoothLowEnergyAdvertisementManagerMac> advertisement_manager_;
diff --git a/device/bluetooth/bluetooth_advertisement_mac.mm b/device/bluetooth/bluetooth_advertisement_mac.mm
index 0014beef..58126f83 100644
--- a/device/bluetooth/bluetooth_advertisement_mac.mm
+++ b/device/bluetooth/bluetooth_advertisement_mac.mm
@@ -11,7 +11,7 @@
 namespace device {
 
 BluetoothAdvertisementMac::BluetoothAdvertisementMac(
-    absl::optional<BluetoothAdvertisement::UUIDList> service_uuids,
+    std::optional<BluetoothAdvertisement::UUIDList> service_uuids,
     BluetoothAdapter::CreateAdvertisementCallback success_callback,
     BluetoothAdapter::AdvertisementErrorCallback error_callback,
     BluetoothLowEnergyAdvertisementManagerMac* advertisement_manager)
diff --git a/device/bluetooth/bluetooth_advertisement_winrt.h b/device/bluetooth/bluetooth_advertisement_winrt.h
index bff6086..863ae40c8 100644
--- a/device/bluetooth/bluetooth_advertisement_winrt.h
+++ b/device/bluetooth/bluetooth_advertisement_winrt.h
@@ -9,13 +9,13 @@
 #include <wrl/client.h>
 
 #include <memory>
+#include <optional>
 
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_advertisement.h"
 #include "device/bluetooth/bluetooth_export.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -74,7 +74,7 @@
   Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::Advertisement::
                              IBluetoothLEAdvertisementPublisher>
       publisher_;
-  absl::optional<EventRegistrationToken> status_changed_token_;
+  std::optional<EventRegistrationToken> status_changed_token_;
   std::unique_ptr<PendingCallbacks> pending_register_callbacks_;
   std::unique_ptr<PendingCallbacks> pending_unregister_callbacks_;
 
diff --git a/device/bluetooth/bluetooth_classic_device_mac.h b/device/bluetooth/bluetooth_classic_device_mac.h
index bf3f33b..3415d243 100644
--- a/device/bluetooth/bluetooth_classic_device_mac.h
+++ b/device/bluetooth/bluetooth_classic_device_mac.h
@@ -8,11 +8,11 @@
 #import <IOBluetooth/IOBluetooth.h>
 #include <stdint.h>
 
+#include <optional>
 #include <string>
 
 #include "base/time/time.h"
 #include "device/bluetooth/bluetooth_device_mac.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 @class IOBluetoothDevice;
 
@@ -41,15 +41,15 @@
   uint16_t GetProductID() const override;
   uint16_t GetDeviceID() const override;
   uint16_t GetAppearance() const override;
-  absl::optional<std::string> GetName() const override;
+  std::optional<std::string> GetName() const override;
   bool IsPaired() const override;
   bool IsConnected() const override;
   bool IsGattConnected() const override;
   bool IsConnectable() const override;
   bool IsConnecting() const override;
   UUIDSet GetUUIDs() const override;
-  absl::optional<int8_t> GetInquiryRSSI() const override;
-  absl::optional<int8_t> GetInquiryTxPower() const override;
+  std::optional<int8_t> GetInquiryRSSI() const override;
+  std::optional<int8_t> GetInquiryTxPower() const override;
   bool ExpectingPinCode() const override;
   bool ExpectingPasskey() const override;
   bool ExpectingConfirmation() const override;
@@ -86,7 +86,7 @@
  protected:
   // BluetoothDevice override
   void CreateGattConnectionImpl(
-      absl::optional<BluetoothUUID> service_uuid) override;
+      std::optional<BluetoothUUID> service_uuid) override;
   void DisconnectGatt() override;
 
  private:
diff --git a/device/bluetooth/bluetooth_classic_device_mac.mm b/device/bluetooth/bluetooth_classic_device_mac.mm
index 63bd3d2..e0c8b53 100644
--- a/device/bluetooth/bluetooth_classic_device_mac.mm
+++ b/device/bluetooth/bluetooth_classic_device_mac.mm
@@ -76,7 +76,7 @@
 }
 
 void BluetoothClassicDeviceMac::CreateGattConnectionImpl(
-    absl::optional<BluetoothUUID> service_uuid) {
+    std::optional<BluetoothUUID> service_uuid) {
   // Classic devices do not support GATT connection.
   DidConnectGatt(ERROR_UNSUPPORTED_DEVICE);
 }
@@ -116,10 +116,10 @@
   return 0;
 }
 
-absl::optional<std::string> BluetoothClassicDeviceMac::GetName() const {
+std::optional<std::string> BluetoothClassicDeviceMac::GetName() const {
   if ([device_ name])
     return base::SysNSStringToUTF8([device_ name]);
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 bool BluetoothClassicDeviceMac::IsPaired() const {
@@ -165,12 +165,12 @@
   return uuids;
 }
 
-absl::optional<int8_t> BluetoothClassicDeviceMac::GetInquiryRSSI() const {
-  return absl::nullopt;
+std::optional<int8_t> BluetoothClassicDeviceMac::GetInquiryRSSI() const {
+  return std::nullopt;
 }
 
-absl::optional<int8_t> BluetoothClassicDeviceMac::GetInquiryTxPower() const {
-  return absl::nullopt;
+std::optional<int8_t> BluetoothClassicDeviceMac::GetInquiryTxPower() const {
+  return std::nullopt;
 }
 
 bool BluetoothClassicDeviceMac::ExpectingPinCode() const {
diff --git a/device/bluetooth/bluetooth_device.cc b/device/bluetooth/bluetooth_device.cc
index aaf7b8c..6b11d92 100644
--- a/device/bluetooth/bluetooth_device.cc
+++ b/device/bluetooth/bluetooth_device.cc
@@ -114,14 +114,13 @@
 
 BluetoothDevice::ConnectionInfo::~ConnectionInfo() = default;
 
-BatteryInfo::BatteryInfo()
-    : BatteryInfo(BatteryType::kDefault, absl::nullopt) {}
+BatteryInfo::BatteryInfo() : BatteryInfo(BatteryType::kDefault, std::nullopt) {}
 
-BatteryInfo::BatteryInfo(BatteryType type, absl::optional<uint8_t> percentage)
+BatteryInfo::BatteryInfo(BatteryType type, std::optional<uint8_t> percentage)
     : BatteryInfo(type, percentage, BatteryInfo::ChargeState::kUnknown) {}
 
 BatteryInfo::BatteryInfo(BatteryType type,
-                         absl::optional<uint8_t> percentage,
+                         std::optional<uint8_t> percentage,
                          ChargeState charge_state)
     : type(type),
       percentage(std::move(percentage)),
@@ -143,7 +142,7 @@
 BatteryInfo::~BatteryInfo() = default;
 
 std::u16string BluetoothDevice::GetNameForDisplay() const {
-  absl::optional<std::string> name = GetName();
+  std::optional<std::string> name = GetName();
   if (name && HasGraphicCharacter(name.value())) {
     return base::UTF8ToUTF16(name.value());
   } else {
@@ -387,21 +386,21 @@
   return nullptr;
 }
 
-absl::optional<int8_t> BluetoothDevice::GetInquiryRSSI() const {
+std::optional<int8_t> BluetoothDevice::GetInquiryRSSI() const {
   return inquiry_rssi_;
 }
 
-absl::optional<uint8_t> BluetoothDevice::GetAdvertisingDataFlags() const {
+std::optional<uint8_t> BluetoothDevice::GetAdvertisingDataFlags() const {
   return advertising_data_flags_;
 }
 
-absl::optional<int8_t> BluetoothDevice::GetInquiryTxPower() const {
+std::optional<int8_t> BluetoothDevice::GetInquiryTxPower() const {
   return inquiry_tx_power_;
 }
 
 void BluetoothDevice::CreateGattConnection(
     GattConnectionCallback callback,
-    absl::optional<BluetoothUUID> service_uuid) {
+    std::optional<BluetoothUUID> service_uuid) {
   if (!supports_service_specific_discovery_)
     service_uuid.reset();
 
@@ -421,7 +420,7 @@
 
   if (IsGattConnected()) {
     DCHECK(!connection_already_pending);
-    return DidConnectGatt(/*error_code=*/absl::nullopt);
+    return DidConnectGatt(/*error_code=*/std::nullopt);
   }
 
   if (connection_already_pending) {
@@ -469,9 +468,9 @@
 
 void BluetoothDevice::UpdateAdvertisementData(
     int8_t rssi,
-    absl::optional<uint8_t> flags,
+    std::optional<uint8_t> flags,
     UUIDList advertised_uuids,
-    absl::optional<int8_t> tx_power,
+    std::optional<int8_t> tx_power,
     ServiceDataMap service_data,
     ManufacturerDataMap manufacturer_data) {
   UpdateTimestamp();
@@ -554,12 +553,12 @@
   return false;
 }
 
-absl::optional<BatteryInfo> BluetoothDevice::GetBatteryInfo(
+std::optional<BatteryInfo> BluetoothDevice::GetBatteryInfo(
     const BatteryType& type) const {
   auto it = battery_info_map_.find(type);
 
   if (it == battery_info_map_.end())
-    return absl::nullopt;
+    return std::nullopt;
 
   return it->second;
 }
@@ -590,7 +589,7 @@
   return std::make_unique<BluetoothGattConnection>(adapter_, GetAddress());
 }
 
-void BluetoothDevice::DidConnectGatt(absl::optional<ConnectErrorCode> error) {
+void BluetoothDevice::DidConnectGatt(std::optional<ConnectErrorCode> error) {
   if (error.has_value()) {
     // Connection request should only be made if there are no active
     // connections.
@@ -611,7 +610,7 @@
   auto callbacks = std::move(create_gatt_connection_callbacks_);
   for (auto& callback : callbacks) {
     std::move(callback).Run(CreateBluetoothGattConnectionObject(),
-                            /*error_code=*/absl::nullopt);
+                            /*error_code=*/std::nullopt);
   }
 
   GetAdapter()->NotifyDeviceChanged(this);
diff --git a/device/bluetooth/bluetooth_device.h b/device/bluetooth/bluetooth_device.h
index 8018776..0139dd1ef 100644
--- a/device/bluetooth/bluetooth_device.h
+++ b/device/bluetooth/bluetooth_device.h
@@ -9,6 +9,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <optional>
 #include <set>
 #include <string>
 #include <unordered_map>
@@ -28,7 +29,6 @@
 #include "device/bluetooth/bluetooth_export.h"
 #include "device/bluetooth/bluetooth_remote_gatt_service.h"
 #include "device/bluetooth/public/cpp/bluetooth_uuid.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -132,13 +132,13 @@
     };
 
     BatteryType type;
-    absl::optional<uint8_t> percentage;
+    std::optional<uint8_t> percentage;
     ChargeState charge_state;
 
     BatteryInfo();
-    BatteryInfo(BatteryType type, absl::optional<uint8_t> percentage);
+    BatteryInfo(BatteryType type, std::optional<uint8_t> percentage);
     BatteryInfo(BatteryType type,
-                absl::optional<uint8_t> percentage,
+                std::optional<uint8_t> percentage,
                 ChargeState charge_state);
     BatteryInfo(const BatteryInfo&);
     BatteryInfo& operator=(const BatteryInfo&);
@@ -301,7 +301,7 @@
   virtual uint16_t GetAppearance() const = 0;
 
   // Returns the name of the device, which may be empty.
-  virtual absl::optional<std::string> GetName() const = 0;
+  virtual std::optional<std::string> GetName() const = 0;
 
   // Returns the name of the device suitable for displaying, this may
   // be a synthesized string containing the address and localized type name
@@ -421,22 +421,22 @@
   // The received signal strength, in dBm. This field is avaliable and valid
   // only during discovery.
   // TODO(http://crbug.com/580406): Devirtualize once BlueZ sets inquiry_rssi_.
-  virtual absl::optional<int8_t> GetInquiryRSSI() const;
+  virtual std::optional<int8_t> GetInquiryRSSI() const;
 
   // The transmitted power level. This field is avaliable only for LE devices
   // that include this field in AD. It is avaliable and valid only during
   // discovery.
   // TODO(http://crbug.com/580406): Devirtualize once BlueZ sets
   // inquiry_tx_power_.
-  virtual absl::optional<int8_t> GetInquiryTxPower() const;
+  virtual std::optional<int8_t> GetInquiryTxPower() const;
 
   // Returns Advertising Data Flags.
   // Returns cached value if the adapter is not discovering.
   //
   // Only Chrome OS and WinRT support this now. Upstream BlueZ has this feature
-  // as experimental. This method returns absl::nullopt on platforms that don't
+  // as experimental. This method returns std::nullopt on platforms that don't
   // support this feature.
-  absl::optional<uint8_t> GetAdvertisingDataFlags() const;
+  std::optional<uint8_t> GetAdvertisingDataFlags() const;
 
   // The ErrorCallback is used for methods that can fail in which case it
   // is called, in the success case the callback is simply not called.
@@ -445,7 +445,7 @@
   // Reports the status of a device connection attempt. |error_code| will
   // contain a value upon failure, otherwise the attempt was successful.
   using ConnectCallback =
-      base::OnceCallback<void(absl::optional<ConnectErrorCode> error_code)>;
+      base::OnceCallback<void(std::optional<ConnectErrorCode> error_code)>;
 
   using ConnectionInfoCallback =
       base::OnceCallback<void(const ConnectionInfo&)>;
@@ -617,10 +617,10 @@
   // event to watch for.
   using GattConnectionCallback =
       base::OnceCallback<void(std::unique_ptr<BluetoothGattConnection>,
-                              absl::optional<ConnectErrorCode> error_code)>;
+                              std::optional<ConnectErrorCode> error_code)>;
   virtual void CreateGattConnection(
       GattConnectionCallback callback,
-      absl::optional<BluetoothUUID> service_uuid = absl::nullopt);
+      std::optional<BluetoothUUID> service_uuid = std::nullopt);
 
   // Set the gatt services discovery complete flag for this device.
   virtual void SetGattServicesDiscoveryComplete(bool complete);
@@ -651,9 +651,9 @@
   // arguments matches the order of their corresponding Data Type specified in
   // https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile.
   void UpdateAdvertisementData(int8_t rssi,
-                               absl::optional<uint8_t> flags,
+                               std::optional<uint8_t> flags,
                                UUIDList advertised_uuids,
-                               absl::optional<int8_t> tx_power,
+                               std::optional<int8_t> tx_power,
                                ServiceDataMap service_data,
                                ManufacturerDataMap manufacturer_data);
 
@@ -688,7 +688,7 @@
   // Removes the battery information associated with |type|.
   // Returns true if removed, otherwise false.
   bool RemoveBatteryInfo(const BatteryType& type);
-  absl::optional<BatteryInfo> GetBatteryInfo(const BatteryType& type) const;
+  std::optional<BatteryInfo> GetBatteryInfo(const BatteryType& type) const;
   // Returns the list of currently set BatteryTypes.
   std::vector<BatteryType> GetAvailableBatteryTypes();
 #endif
@@ -768,7 +768,7 @@
   // Subclasses must also call DidConnectGatt or DidDisconnectGatt immediately
   // or asynchronously as the connection state changes.
   virtual void CreateGattConnectionImpl(
-      absl::optional<BluetoothUUID> service_uuid) = 0;
+      std::optional<BluetoothUUID> service_uuid) = 0;
 
   // UpgradeToFullDiscovery is called when there is a pending or current GATT
   // connection that was created with a service UUID, but now discovery of all
@@ -792,7 +792,7 @@
   //
   // Under normal behavior it is expected that after CreateGattConnectionImpl
   // a platform will call DidConnectGatt but not DidDisconnectGatt.
-  void DidConnectGatt(absl::optional<ConnectErrorCode> error_code);
+  void DidConnectGatt(std::optional<ConnectErrorCode> error_code);
   void DidDisconnectGatt();
 
   // Tracks BluetoothGattConnection instances that act as a reference count
@@ -818,7 +818,7 @@
 
   // Contains the specified service that was targeted for discovery. Only ever
   // contains a value if |supports_service_specific_discovery_| is true.
-  absl::optional<BluetoothUUID> target_service_;
+  std::optional<BluetoothUUID> target_service_;
 
   // Callbacks for result of CreateGattConnection.
   std::vector<GattConnectionCallback> create_gatt_connection_callbacks_;
@@ -830,13 +830,13 @@
   bool gatt_services_discovery_complete_;
 
   // Received Signal Strength Indicator of the advertisement received.
-  absl::optional<int8_t> inquiry_rssi_;
+  std::optional<int8_t> inquiry_rssi_;
 
   // Advertising Data flags of the device.
-  absl::optional<uint8_t> advertising_data_flags_;
+  std::optional<uint8_t> advertising_data_flags_;
 
   // Tx Power advertised by the device.
-  absl::optional<int8_t> inquiry_tx_power_;
+  std::optional<int8_t> inquiry_tx_power_;
 
   // Class that holds the union of Advertised UUIDs and Service UUIDs.
   DeviceUUIDs device_uuids_;
diff --git a/device/bluetooth/bluetooth_device_android.cc b/device/bluetooth/bluetooth_device_android.cc
index a6ad835..6d50f9a 100644
--- a/device/bluetooth/bluetooth_device_android.cc
+++ b/device/bluetooth/bluetooth_device_android.cc
@@ -85,11 +85,11 @@
   return 0;
 }
 
-absl::optional<std::string> BluetoothDeviceAndroid::GetName() const {
+std::optional<std::string> BluetoothDeviceAndroid::GetName() const {
   auto name =
       Java_ChromeBluetoothDevice_getName(AttachCurrentThread(), j_device_);
   if (name.is_null())
-    return absl::nullopt;
+    return std::nullopt;
   return ConvertJavaStringToUTF8(name);
 }
 
@@ -200,7 +200,7 @@
     bool connected) {
   gatt_connected_ = connected;
   if (gatt_connected_) {
-    DidConnectGatt(/*error_code=*/absl::nullopt);
+    DidConnectGatt(/*error_code=*/std::nullopt);
   } else if (!create_gatt_connection_callbacks_.empty()) {
     // We assume that if there are any pending connection callbacks there
     // was a failed connection attempt.
@@ -250,7 +250,7 @@
     : BluetoothDevice(adapter) {}
 
 void BluetoothDeviceAndroid::CreateGattConnectionImpl(
-    absl::optional<device::BluetoothUUID> service_uuid) {
+    std::optional<device::BluetoothUUID> service_uuid) {
   Java_ChromeBluetoothDevice_createGattConnectionImpl(AttachCurrentThread(),
                                                       j_device_);
 }
diff --git a/device/bluetooth/bluetooth_device_android.h b/device/bluetooth/bluetooth_device_android.h
index e020c34d..8ac50cc 100644
--- a/device/bluetooth/bluetooth_device_android.h
+++ b/device/bluetooth/bluetooth_device_android.h
@@ -8,13 +8,13 @@
 #include <stdint.h>
 
 #include <memory>
+#include <optional>
 
 #include "base/android/jni_android.h"
 #include "base/android/scoped_java_ref.h"
 #include "base/memory/weak_ptr.h"
 #include "device/bluetooth/bluetooth_adapter_android.h"
 #include "device/bluetooth/bluetooth_device.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -58,7 +58,7 @@
   uint16_t GetProductID() const override;
   uint16_t GetDeviceID() const override;
   uint16_t GetAppearance() const override;
-  absl::optional<std::string> GetName() const override;
+  std::optional<std::string> GetName() const override;
   bool IsPaired() const override;
   bool IsConnected() const override;
   bool IsGattConnected() const override;
@@ -118,7 +118,7 @@
 
   // BluetoothDevice:
   void CreateGattConnectionImpl(
-      absl::optional<device::BluetoothUUID> service_uuid) override;
+      std::optional<device::BluetoothUUID> service_uuid) override;
   void DisconnectGatt() override;
 
   // Java object org.chromium.device.bluetooth.ChromeBluetoothDevice.
diff --git a/device/bluetooth/bluetooth_device_unittest.cc b/device/bluetooth/bluetooth_device_unittest.cc
index 1461109..d763998e 100644
--- a/device/bluetooth/bluetooth_device_unittest.cc
+++ b/device/bluetooth/bluetooth_device_unittest.cc
@@ -154,12 +154,12 @@
   EXPECT_CALL(device, CreateGattConnection(_, _))
       .Times(2)
       .WillRepeatedly([&](BluetoothDevice::GattConnectionCallback callback,
-                          absl::optional<BluetoothUUID> service_uuid) {
+                          std::optional<BluetoothUUID> service_uuid) {
         device.BluetoothDevice::CreateGattConnection(std::move(callback),
                                                      service_uuid);
       });
   EXPECT_CALL(device, CreateGattConnectionImpl(_))
-      .WillOnce([&](absl::optional<BluetoothUUID> service_uuid) {
+      .WillOnce([&](std::optional<BluetoothUUID> service_uuid) {
         device.DidConnectGatt(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
       });
   EXPECT_CALL(device, IsGattConnected())
@@ -171,21 +171,21 @@
   device.CreateGattConnection(
       base::BindLambdaForTesting(
           [&](std::unique_ptr<BluetoothGattConnection> connection,
-              absl::optional<BluetoothDevice::ConnectErrorCode> error_code) {
+              std::optional<BluetoothDevice::ConnectErrorCode> error_code) {
             EXPECT_FALSE(connection);
             EXPECT_EQ(error_code,
                       BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
             device.CreateGattConnection(
                 base::BindLambdaForTesting(
                     [&](std::unique_ptr<BluetoothGattConnection> connection,
-                        absl::optional<BluetoothDevice::ConnectErrorCode>
+                        std::optional<BluetoothDevice::ConnectErrorCode>
                             error_code) {
                       EXPECT_TRUE(connection);
                       EXPECT_FALSE(error_code);
                     }),
-                /*service_uuid=*/absl::nullopt);
+                /*service_uuid=*/std::nullopt);
           }),
-      /*service_uuid=*/absl::nullopt);
+      /*service_uuid=*/std::nullopt);
 }
 
 #if BUILDFLAG(IS_WIN)
@@ -234,7 +234,7 @@
   device->Pair(
       &pairing_delegate,
       base::BindLambdaForTesting(
-          [&](absl::optional<BluetoothDevice::ConnectErrorCode> error_code) {
+          [&](std::optional<BluetoothDevice::ConnectErrorCode> error_code) {
             EXPECT_FALSE(error_code.has_value());
             run_loop.Quit();
           }));
@@ -268,7 +268,7 @@
   device->Pair(
       &pairing_delegate,
       base::BindLambdaForTesting(
-          [&](absl::optional<BluetoothDevice::ConnectErrorCode> error_code) {
+          [&](std::optional<BluetoothDevice::ConnectErrorCode> error_code) {
             EXPECT_EQ(BluetoothDevice::ERROR_FAILED, error_code);
             run_loop.Quit();
           }));
@@ -303,7 +303,7 @@
   device->Pair(
       &pairing_delegate,
       base::BindLambdaForTesting(
-          [&](absl::optional<BluetoothDevice::ConnectErrorCode> error_code) {
+          [&](std::optional<BluetoothDevice::ConnectErrorCode> error_code) {
             EXPECT_EQ(BluetoothDevice::ERROR_AUTH_REJECTED, error_code);
             run_loop.Quit();
           }));
@@ -339,7 +339,7 @@
   device->Pair(
       &pairing_delegate,
       base::BindLambdaForTesting(
-          [&](absl::optional<BluetoothDevice::ConnectErrorCode> error_code) {
+          [&](std::optional<BluetoothDevice::ConnectErrorCode> error_code) {
             EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, error_code);
             run_loop.Quit();
           }));
@@ -369,7 +369,7 @@
         device->ConfirmPairing();
       });
 
-  base::test::TestFuture<absl::optional<BluetoothDevice::ConnectErrorCode>>
+  base::test::TestFuture<std::optional<BluetoothDevice::ConnectErrorCode>>
       error_code_future;
   device->Pair(&pairing_delegate, error_code_future.GetCallback());
 
@@ -397,7 +397,7 @@
         ScheduleAsynchronousCancelPairing(device);
       });
 
-  base::test::TestFuture<absl::optional<BluetoothDevice::ConnectErrorCode>>
+  base::test::TestFuture<std::optional<BluetoothDevice::ConnectErrorCode>>
       error_code_future;
   device->Pair(&pairing_delegate, error_code_future.GetCallback());
 
@@ -426,7 +426,7 @@
         device->ConfirmPairing();
       });
 
-  base::test::TestFuture<absl::optional<BluetoothDevice::ConnectErrorCode>>
+  base::test::TestFuture<std::optional<BluetoothDevice::ConnectErrorCode>>
       error_code_future;
   device->Pair(&pairing_delegate, error_code_future.GetCallback());
 
@@ -455,7 +455,7 @@
         ScheduleAsynchronousCancelPairing(device);
       });
 
-  base::test::TestFuture<absl::optional<BluetoothDevice::ConnectErrorCode>>
+  base::test::TestFuture<std::optional<BluetoothDevice::ConnectErrorCode>>
       error_code_future;
   device->Pair(&pairing_delegate, error_code_future.GetCallback());
 
@@ -484,7 +484,7 @@
         device->ConfirmPairing();
       });
 
-  base::test::TestFuture<absl::optional<BluetoothDevice::ConnectErrorCode>>
+  base::test::TestFuture<std::optional<BluetoothDevice::ConnectErrorCode>>
       error_code_future;
   device->Pair(&pairing_delegate, error_code_future.GetCallback());
 
@@ -509,7 +509,7 @@
 
   EXPECT_CALL(pairing_delegate, ConfirmPasskey).Times(0);
 
-  base::test::TestFuture<absl::optional<BluetoothDevice::ConnectErrorCode>>
+  base::test::TestFuture<std::optional<BluetoothDevice::ConnectErrorCode>>
       error_code_future;
   device->Pair(&pairing_delegate, error_code_future.GetCallback());
 
@@ -562,7 +562,7 @@
   StartLowEnergyDiscoverySession();
   BluetoothDevice* device = SimulateLowEnergyDevice(3);
   ASSERT_TRUE(device);
-  // GetName() returns a absl::optional<std:string> however some backends still
+  // GetName() returns a std::optional<std:string> however some backends still
   // return an empty string rather than nullopt when no name is available.
   EXPECT_TRUE(!device->GetName().has_value() || device->GetName()->empty());
 
@@ -1557,7 +1557,7 @@
   ResetEventCounts();
 
   ASSERT_TRUE(
-      ConnectGatt(device, /*service_uuid=*/absl::nullopt,
+      ConnectGatt(device, /*service_uuid=*/std::nullopt,
                   base::BindLambdaForTesting([this](BluetoothDevice* device) {
                     SimulateGattConnection(device);
                     SimulateGattConnection(device);
@@ -1669,7 +1669,7 @@
   // Create multiple connections and simulate connection complete:
   ASSERT_TRUE(
       ConnectGatt(device,
-                  /*service_uuid=*/absl::nullopt,
+                  /*service_uuid=*/std::nullopt,
                   base::BindLambdaForTesting([this](BluetoothDevice* device) {
                     ConnectGatt(device);
                   })));
@@ -1704,7 +1704,7 @@
   // Create multiple connections and simulate connection complete:
   ASSERT_TRUE(
       ConnectGatt(device,
-                  /*service_uuid=*/absl::nullopt,
+                  /*service_uuid=*/std::nullopt,
                   base::BindLambdaForTesting([this](BluetoothDevice* device) {
                     ConnectGatt(device);
                   })));
@@ -1754,7 +1754,7 @@
   ResetEventCounts();
   EXPECT_FALSE(
       ConnectGatt(device,
-                  /*service_uuid=*/absl::nullopt,
+                  /*service_uuid=*/std::nullopt,
                   base::BindLambdaForTesting([this](BluetoothDevice* device) {
                     SimulateGattDisconnection(device);
                   })));
@@ -1789,7 +1789,7 @@
   ResetEventCounts();
   EXPECT_TRUE(
       ConnectGatt(device,
-                  /*service_uuid=*/absl::nullopt,
+                  /*service_uuid=*/std::nullopt,
                   base::BindLambdaForTesting([this](BluetoothDevice* device) {
 #if !BUILDFLAG(IS_WIN)
                     // On Windows there is currently no way to cancel a
@@ -1835,7 +1835,7 @@
   ResetEventCounts();
   ASSERT_FALSE(
       ConnectGatt(device,
-                  /*service_uuid=*/absl::nullopt,
+                  /*service_uuid=*/std::nullopt,
                   base::BindLambdaForTesting([this](BluetoothDevice* device) {
                     device->DisconnectGatt();
                     SimulateGattDisconnection(device);
@@ -1938,7 +1938,7 @@
   ResetEventCounts();
   EXPECT_FALSE(ConnectGatt(
       device,
-      /*service_uuid=*/absl::nullopt,
+      /*service_uuid=*/std::nullopt,
       base::BindLambdaForTesting([this](BluetoothDevice* device) {
         SimulateGattConnectionError(device, BluetoothDevice::ERROR_AUTH_FAILED);
         SimulateGattConnectionError(device, BluetoothDevice::ERROR_FAILED);
@@ -2394,7 +2394,7 @@
   StartLowEnergyDiscoverySession();
   BluetoothDevice* device = SimulateLowEnergyDevice(3);
   ASSERT_TRUE(ConnectGatt(device));
-  // GetName() returns a absl::optional<std:string> however some backends still
+  // GetName() returns a std::optional<std:string> however some backends still
   // return an empty string rather than nullopt when no name is available.
   EXPECT_TRUE(!device->GetName() || device->GetName()->empty());
 
@@ -2461,7 +2461,7 @@
 #else
   ASSERT_TRUE(device->supports_service_specific_discovery());
 
-  absl::optional<BluetoothUUID> service_uuid = GetTargetGattService(device);
+  std::optional<BluetoothUUID> service_uuid = GetTargetGattService(device);
   ASSERT_TRUE(service_uuid.has_value());
   EXPECT_EQ(*service_uuid, BluetoothUUID(kTestUUIDGenericAccess));
   EXPECT_FALSE(device->IsGattServicesDiscoveryComplete());
diff --git a/device/bluetooth/bluetooth_device_win.cc b/device/bluetooth/bluetooth_device_win.cc
index fb972db..e73aea87 100644
--- a/device/bluetooth/bluetooth_device_win.cc
+++ b/device/bluetooth/bluetooth_device_win.cc
@@ -78,7 +78,7 @@
   return 0;
 }
 
-absl::optional<std::string> BluetoothDeviceWin::GetName() const {
+std::optional<std::string> BluetoothDeviceWin::GetName() const {
   return name_;
 }
 
@@ -106,16 +106,16 @@
   return uuids_;
 }
 
-absl::optional<int8_t> BluetoothDeviceWin::GetInquiryRSSI() const {
+std::optional<int8_t> BluetoothDeviceWin::GetInquiryRSSI() const {
   // In windows, we can only get connected devices and connected
   // devices don't have an Inquiry RSSI.
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-absl::optional<int8_t> BluetoothDeviceWin::GetInquiryTxPower() const {
+std::optional<int8_t> BluetoothDeviceWin::GetInquiryTxPower() const {
   // In windows, we can only get connected devices and connected
   // devices don't have an Inquiry Tx Power.
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 bool BluetoothDeviceWin::ExpectingPinCode() const {
@@ -258,7 +258,7 @@
 }
 
 void BluetoothDeviceWin::CreateGattConnectionImpl(
-    absl::optional<BluetoothUUID> service_uuid) {
+    std::optional<BluetoothUUID> service_uuid) {
   // Windows will create the Gatt connection as needed.  See:
   // https://docs.microsoft.com/en-us/windows/uwp/devices-sensors/gatt-client#connecting-to-the-device
 }
diff --git a/device/bluetooth/bluetooth_device_win.h b/device/bluetooth/bluetooth_device_win.h
index dffae40b..d9da38f 100644
--- a/device/bluetooth/bluetooth_device_win.h
+++ b/device/bluetooth/bluetooth_device_win.h
@@ -49,15 +49,15 @@
   uint16_t GetProductID() const override;
   uint16_t GetDeviceID() const override;
   uint16_t GetAppearance() const override;
-  absl::optional<std::string> GetName() const override;
+  std::optional<std::string> GetName() const override;
   bool IsPaired() const override;
   bool IsConnected() const override;
   bool IsGattConnected() const override;
   bool IsConnectable() const override;
   bool IsConnecting() const override;
   UUIDSet GetUUIDs() const override;
-  absl::optional<int8_t> GetInquiryRSSI() const override;
-  absl::optional<int8_t> GetInquiryTxPower() const override;
+  std::optional<int8_t> GetInquiryRSSI() const override;
+  std::optional<int8_t> GetInquiryTxPower() const override;
   bool ExpectingPinCode() const override;
   bool ExpectingPasskey() const override;
   bool ExpectingConfirmation() const override;
@@ -100,7 +100,7 @@
  protected:
   // BluetoothDevice override
   void CreateGattConnectionImpl(
-      absl::optional<BluetoothUUID> service_uuid) override;
+      std::optional<BluetoothUUID> service_uuid) override;
   void DisconnectGatt() override;
 
  private:
@@ -121,7 +121,7 @@
   uint32_t bluetooth_class_;
 
   // The name of the device, as supplied by the remote device.
-  absl::optional<std::string> name_;
+  std::optional<std::string> name_;
 
   // The Bluetooth address of the device.
   std::string address_;
diff --git a/device/bluetooth/bluetooth_device_winrt.cc b/device/bluetooth/bluetooth_device_winrt.cc
index 2d30969..507f914 100644
--- a/device/bluetooth/bluetooth_device_winrt.cc
+++ b/device/bluetooth/bluetooth_device_winrt.cc
@@ -62,7 +62,7 @@
 using Microsoft::WRL::ComPtr;
 
 void PostTask(BluetoothPairingWinrt::ConnectCallback callback,
-              absl::optional<BluetoothDevice::ConnectErrorCode> error_code) {
+              std::optional<BluetoothDevice::ConnectErrorCode> error_code) {
   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), error_code));
 }
@@ -239,7 +239,7 @@
   return 0;
 }
 
-absl::optional<std::string> BluetoothDeviceWinrt::GetName() const {
+std::optional<std::string> BluetoothDeviceWinrt::GetName() const {
   if (!ble_device_)
     return local_name_;
 
@@ -369,7 +369,7 @@
   // Wrap callback, so that it cleans up the pairing object when run.
   auto wrapped_callback = base::BindOnce(
       [](base::WeakPtr<BluetoothDeviceWinrt> device, ConnectCallback callback,
-         absl::optional<ConnectErrorCode> error_code) {
+         std::optional<ConnectErrorCode> error_code) {
         if (device)
           device->pairing_.reset();
         std::move(callback).Run(error_code);
@@ -438,7 +438,7 @@
 }
 
 void BluetoothDeviceWinrt::UpdateLocalName(
-    absl::optional<std::string> local_name) {
+    std::optional<std::string> local_name) {
   if (!local_name)
     return;
 
@@ -446,7 +446,7 @@
 }
 
 void BluetoothDeviceWinrt::CreateGattConnectionImpl(
-    absl::optional<BluetoothUUID> service_uuid) {
+    std::optional<BluetoothUUID> service_uuid) {
   ComPtr<IBluetoothLEDeviceStatics> device_statics;
   HRESULT hr = GetBluetoothLEDeviceStaticsActivationFactory(&device_statics);
   if (FAILED(hr)) {
@@ -584,7 +584,7 @@
     // in a GATT connection attempt as well and trigger
     // OnConnectionStatusChanged on success.
     if (IsGattConnected()) {
-      DidConnectGatt(/*error_code=*/absl::nullopt);
+      DidConnectGatt(/*error_code=*/std::nullopt);
     }
     StartGattDiscovery();
     return;
@@ -673,7 +673,7 @@
   // Check whether we missed the initial GattSessionStatus change notification
   // because the OS had already established a connection.
   if (IsGattConnected()) {
-    DidConnectGatt(/*error_code=*/absl::nullopt);
+    DidConnectGatt(/*error_code=*/std::nullopt);
     StartGattDiscovery();
   }
 }
@@ -706,7 +706,7 @@
   }
 
   if (IsGattConnected()) {
-    DidConnectGatt(/*error_code=*/absl::nullopt);
+    DidConnectGatt(/*error_code=*/std::nullopt);
     StartGattDiscovery();
   } else {
     gatt_discoverer_.reset();
@@ -735,7 +735,7 @@
   }
 
   if (IsGattConnected()) {
-    DidConnectGatt(/*error_code=*/absl::nullopt);
+    DidConnectGatt(/*error_code=*/std::nullopt);
   } else {
     gatt_discoverer_.reset();
     ClearGattServices();
diff --git a/device/bluetooth/bluetooth_device_winrt.h b/device/bluetooth/bluetooth_device_winrt.h
index 02c93c83..7620f895 100644
--- a/device/bluetooth/bluetooth_device_winrt.h
+++ b/device/bluetooth/bluetooth_device_winrt.h
@@ -5,13 +5,13 @@
 #ifndef DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_WINRT_H_
 #define DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_WINRT_H_
 
+#include <stdint.h>
 #include <windows.devices.bluetooth.genericattributeprofile.h>
 #include <windows.devices.bluetooth.h>
 #include <wrl/client.h>
 
-#include <stdint.h>
-
 #include <memory>
+#include <optional>
 #include <string>
 
 #include "base/feature_list.h"
@@ -22,7 +22,6 @@
 #include "device/base/features.h"
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/bluetooth_export.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -57,7 +56,7 @@
   uint16_t GetProductID() const override;
   uint16_t GetDeviceID() const override;
   uint16_t GetAppearance() const override;
-  absl::optional<std::string> GetName() const override;
+  std::optional<std::string> GetName() const override;
   bool IsPaired() const override;
   bool IsConnected() const override;
   bool IsGattConnected() const override;
@@ -96,12 +95,12 @@
   static std::string CanonicalizeAddress(uint64_t address);
 
   // Called by BluetoothAdapterWinrt when an advertisement packet is received.
-  void UpdateLocalName(absl::optional<std::string> local_name);
+  void UpdateLocalName(std::optional<std::string> local_name);
 
  protected:
   // BluetoothDevice:
   void CreateGattConnectionImpl(
-      absl::optional<BluetoothUUID> service_uuid) override;
+      std::optional<BluetoothUUID> service_uuid) override;
   void UpgradeToFullDiscovery() override;
   void DisconnectGatt() override;
 
@@ -162,7 +161,7 @@
       gatt_session_status_;
   uint64_t raw_address_;
   std::string address_;
-  absl::optional<std::string> local_name_;
+  std::optional<std::string> local_name_;
 
   std::unique_ptr<BluetoothPairingWinrt> pairing_;
 
@@ -182,13 +181,13 @@
   // FromBluetoothAddressAsync() otherwise.
   bool pending_gatt_service_discovery_start_ = false;
 
-  absl::optional<BluetoothUUID> target_uuid_;
+  std::optional<BluetoothUUID> target_uuid_;
   std::unique_ptr<BluetoothGattDiscovererWinrt> gatt_discoverer_;
 
-  absl::optional<EventRegistrationToken> connection_changed_token_;
-  absl::optional<EventRegistrationToken> gatt_session_status_changed_token_;
-  absl::optional<EventRegistrationToken> gatt_services_changed_token_;
-  absl::optional<EventRegistrationToken> name_changed_token_;
+  std::optional<EventRegistrationToken> connection_changed_token_;
+  std::optional<EventRegistrationToken> gatt_session_status_changed_token_;
+  std::optional<EventRegistrationToken> gatt_services_changed_token_;
+  std::optional<EventRegistrationToken> name_changed_token_;
 
   THREAD_CHECKER(thread_checker_);
 
diff --git a/device/bluetooth/bluetooth_discovery_filter.h b/device/bluetooth/bluetooth_discovery_filter.h
index 57bdfc9..3ed6e00 100644
--- a/device/bluetooth/bluetooth_discovery_filter.h
+++ b/device/bluetooth/bluetooth_discovery_filter.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <optional>
 #include <set>
 #include <string>
 #include <vector>
@@ -16,7 +17,6 @@
 #include "device/bluetooth/bluetooth_common.h"
 #include "device/bluetooth/bluetooth_export.h"
 #include "device/bluetooth/public/cpp/bluetooth_uuid.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -105,8 +105,8 @@
       const device::BluetoothDiscoveryFilter* filter_b);
 
  private:
-  absl::optional<int16_t> rssi_;
-  absl::optional<uint16_t> pathloss_;
+  std::optional<int16_t> rssi_;
+  std::optional<uint16_t> pathloss_;
   BluetoothTransport transport_;
   base::flat_set<DeviceInfoFilter> device_filters_;
 };
diff --git a/device/bluetooth/bluetooth_gatt_discoverer_winrt.cc b/device/bluetooth/bluetooth_gatt_discoverer_winrt.cc
index 7fe12d0..71c786a 100644
--- a/device/bluetooth/bluetooth_gatt_discoverer_winrt.cc
+++ b/device/bluetooth/bluetooth_gatt_discoverer_winrt.cc
@@ -170,7 +170,7 @@
 
 BluetoothGattDiscovererWinrt::BluetoothGattDiscovererWinrt(
     ComPtr<IBluetoothLEDevice> ble_device,
-    absl::optional<BluetoothUUID> service_uuid)
+    std::optional<BluetoothUUID> service_uuid)
     : ble_device_(std::move(ble_device)),
       service_uuid_(std::move(service_uuid)) {}
 
diff --git a/device/bluetooth/bluetooth_gatt_discoverer_winrt.h b/device/bluetooth/bluetooth_gatt_discoverer_winrt.h
index 86fc599..8dce1fb0 100644
--- a/device/bluetooth/bluetooth_gatt_discoverer_winrt.h
+++ b/device/bluetooth/bluetooth_gatt_discoverer_winrt.h
@@ -5,13 +5,13 @@
 #ifndef DEVICE_BLUETOOTH_BLUETOOTH_GATT_DISCOVERER_WINRT_H_
 #define DEVICE_BLUETOOTH_BLUETOOTH_GATT_DISCOVERER_WINRT_H_
 
+#include <stdint.h>
 #include <windows.devices.bluetooth.genericattributeprofile.h>
 #include <windows.devices.bluetooth.h>
 #include <wrl/client.h>
 
-#include <stdint.h>
-
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "base/containers/flat_map.h"
@@ -21,7 +21,6 @@
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/bluetooth_export.h"
 #include "device/bluetooth/public/cpp/bluetooth_uuid.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -46,7 +45,7 @@
   BluetoothGattDiscovererWinrt(
       Microsoft::WRL::ComPtr<
           ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice> ble_device,
-      absl::optional<BluetoothUUID> service_uuid);
+      std::optional<BluetoothUUID> service_uuid);
 
   BluetoothGattDiscovererWinrt(const BluetoothGattDiscovererWinrt&) = delete;
   BluetoothGattDiscovererWinrt& operator=(const BluetoothGattDiscovererWinrt&) =
@@ -101,7 +100,7 @@
       service_to_characteristics_map_;
   base::flat_map<uint16_t, GattDescriptorList>
       characteristic_to_descriptors_map_;
-  absl::optional<BluetoothUUID> service_uuid_;
+  std::optional<BluetoothUUID> service_uuid_;
   size_t num_services_ = 0;
   size_t num_characteristics_ = 0;
 
diff --git a/device/bluetooth/bluetooth_local_gatt_service.h b/device/bluetooth/bluetooth_local_gatt_service.h
index fd17b1c6..94f41d9 100644
--- a/device/bluetooth/bluetooth_local_gatt_service.h
+++ b/device/bluetooth/bluetooth_local_gatt_service.h
@@ -6,6 +6,8 @@
 #define DEVICE_BLUETOOTH_BLUETOOTH_LOCAL_GATT_SERVICE_H_
 
 #include <stdint.h>
+
+#include <optional>
 #include <vector>
 
 #include "base/functional/callback.h"
@@ -16,7 +18,6 @@
 #include "device/bluetooth/bluetooth_gatt_characteristic.h"
 #include "device/bluetooth/bluetooth_gatt_service.h"
 #include "device/bluetooth/public/cpp/bluetooth_uuid.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -46,7 +47,7 @@
    public:
     // Callbacks used for communicating GATT request responses.
     using ValueCallback = base::OnceCallback<void(
-        absl::optional<BluetoothGattService::GattErrorCode> error_code,
+        std::optional<BluetoothGattService::GattErrorCode> error_code,
         const std::vector<uint8_t>&)>;
     using ErrorCallback = base::OnceClosure;
 
diff --git a/device/bluetooth/bluetooth_low_energy_adapter_apple.mm b/device/bluetooth/bluetooth_low_energy_adapter_apple.mm
index 5e053cf..88f7c3de 100644
--- a/device/bluetooth/bluetooth_low_energy_adapter_apple.mm
+++ b/device/bluetooth/bluetooth_low_energy_adapter_apple.mm
@@ -442,22 +442,22 @@
   NSString* local_name = advertisement_data[CBAdvertisementDataLocalNameKey];
 
   for (auto& observer : observers_) {
-    absl::optional<std::string> device_name_opt = device_mac->GetName();
-    absl::optional<std::string> local_name_opt =
+    std::optional<std::string> device_name_opt = device_mac->GetName();
+    std::optional<std::string> local_name_opt =
         base::SysNSStringToUTF8(local_name);
 
     observer.DeviceAdvertisementReceived(
         device_mac->GetAddress(), device_name_opt,
-        local_name == nil ? absl::nullopt : local_name_opt, rssi,
-        tx_power == nil ? absl::nullopt : absl::make_optional(clamped_tx_power),
-        absl::nullopt, /* TODO(crbug.com/588083) Implement appearance */
+        local_name == nil ? std::nullopt : local_name_opt, rssi,
+        tx_power == nil ? std::nullopt : std::make_optional(clamped_tx_power),
+        std::nullopt, /* TODO(crbug.com/588083) Implement appearance */
         advertised_uuids, service_data_map, manufacturer_data_map);
   }
 
   device_mac->UpdateAdvertisementData(
-      BluetoothDevice::ClampPower(rssi), absl::nullopt /* flags */,
+      BluetoothDevice::ClampPower(rssi), std::nullopt /* flags */,
       std::move(advertised_uuids),
-      tx_power == nil ? absl::nullopt : absl::make_optional(clamped_tx_power),
+      tx_power == nil ? std::nullopt : std::make_optional(clamped_tx_power),
       std::move(service_data_map), std::move(manufacturer_data_map));
 
   if (is_new_device) {
diff --git a/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac.mm b/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac.mm
index 18f410be..e3b99cbf 100644
--- a/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac.mm
+++ b/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac.mm
@@ -4,12 +4,13 @@
 
 #include "device/bluetooth/bluetooth_low_energy_advertisement_manager_mac.h"
 
+#include <optional>
+
 #include "base/functional/bind.h"
 #include "base/logging.h"
 #include "base/strings/sys_string_conversions.h"
 #import "base/task/single_thread_task_runner.h"
 #include "device/bluetooth/bluetooth_advertisement.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -104,9 +105,9 @@
     std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data,
     BluetoothAdapter::CreateAdvertisementCallback callback,
     BluetoothAdapter::AdvertisementErrorCallback error_callback) {
-  absl::optional<BluetoothAdvertisement::ErrorCode> error_code;
+  std::optional<BluetoothAdvertisement::ErrorCode> error_code;
 
-  const absl::optional<BluetoothAdvertisement::UUIDList>& service_uuids =
+  const std::optional<BluetoothAdvertisement::UUIDList>& service_uuids =
       advertisement_data->service_uuids();
   if (!service_uuids || advertisement_data->manufacturer_data() ||
       advertisement_data->solicit_uuids() ||
diff --git a/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac_unittest.mm b/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac_unittest.mm
index ba233e9..33986b22c 100644
--- a/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac_unittest.mm
+++ b/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac_unittest.mm
@@ -236,7 +236,7 @@
 TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest,
        Unregister_InvalidAdvertisement) {
   scoped_refptr<BluetoothAdvertisementMac> invalid_advertisement =
-      new BluetoothAdvertisementMac(absl::nullopt, base::DoNothing(),
+      new BluetoothAdvertisementMac(std::nullopt, base::DoNothing(),
                                     base::DoNothing(), &advertisement_manager_);
 
   // Register advertisement.
diff --git a/device/bluetooth/bluetooth_low_energy_device_mac.h b/device/bluetooth/bluetooth_low_energy_device_mac.h
index a60484b..b9b66b6a 100644
--- a/device/bluetooth/bluetooth_low_energy_device_mac.h
+++ b/device/bluetooth/bluetooth_low_energy_device_mac.h
@@ -8,13 +8,13 @@
 #import <CoreBluetooth/CoreBluetooth.h>
 #include <stdint.h>
 
+#include <optional>
 #include <set>
 #include <string_view>
 
 #include "build/build_config.h"
 #include "crypto/sha2.h"
 #include "device/bluetooth/bluetooth_device_mac.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if !BUILDFLAG(IS_IOS)
 #import <IOBluetooth/IOBluetooth.h>
@@ -53,7 +53,7 @@
   uint16_t GetProductID() const override;
   uint16_t GetDeviceID() const override;
   uint16_t GetAppearance() const override;
-  absl::optional<std::string> GetName() const override;
+  std::optional<std::string> GetName() const override;
   bool IsPaired() const override;
   bool IsConnected() const override;
   bool IsGattConnected() const override;
@@ -89,7 +89,7 @@
  protected:
   // BluetoothDevice override.
   void CreateGattConnectionImpl(
-      absl::optional<BluetoothUUID> service_uuid) override;
+      std::optional<BluetoothUUID> service_uuid) override;
   void DisconnectGatt() override;
 
   // Methods used by BluetoothLowEnergyPeripheralBridge.
diff --git a/device/bluetooth/bluetooth_low_energy_device_mac.mm b/device/bluetooth/bluetooth_low_energy_device_mac.mm
index 9c20890..778bc9c 100644
--- a/device/bluetooth/bluetooth_low_energy_device_mac.mm
+++ b/device/bluetooth/bluetooth_low_energy_device_mac.mm
@@ -90,10 +90,10 @@
   return 0;
 }
 
-absl::optional<std::string> BluetoothLowEnergyDeviceMac::GetName() const {
+std::optional<std::string> BluetoothLowEnergyDeviceMac::GetName() const {
   if ([peripheral_ name])
     return base::SysNSStringToUTF8([peripheral_ name]);
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 bool BluetoothLowEnergyDeviceMac::IsPaired() const {
@@ -201,7 +201,7 @@
 }
 
 void BluetoothLowEnergyDeviceMac::CreateGattConnectionImpl(
-    absl::optional<BluetoothUUID> serivce_uuid) {
+    std::optional<BluetoothUUID> serivce_uuid) {
   if (!IsGattConnected()) {
     GetLowEnergyAdapter()->CreateGattConnection(this);
   }
@@ -418,7 +418,7 @@
   DVLOG(1) << *this << ": GATT connected.";
   if (!connected_) {
     connected_ = true;
-    DidConnectGatt(/*error_code=*/absl::nullopt);
+    DidConnectGatt(/*error_code=*/std::nullopt);
     DiscoverPrimaryServices();
   } else {
     // -[<CBCentralManagerDelegate> centralManager:didConnectPeripheral:] can be
@@ -539,7 +539,7 @@
                          const BluetoothLowEnergyDeviceMac& device) {
   // TODO(crbug.com/703878): Should use
   // BluetoothLowEnergyDeviceMac::GetNameForDisplay() instead.
-  absl::optional<std::string> name = device.GetName();
+  std::optional<std::string> name = device.GetName();
   const char* is_gatt_connected =
       device.IsGattConnected() ? "GATT connected" : "GATT disconnected";
   return out << "<BluetoothLowEnergyDeviceMac " << device.GetAddress() << "/"
diff --git a/device/bluetooth/bluetooth_low_energy_scan_filter.cc b/device/bluetooth/bluetooth_low_energy_scan_filter.cc
index 44d1fa98..a5920fe 100644
--- a/device/bluetooth/bluetooth_low_energy_scan_filter.cc
+++ b/device/bluetooth/bluetooth_low_energy_scan_filter.cc
@@ -4,9 +4,10 @@
 
 #include "device/bluetooth/bluetooth_low_energy_scan_filter.h"
 
+#include <optional>
+
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace {
 
@@ -97,7 +98,7 @@
     base::TimeDelta device_found_timeout,
     base::TimeDelta device_lost_timeout,
     const std::vector<Pattern>& patterns,
-    absl::optional<base::TimeDelta> rssi_sampling_period) {
+    std::optional<base::TimeDelta> rssi_sampling_period) {
   return Create(GetDeviceFoundRSSIThreshold(device_range),
                 GetDeviceLostRSSIThreshold(device_range), device_found_timeout,
                 device_lost_timeout, patterns, rssi_sampling_period);
@@ -111,7 +112,7 @@
     base::TimeDelta device_found_timeout,
     base::TimeDelta device_lost_timeout,
     const std::vector<BluetoothLowEnergyScanFilter::Pattern>& patterns,
-    absl::optional<base::TimeDelta> rssi_sampling_period) {
+    std::optional<base::TimeDelta> rssi_sampling_period) {
   // We use WrapUnique() here so that we can call the private constructor.
   auto filter = base::WrapUnique(new BluetoothLowEnergyScanFilter(
       device_found_rssi_threshold, device_lost_rssi_threshold,
@@ -130,7 +131,7 @@
     base::TimeDelta device_found_timeout,
     base::TimeDelta device_lost_timeout,
     std::vector<Pattern> patterns,
-    absl::optional<base::TimeDelta> rssi_sampling_period)
+    std::optional<base::TimeDelta> rssi_sampling_period)
     : device_found_rssi_threshold_(device_found_rssi_threshold),
       device_lost_rssi_threshold_(device_lost_rssi_threshold),
       device_found_timeout_(device_found_timeout),
diff --git a/device/bluetooth/bluetooth_low_energy_scan_filter.h b/device/bluetooth/bluetooth_low_energy_scan_filter.h
index 8111fb84..2776248a 100644
--- a/device/bluetooth/bluetooth_low_energy_scan_filter.h
+++ b/device/bluetooth/bluetooth_low_energy_scan_filter.h
@@ -7,12 +7,13 @@
 
 #include <stddef.h>
 #include <stdint.h>
+
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "base/time/time.h"
 #include "device/bluetooth/bluetooth_export.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -76,7 +77,7 @@
       base::TimeDelta device_found_timeout,
       base::TimeDelta device_lost_timeout,
       const std::vector<Pattern>& patterns,
-      absl::optional<base::TimeDelta> rssi_sampling_period);
+      std::optional<base::TimeDelta> rssi_sampling_period);
 
   static std::unique_ptr<BluetoothLowEnergyScanFilter> Create(
       int16_t device_found_rssi_threshold,
@@ -84,7 +85,7 @@
       base::TimeDelta device_found_timeout,
       base::TimeDelta device_lost_timeout,
       const std::vector<Pattern>& patterns,
-      absl::optional<base::TimeDelta> rssi_sampling_period);
+      std::optional<base::TimeDelta> rssi_sampling_period);
 
   BluetoothLowEnergyScanFilter(const BluetoothLowEnergyScanFilter&) = delete;
   BluetoothLowEnergyScanFilter& operator=(const BluetoothLowEnergyScanFilter&) =
@@ -100,7 +101,7 @@
   base::TimeDelta device_found_timeout() const { return device_found_timeout_; }
   base::TimeDelta device_lost_timeout() const { return device_lost_timeout_; }
   const std::vector<Pattern>& patterns() const { return patterns_; }
-  const absl::optional<base::TimeDelta>& rssi_sampling_period() const {
+  const std::optional<base::TimeDelta>& rssi_sampling_period() const {
     return rssi_sampling_period_;
   }
 
@@ -111,7 +112,7 @@
       base::TimeDelta device_found_timeout,
       base::TimeDelta device_lost_timeout,
       std::vector<Pattern> patterns,
-      absl::optional<base::TimeDelta> rssi_sampling_period);
+      std::optional<base::TimeDelta> rssi_sampling_period);
 
   bool IsValid() const;
 
@@ -143,7 +144,7 @@
   // propagated after the specified time period (rounded up to the nearest 100
   // ms). A lower sampling period will result in higher power consumption, with
   // the default setting being the most power-efficient.
-  absl::optional<base::TimeDelta> rssi_sampling_period_;
+  std::optional<base::TimeDelta> rssi_sampling_period_;
 };
 
 }  // namespace device
diff --git a/device/bluetooth/bluetooth_low_energy_scan_filter_unittest.cc b/device/bluetooth/bluetooth_low_energy_scan_filter_unittest.cc
index 6731fb9..124f0be 100644
--- a/device/bluetooth/bluetooth_low_energy_scan_filter_unittest.cc
+++ b/device/bluetooth/bluetooth_low_energy_scan_filter_unittest.cc
@@ -2,12 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "device/bluetooth/bluetooth_low_energy_scan_filter.h"
+
 #include <stdint.h>
+
+#include <optional>
 #include <vector>
 
-#include "device/bluetooth/bluetooth_low_energy_scan_filter.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace {
 
@@ -33,14 +35,14 @@
   auto filter = device::BluetoothLowEnergyScanFilter::Create(
       kDeviceFoundRSSIThreshold, kDeviceLostRSSIThreshold, kDeviceFoundTimeout,
       kDeviceLostTimeout, {GetPattern()},
-      /*rssi_sampling_period=*/absl::nullopt);
+      /*rssi_sampling_period=*/std::nullopt);
   EXPECT_TRUE(filter);
 }
 
 TEST(BluetoothLowEnergyScanFilterTest, InvalidNoPattern) {
   auto filter = device::BluetoothLowEnergyScanFilter::Create(
       kDeviceFoundRSSIThreshold, kDeviceLostRSSIThreshold, kDeviceFoundTimeout,
-      kDeviceLostTimeout, {}, /*rssi_sampling_period=*/absl::nullopt);
+      kDeviceLostTimeout, {}, /*rssi_sampling_period=*/std::nullopt);
   EXPECT_FALSE(filter);
 }
 
@@ -51,20 +53,20 @@
       kPatternValue);
   auto filter = device::BluetoothLowEnergyScanFilter::Create(
       kDeviceFoundRSSIThreshold, kDeviceLostRSSIThreshold, kDeviceFoundTimeout,
-      kDeviceLostTimeout, {pattern}, /*rssi_sampling_period=*/absl::nullopt);
+      kDeviceLostTimeout, {pattern}, /*rssi_sampling_period=*/std::nullopt);
   EXPECT_FALSE(filter);
 }
 
 TEST(BluetoothLowEnergyScanFilterTest, InvalidBadTimeout) {
   auto filter = device::BluetoothLowEnergyScanFilter::Create(
       kDeviceFoundRSSIThreshold, kDeviceLostRSSIThreshold, kDeviceFoundTimeout,
-      base::Seconds(0), {GetPattern()}, /*rssi_sampling_period=*/absl::nullopt);
+      base::Seconds(0), {GetPattern()}, /*rssi_sampling_period=*/std::nullopt);
   EXPECT_FALSE(filter);
 
   filter = device::BluetoothLowEnergyScanFilter::Create(
       kDeviceFoundRSSIThreshold, kDeviceLostRSSIThreshold, kDeviceFoundTimeout,
       base::Seconds(301), {GetPattern()},
-      /*rssi_sampling_period=*/absl::nullopt);
+      /*rssi_sampling_period=*/std::nullopt);
   EXPECT_FALSE(filter);
 }
 
@@ -107,7 +109,7 @@
   filter = device::BluetoothLowEnergyScanFilter::Create(
       kDeviceFoundRSSIThreshold, kDeviceLostRSSIThreshold, kDeviceFoundTimeout,
       kDeviceLostTimeout, {GetPattern()},
-      /*rssi_sampling_period=*/absl::nullopt);
+      /*rssi_sampling_period=*/std::nullopt);
   EXPECT_TRUE(filter);
   EXPECT_FALSE(filter->rssi_sampling_period().has_value());
 }
@@ -116,25 +118,25 @@
   auto filter = device::BluetoothLowEnergyScanFilter::Create(
       /*device_found_rssi_threshold=*/-128, kDeviceLostRSSIThreshold,
       kDeviceFoundTimeout, kDeviceLostTimeout, {GetPattern()},
-      /*rssi_sampling_period=*/absl::nullopt);
+      /*rssi_sampling_period=*/std::nullopt);
   EXPECT_FALSE(filter);
 
   filter = device::BluetoothLowEnergyScanFilter::Create(
       /*device_found_rssi_threshold=*/21, kDeviceLostRSSIThreshold,
       kDeviceFoundTimeout, kDeviceLostTimeout, {GetPattern()},
-      /*rssi_sampling_period=*/absl::nullopt);
+      /*rssi_sampling_period=*/std::nullopt);
   EXPECT_FALSE(filter);
 
   filter = device::BluetoothLowEnergyScanFilter::Create(
       kDeviceFoundRSSIThreshold, /*device_lost_rssi_threshold=*/-128,
       kDeviceFoundTimeout, kDeviceLostTimeout, {GetPattern()},
-      /*rssi_sampling_period=*/absl::nullopt);
+      /*rssi_sampling_period=*/std::nullopt);
   EXPECT_FALSE(filter);
 
   filter = device::BluetoothLowEnergyScanFilter::Create(
       kDeviceFoundRSSIThreshold, /*device_lost_rssi_threshold=*/21,
       kDeviceFoundTimeout, kDeviceLostTimeout, {GetPattern()},
-      /*rssi_sampling_period=*/absl::nullopt);
+      /*rssi_sampling_period=*/std::nullopt);
   EXPECT_FALSE(filter);
 
   // Expect a failure if the "device lost" threshold is greater than the "device
@@ -142,7 +144,7 @@
   filter = device::BluetoothLowEnergyScanFilter::Create(
       /*device_found_rssi_threshold=*/-80, /*device_lost_rssi_threshold=*/-60,
       kDeviceFoundTimeout, kDeviceLostTimeout, {GetPattern()},
-      /*rssi_sampling_period=*/absl::nullopt);
+      /*rssi_sampling_period=*/std::nullopt);
   EXPECT_FALSE(filter);
 }
 
@@ -150,19 +152,19 @@
   auto filter = device::BluetoothLowEnergyScanFilter::Create(
       device::BluetoothLowEnergyScanFilter::Range::kImmediate,
       kDeviceFoundTimeout, kDeviceLostTimeout, {GetPattern()},
-      /*rssi_sampling_period=*/absl::nullopt);
+      /*rssi_sampling_period=*/std::nullopt);
   EXPECT_TRUE(filter);
 
   filter = device::BluetoothLowEnergyScanFilter::Create(
       device::BluetoothLowEnergyScanFilter::Range::kNear, kDeviceFoundTimeout,
       kDeviceLostTimeout, {GetPattern()},
-      /*rssi_sampling_period=*/absl::nullopt);
+      /*rssi_sampling_period=*/std::nullopt);
   EXPECT_TRUE(filter);
 
   filter = device::BluetoothLowEnergyScanFilter::Create(
       device::BluetoothLowEnergyScanFilter::Range::kFar, kDeviceFoundTimeout,
       kDeviceLostTimeout, {GetPattern()},
-      /*rssi_sampling_period=*/absl::nullopt);
+      /*rssi_sampling_period=*/std::nullopt);
   EXPECT_TRUE(filter);
 }
 
diff --git a/device/bluetooth/bluetooth_low_energy_scan_session.h b/device/bluetooth/bluetooth_low_energy_scan_session.h
index 917ac2b36..6548c64 100644
--- a/device/bluetooth/bluetooth_low_energy_scan_session.h
+++ b/device/bluetooth/bluetooth_low_energy_scan_session.h
@@ -5,8 +5,9 @@
 #ifndef DEVICE_BLUETOOTH_BLUETOOTH_LOW_ENERGY_SCAN_SESSION_H_
 #define DEVICE_BLUETOOTH_BLUETOOTH_LOW_ENERGY_SCAN_SESSION_H_
 
+#include <optional>
+
 #include "device/bluetooth/bluetooth_export.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -28,8 +29,7 @@
     // the session failed to start.
     virtual void OnSessionStarted(
         BluetoothLowEnergyScanSession* scan_session,
-        absl::optional<BluetoothLowEnergyScanSession::ErrorCode>
-            error_code) = 0;
+        std::optional<BluetoothLowEnergyScanSession::ErrorCode> error_code) = 0;
 
     // Notifies that a device matching the filter criteria has been found.
     virtual void OnDeviceFound(BluetoothLowEnergyScanSession* scan_session,
diff --git a/device/bluetooth/bluetooth_pairing_winrt.cc b/device/bluetooth/bluetooth_pairing_winrt.cc
index 35583c3..51d8b9b 100644
--- a/device/bluetooth/bluetooth_pairing_winrt.cc
+++ b/device/bluetooth/bluetooth_pairing_winrt.cc
@@ -55,7 +55,7 @@
 using Microsoft::WRL::ComPtr;
 
 void PostTask(BluetoothPairingWinrt::ConnectCallback callback,
-              absl::optional<BluetoothDevice::ConnectErrorCode> error_code) {
+              std::optional<BluetoothDevice::ConnectErrorCode> error_code) {
   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), error_code));
 }
@@ -391,7 +391,7 @@
   switch (status) {
     case DevicePairingResultStatus_AlreadyPaired:
     case DevicePairingResultStatus_Paired:
-      std::move(callback_).Run(/*error_code=*/absl::nullopt);
+      std::move(callback_).Run(/*error_code=*/std::nullopt);
       return;
     case DevicePairingResultStatus_PairingCanceled:
       std::move(callback_).Run(
diff --git a/device/bluetooth/bluetooth_pairing_winrt.h b/device/bluetooth/bluetooth_pairing_winrt.h
index 1629f1a..ab7203e3 100644
--- a/device/bluetooth/bluetooth_pairing_winrt.h
+++ b/device/bluetooth/bluetooth_pairing_winrt.h
@@ -9,13 +9,13 @@
 #include <windows.foundation.h>
 #include <wrl/client.h>
 
+#include <optional>
 #include <string_view>
 
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
 #include "device/bluetooth/bluetooth_device.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -27,7 +27,7 @@
  public:
   // On error |error_code| will have a value, otherwise successful.
   using ConnectCallback = base::OnceCallback<void(
-      absl::optional<BluetoothDevice::ConnectErrorCode> error_code)>;
+      std::optional<BluetoothDevice::ConnectErrorCode> error_code)>;
 
   BluetoothPairingWinrt(
       BluetoothDeviceWinrt* device,
@@ -94,7 +94,7 @@
       custom_pairing_;
   ConnectCallback callback_;
 
-  absl::optional<EventRegistrationToken> pairing_requested_token_;
+  std::optional<EventRegistrationToken> pairing_requested_token_;
 
   Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IDeferral> pairing_deferral_;
   Microsoft::WRL::ComPtr<
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic.cc b/device/bluetooth/bluetooth_remote_gatt_characteristic.cc
index b6ab7af..1c53074 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic.cc
@@ -19,7 +19,7 @@
 
 BluetoothRemoteGattCharacteristic::CommandStatus::CommandStatus(
     CommandType type,
-    absl::optional<BluetoothRemoteGattService::GattErrorCode> error_code)
+    std::optional<BluetoothRemoteGattService::GattErrorCode> error_code)
     : type(type), error_code(error_code) {}
 
 BluetoothRemoteGattCharacteristic::CommandStatus::CommandStatus(
@@ -109,7 +109,7 @@
     NotifySessionCallback callback,
     ErrorCallback error_callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  StartNotifySessionInternal(absl::nullopt, std::move(callback),
+  StartNotifySessionInternal(std::nullopt, std::move(callback),
                              std::move(error_callback));
 }
 
@@ -137,7 +137,7 @@
 }
 
 void BluetoothRemoteGattCharacteristic::StartNotifySessionInternal(
-    const absl::optional<NotificationType>& notification_type,
+    const std::optional<NotificationType>& notification_type,
     NotifySessionCallback callback,
     ErrorCallback error_callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -161,7 +161,7 @@
 }
 
 void BluetoothRemoteGattCharacteristic::ExecuteStartNotifySession(
-    const absl::optional<NotificationType>& notification_type,
+    const std::optional<NotificationType>& notification_type,
     NotifySessionCallback callback,
     ErrorCallback error_callback,
     CommandStatus previous_command) {
@@ -430,7 +430,7 @@
 }
 
 bool BluetoothRemoteGattCharacteristic::IsNotificationTypeSupported(
-    const absl::optional<NotificationType>& notification_type) {
+    const std::optional<NotificationType>& notification_type) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   Properties properties = GetProperties();
   bool hasNotify = (properties & PROPERTY_NOTIFY) != 0;
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic.h b/device/bluetooth/bluetooth_remote_gatt_characteristic.h
index f7b3c31c..277ed664 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic.h
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <optional>
 #include <set>
 #include <string>
 #include <vector>
@@ -23,7 +24,6 @@
 #include "device/bluetooth/bluetooth_gatt_notify_session.h"
 #include "device/bluetooth/bluetooth_remote_gatt_service.h"
 #include "device/bluetooth/public/cpp/bluetooth_uuid.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -53,7 +53,7 @@
   // have a value and |value| may be used. When unsuccessful |error_code| will
   // have a value and |value| must be ignored.
   using ValueCallback = base::OnceCallback<void(
-      absl::optional<BluetoothGattService::GattErrorCode> error_code,
+      std::optional<BluetoothGattService::GattErrorCode> error_code,
       const std::vector<uint8_t>& value)>;
 
   // The NotifySessionCallback is used to return sessions after they have
@@ -233,12 +233,12 @@
 
   struct CommandStatus {
     explicit CommandStatus(CommandType type = CommandType::kNone,
-                           absl::optional<BluetoothGattService::GattErrorCode>
-                               error_code = absl::nullopt);
+                           std::optional<BluetoothGattService::GattErrorCode>
+                               error_code = std::nullopt);
     CommandStatus(CommandStatus&& other);
 
     CommandType type;
-    absl::optional<BluetoothGattService::GattErrorCode> error_code;
+    std::optional<BluetoothGattService::GattErrorCode> error_code;
   };
 
   // Stops an active notify session for the remote characteristic. On success,
@@ -277,11 +277,11 @@
   };
 
   void StartNotifySessionInternal(
-      const absl::optional<NotificationType>& notification_type,
+      const std::optional<NotificationType>& notification_type,
       NotifySessionCallback callback,
       ErrorCallback error_callback);
   void ExecuteStartNotifySession(
-      const absl::optional<NotificationType>& notification_type,
+      const std::optional<NotificationType>& notification_type,
       NotifySessionCallback callback,
       ErrorCallback error_callback,
       CommandStatus previous_command);
@@ -300,7 +300,7 @@
                                 base::OnceClosure callback,
                                 BluetoothGattService::GattErrorCode error);
   bool IsNotificationTypeSupported(
-      const absl::optional<NotificationType>& notification_type);
+      const std::optional<NotificationType>& notification_type);
 
   // Pending StartNotifySession / StopNotifySession calls.
   // The front will either be awaiting execution, or in the process of being
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_android.cc b/device/bluetooth/bluetooth_remote_gatt_characteristic_android.cc
index 5fd0a501..e5b5573 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_android.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_android.cc
@@ -238,7 +238,7 @@
 
   if (status == 0) {  // android.bluetooth.BluetoothGatt.GATT_SUCCESS
     base::android::JavaByteArrayToByteVector(env, value, &value_);
-    std::move(read_callback).Run(/*error_code=*/absl::nullopt, value_);
+    std::move(read_callback).Run(/*error_code=*/std::nullopt, value_);
   } else {
     std::move(read_callback)
         .Run(BluetoothRemoteGattServiceAndroid::GetGattErrorCode(status),
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm b/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm
index 2fe92f9..965814b6 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm
@@ -288,7 +288,7 @@
     }
     DVLOG(1) << *this << ": Read request arrived.";
     UpdateValue();
-    std::move(read_callback).Run(/*error_code=*/absl::nullopt, value_);
+    std::move(read_callback).Run(/*error_code=*/std::nullopt, value_);
   } else if (IsNotifying()) {
     DVLOG(1) << *this << ": Notification arrived.";
     UpdateValue();
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc b/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
index 7dc48b9..4fac09d 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
@@ -469,7 +469,7 @@
 
   bool read_characteristic_failed = false;
   characteristic1_->ReadRemoteCharacteristic(base::BindLambdaForTesting(
-      [&](absl::optional<BluetoothGattService::GattErrorCode> error_code,
+      [&](std::optional<BluetoothGattService::GattErrorCode> error_code,
           const std::vector<uint8_t>&) {
         EXPECT_EQ(BluetoothGattService::GattErrorCode::kFailed, error_code);
         read_characteristic_failed = true;
@@ -849,7 +849,7 @@
 static void TestCallback(
     BluetoothRemoteGattCharacteristic::ValueCallback callback,
     const TestBluetoothAdapterObserver& callback_observer,
-    absl::optional<BluetoothGattService::GattErrorCode> error_code,
+    std::optional<BluetoothGattService::GattErrorCode> error_code,
     const std::vector<uint8_t>& value) {
   EXPECT_EQ(0, callback_observer.gatt_characteristic_value_changed_count());
   std::move(callback).Run(error_code, value);
@@ -1269,7 +1269,7 @@
   std::vector<uint8_t> test_vector_2 = {0xf, 0xf0, 0xff};
 
   characteristic1_->ReadRemoteCharacteristic(base::BindLambdaForTesting(
-      [&](absl::optional<BluetoothGattService::GattErrorCode> error_code,
+      [&](std::optional<BluetoothGattService::GattErrorCode> error_code,
           const std::vector<uint8_t>& data) {
         GetReadValueCallback(Call::EXPECTED, Result::SUCCESS)
             .Run(error_code, data);
@@ -1421,7 +1421,7 @@
   std::vector<uint8_t> test_vector_2 = {0xf, 0xf0, 0xff};
 
   characteristic1_->ReadRemoteCharacteristic(base::BindLambdaForTesting(
-      [&](absl::optional<BluetoothGattService::GattErrorCode> error_code,
+      [&](std::optional<BluetoothGattService::GattErrorCode> error_code,
           const std::vector<uint8_t>& data) {
         ASSERT_FALSE(error_code.has_value())
             << "unexpected error: " << static_cast<int>(error_code.value());
@@ -1476,7 +1476,7 @@
   std::vector<uint8_t> test_vector_2 = {0xf, 0xf0, 0xff};
 
   characteristic1_->ReadRemoteCharacteristic(base::BindLambdaForTesting(
-      [&](absl::optional<BluetoothGattService::GattErrorCode> error_code,
+      [&](std::optional<BluetoothGattService::GattErrorCode> error_code,
           const std::vector<uint8_t>& data) {
         GetReadValueCallback(Call::EXPECTED, Result::SUCCESS)
             .Run(error_code, data);
@@ -1534,9 +1534,9 @@
         EXPECT_EQ(test_vector_1, last_write_value_);
 
         characteristic1_->ReadRemoteCharacteristic(base::BindLambdaForTesting(
-            [&](absl::optional<BluetoothGattService::GattErrorCode> error_code,
+            [&](std::optional<BluetoothGattService::GattErrorCode> error_code,
                 const std::vector<uint8_t>& data) {
-              EXPECT_EQ(error_code, absl::nullopt);
+              EXPECT_EQ(error_code, std::nullopt);
               EXPECT_EQ(1, gatt_read_characteristic_attempts_);
               EXPECT_EQ(1, gatt_write_characteristic_attempts_);
               EXPECT_EQ(test_vector_2, data);
@@ -1980,7 +1980,7 @@
             loop1.Quit();
           }));
   characteristic1_->ReadRemoteCharacteristic(base::BindLambdaForTesting(
-      [&](absl::optional<BluetoothGattService::GattErrorCode> error_code,
+      [&](std::optional<BluetoothGattService::GattErrorCode> error_code,
           const std::vector<uint8_t>& data) {
         EXPECT_EQ(BluetoothGattService::GattErrorCode::kInProgress, error_code);
         loop2.Quit();
@@ -2058,7 +2058,7 @@
   base::RunLoop loop2;
   std::vector<uint8_t> empty_vector;
   characteristic1_->ReadRemoteCharacteristic(base::BindLambdaForTesting(
-      [&](absl::optional<BluetoothGattService::GattErrorCode> error_code,
+      [&](std::optional<BluetoothGattService::GattErrorCode> error_code,
           const std::vector<uint8_t>& data) {
         EXPECT_FALSE(error_code.has_value()) << "unexpected failure";
         loop1.Quit();
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.cc b/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.cc
index 711a5de..e827429d6 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.cc
@@ -539,7 +539,7 @@
   }
 
   value_.assign(data, data + length);
-  std::move(pending_read_callback).Run(/*error_code=*/absl::nullopt, value_);
+  std::move(pending_read_callback).Run(/*error_code=*/std::nullopt, value_);
 }
 
 void BluetoothRemoteGattCharacteristicWinrt::OnWriteValueWithResultAndOption(
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.h b/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.h
index c0fb509..c5745794 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.h
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.h
@@ -144,7 +144,7 @@
   ValueCallback pending_read_callback_;
   std::unique_ptr<PendingWriteCallbacks> pending_write_callbacks_;
   std::unique_ptr<PendingNotificationCallbacks> pending_notification_callbacks_;
-  absl::optional<EventRegistrationToken> value_changed_token_;
+  std::optional<EventRegistrationToken> value_changed_token_;
   // The destructor runs callbacks. Methods can use |destructor_called_| to
   // protect against reentrant calls to a partially deleted instance.
   bool destructor_called_ = false;
diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor.h b/device/bluetooth/bluetooth_remote_gatt_descriptor.h
index fab635c..11a6884e 100644
--- a/device/bluetooth/bluetooth_remote_gatt_descriptor.h
+++ b/device/bluetooth/bluetooth_remote_gatt_descriptor.h
@@ -6,6 +6,8 @@
 #define DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_DESCRIPTOR_H_
 
 #include <stdint.h>
+
+#include <optional>
 #include <vector>
 
 #include "base/functional/callback.h"
@@ -13,7 +15,6 @@
 #include "device/bluetooth/bluetooth_export.h"
 #include "device/bluetooth/bluetooth_gatt_descriptor.h"
 #include "device/bluetooth/public/cpp/bluetooth_uuid.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -42,7 +43,7 @@
   // will contain a value and |value| should be ignored. When successful
   // |error_code| will have no value and |value| may be used.
   using ValueCallback = base::OnceCallback<void(
-      absl::optional<BluetoothGattService::GattErrorCode> error_code,
+      std::optional<BluetoothGattService::GattErrorCode> error_code,
       const std::vector<uint8_t>& value)>;
 
   // Returns the value of the descriptor. For remote descriptors, this is the
diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor_android.cc b/device/bluetooth/bluetooth_remote_gatt_descriptor_android.cc
index be8f95f..9b7d7fae 100644
--- a/device/bluetooth/bluetooth_remote_gatt_descriptor_android.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_descriptor_android.cc
@@ -145,7 +145,7 @@
 
   if (status == 0) {  // android.bluetooth.BluetoothGatt.GATT_SUCCESS
     base::android::JavaByteArrayToByteVector(env, value, &value_);
-    std::move(read_callback).Run(/*error_code=*/absl::nullopt, value_);
+    std::move(read_callback).Run(/*error_code=*/std::nullopt, value_);
     // TODO(https://crbug.com/584369): Call GattDescriptorValueChanged.
   } else {
     std::move(read_callback)
diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.mm b/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.mm
index 04ce2c3..88337f9f 100644
--- a/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.mm
+++ b/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.mm
@@ -148,7 +148,7 @@
   }
   DVLOG(1) << *this << ": Value read.";
   value_ = VectorValueFromObjC([cb_descriptor_ value]);
-  std::move(read_value_callback_).Run(/*error_code=*/absl::nullopt, value_);
+  std::move(read_value_callback_).Run(/*error_code=*/std::nullopt, value_);
 }
 
 void BluetoothRemoteGattDescriptorMac::DidWriteValueForDescriptor(
diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor_winrt.cc b/device/bluetooth/bluetooth_remote_gatt_descriptor_winrt.cc
index 3c8d780..eadc38f2 100644
--- a/device/bluetooth/bluetooth_remote_gatt_descriptor_winrt.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_descriptor_winrt.cc
@@ -307,7 +307,7 @@
   }
 
   value_.assign(data, data + length);
-  std::move(pending_read_callback).Run(/*error_code=*/absl::nullopt, value_);
+  std::move(pending_read_callback).Run(/*error_code=*/std::nullopt, value_);
 }
 
 void BluetoothRemoteGattDescriptorWinrt::OnWriteValueWithResult(
diff --git a/device/bluetooth/bluetooth_socket_mac.mm b/device/bluetooth/bluetooth_socket_mac.mm
index e5ecb1b..bb284d2c 100644
--- a/device/bluetooth/bluetooth_socket_mac.mm
+++ b/device/bluetooth/bluetooth_socket_mac.mm
@@ -9,6 +9,7 @@
 
 #include <limits>
 #include <memory>
+#include <optional>
 #include <sstream>
 #include <string>
 #include <utility>
@@ -32,7 +33,6 @@
 #include "device/bluetooth/bluetooth_rfcomm_channel_mac.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 using device::BluetoothSocket;
 
@@ -255,7 +255,7 @@
 // corresponding to the provided |uuid|, |name|, and |protocol_definition|. Does
 // not include a service name in the definition if |name| is null.
 NSDictionary* BuildServiceDefinition(const BluetoothUUID& uuid,
-                                     const absl::optional<std::string>& name,
+                                     const std::optional<std::string>& name,
                                      NSArray* protocol_definition) {
   NSMutableDictionary* service_definition = [NSMutableDictionary dictionary];
 
@@ -347,7 +347,7 @@
 // Returns true iff the |requested_channel_id| was registered in the RFCOMM
 // |service_record|. If it was, also updates |registered_channel_id| with the
 // registered value, as the requested id may have been left unspecified.
-bool VerifyRfcommService(const absl::optional<int>& requested_channel_id,
+bool VerifyRfcommService(const std::optional<int>& requested_channel_id,
                          BluetoothRFCOMMChannelID* registered_channel_id,
                          IOBluetoothSDPServiceRecord* service_record) {
   // Test whether the requested channel id was available.
@@ -383,7 +383,7 @@
 // Returns true iff the |requested_psm| was registered in the L2CAP
 // |service_record|. If it was, also updates |registered_psm| with the
 // registered value, as the requested PSM may have been left unspecified.
-bool VerifyL2capService(const absl::optional<int>& requested_psm,
+bool VerifyL2capService(const std::optional<int>& requested_psm,
                         BluetoothL2CAPPSM* registered_psm,
                         IOBluetoothSDPServiceRecord* service_record) {
   // Test whether the requested PSM was available.
diff --git a/device/bluetooth/bluetooth_task_manager_win.h b/device/bluetooth/bluetooth_task_manager_win.h
index 18d6f680..c3f54fdb 100644
--- a/device/bluetooth/bluetooth_task_manager_win.h
+++ b/device/bluetooth/bluetooth_task_manager_win.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -18,7 +19,6 @@
 #include "base/win/scoped_handle.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_export.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
 
@@ -82,7 +82,7 @@
 
     // Properties common to Bluetooth Classic and LE devices.
     std::string address;  // This uniquely identifies the device.
-    absl::optional<std::string> name;  // Friendly name
+    std::optional<std::string> name;  // Friendly name
     bool visible;
     bool connected;
     bool authenticated;
diff --git a/device/bluetooth/bluez/bluetooth_adapter_bluez.cc b/device/bluetooth/bluez/bluetooth_adapter_bluez.cc
index 028c61f..57e4969 100644
--- a/device/bluetooth/bluez/bluetooth_adapter_bluez.cc
+++ b/device/bluetooth/bluez/bluetooth_adapter_bluez.cc
@@ -710,12 +710,12 @@
 
 void BluetoothAdapterBlueZ::ConnectDevice(
     const std::string& address,
-    const absl::optional<device::BluetoothDevice::AddressType>& address_type,
+    const std::optional<device::BluetoothDevice::AddressType>& address_type,
     ConnectDeviceCallback callback,
     ConnectDeviceErrorCallback error_callback) {
   DCHECK(bluez::BluezDBusManager::Get());
 
-  absl::optional<BluetoothAdapterClient::AddressType> client_address_type;
+  std::optional<BluetoothAdapterClient::AddressType> client_address_type;
   if (address_type) {
     switch (*address_type) {
       case device::BluetoothDevice::AddressType::ADDR_TYPE_PUBLIC:
diff --git a/device/bluetooth/bluez/bluetooth_adapter_bluez.h b/device/bluetooth/bluez/bluetooth_adapter_bluez.h
index 0dfdb59..b97bc9cf 100644
--- a/device/bluetooth/bluez/bluetooth_adapter_bluez.h
+++ b/device/bluetooth/bluez/bluetooth_adapter_bluez.h
@@ -38,12 +38,13 @@
 #include "device/bluetooth/dbus/bluetooth_profile_service_provider.h"
 
 #if BUILDFLAG(IS_CHROMEOS)
+#include <optional>
+
 #include "device/bluetooth/bluetooth_low_energy_scan_filter.h"
 #include "device/bluetooth/bluetooth_low_energy_scan_session.h"
 #include "device/bluetooth/dbus/bluetooth_advertisement_monitor_manager_client.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/data_decoder/public/mojom/ble_scan_parser.mojom.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 namespace base {
@@ -172,7 +173,7 @@
 
   void ConnectDevice(
       const std::string& address,
-      const absl::optional<device::BluetoothDevice::AddressType>& address_type,
+      const std::optional<device::BluetoothDevice::AddressType>& address_type,
       ConnectDeviceCallback callback,
       ConnectDeviceErrorCallback error_callback) override;
 
diff --git a/device/bluetooth/bluez/bluetooth_bluez_unittest.cc b/device/bluetooth/bluez/bluetooth_bluez_unittest.cc
index e91fa90..f830190a 100644
--- a/device/bluetooth/bluez/bluetooth_bluez_unittest.cc
+++ b/device/bluetooth/bluez/bluetooth_bluez_unittest.cc
@@ -6,6 +6,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <optional>
 #include <utility>
 #include <vector>
 
@@ -46,7 +47,6 @@
 #include "device/bluetooth/test/test_bluetooth_adapter_observer.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
 #if BUILDFLAG(IS_CHROMEOS)
@@ -104,7 +104,7 @@
       kBackgroundScanningDeviceLostRSSIThreshold,
       kBackgroundScanningDeviceFoundTimeout,
       kBackgroundScanningDeviceLostTimeout, {pattern},
-      /*rssi_sampling_period=*/absl::nullopt);
+      /*rssi_sampling_period=*/std::nullopt);
 }
 
 bluez::FakeBluetoothAdvertisementMonitorApplicationServiceProvider*
@@ -208,7 +208,7 @@
   // device::BluetoothLowEnergyScanSession::Delegate
   void OnSessionStarted(
       device::BluetoothLowEnergyScanSession* scan_session,
-      absl::optional<device::BluetoothLowEnergyScanSession::ErrorCode>
+      std::optional<device::BluetoothLowEnergyScanSession::ErrorCode>
           error_code) override {
     sessions_started_.push_back(std::make_pair(scan_session, error_code));
   }
@@ -227,7 +227,7 @@
 
   const std::vector<std::pair<
       device::BluetoothLowEnergyScanSession*,
-      absl::optional<device::BluetoothLowEnergyScanSession::ErrorCode>>>&
+      std::optional<device::BluetoothLowEnergyScanSession::ErrorCode>>>&
   sessions_started() const {
     return sessions_started_;
   }
@@ -257,7 +257,7 @@
  private:
   std::vector<std::pair<
       device::BluetoothLowEnergyScanSession*,
-      absl::optional<device::BluetoothLowEnergyScanSession::ErrorCode>>>
+      std::optional<device::BluetoothLowEnergyScanSession::ErrorCode>>>
       sessions_started_;
   std::vector<std::pair<device::BluetoothLowEnergyScanSession*,
                         device::BluetoothDevice*>>
@@ -2483,7 +2483,7 @@
   PerformConnect(
       device, nullptr,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
@@ -2533,7 +2533,7 @@
   PerformConnect(
       device, nullptr,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
@@ -2569,7 +2569,7 @@
   PerformConnect(
       device, nullptr,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
@@ -2619,7 +2619,7 @@
         device, nullptr,
         base::BindLambdaForTesting(
             [&run_loop](
-                absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+                std::optional<BluetoothDevice::ConnectErrorCode> error) {
               EXPECT_FALSE(error.has_value());
               run_loop.Quit();
             }));
@@ -2638,7 +2638,7 @@
         device, nullptr,
         base::BindLambdaForTesting(
             [&run_loop](
-                absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+                std::optional<BluetoothDevice::ConnectErrorCode> error) {
               EXPECT_FALSE(error.has_value());
               run_loop.Quit();
             }));
@@ -2674,7 +2674,7 @@
   PerformConnect(
       device, nullptr,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_EQ(BluetoothDevice::ERROR_FAILED, error);
             run_loop.Quit();
           }));
@@ -2699,7 +2699,7 @@
   PerformConnect(
       device, nullptr,
       base::BindLambdaForTesting(
-          [](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             FAIL() << "Connection callback should never be called.";
           }));
 
@@ -2737,7 +2737,7 @@
   PerformConnect(
       device, nullptr,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
@@ -2841,7 +2841,7 @@
   device->Pair(
       &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
@@ -2886,7 +2886,7 @@
   device->Pair(
       &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
@@ -2913,7 +2913,7 @@
   PerformConnect(
       device, &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
@@ -2971,7 +2971,7 @@
   PerformConnect(
       device, &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
@@ -3035,7 +3035,7 @@
   PerformConnect(
       device, &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
@@ -3095,7 +3095,7 @@
   PerformConnect(
       device, &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
@@ -3160,7 +3160,7 @@
   PerformConnect(
       device, &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
@@ -3221,7 +3221,7 @@
   PerformConnect(
       device, &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
@@ -3276,7 +3276,7 @@
   PerformConnect(
       device, &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
@@ -3329,7 +3329,7 @@
   PerformConnect(
       device, &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_EQ(BluetoothDevice::ERROR_FAILED, error);
             run_loop.Quit();
           }));
@@ -3359,7 +3359,7 @@
   PerformConnect(
       device, &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_EQ(BluetoothDevice::ERROR_AUTH_TIMEOUT, error);
             run_loop.Quit();
           }));
@@ -3390,7 +3390,7 @@
   PerformConnect(
       device, &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_EQ(BluetoothDevice::ERROR_FAILED, error);
             run_loop.Quit();
           }));
@@ -3448,7 +3448,7 @@
   PerformConnect(
       device, &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_EQ(BluetoothDevice::ERROR_AUTH_REJECTED, error);
             run_loop.Quit();
           }));
@@ -3485,7 +3485,7 @@
   PerformConnect(
       device, &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, error);
             run_loop.Quit();
           }));
@@ -3522,7 +3522,7 @@
   PerformConnect(
       device, &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_EQ(BluetoothDevice::ERROR_AUTH_REJECTED, error);
             run_loop.Quit();
           }));
@@ -3559,7 +3559,7 @@
   PerformConnect(
       device, &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, error);
             run_loop.Quit();
           }));
@@ -3597,7 +3597,7 @@
   PerformConnect(
       device, &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_EQ(BluetoothDevice::ERROR_AUTH_REJECTED, error);
             run_loop.Quit();
           }));
@@ -3635,7 +3635,7 @@
   PerformConnect(
       device, &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, error);
             run_loop.Quit();
           }));
@@ -3668,7 +3668,7 @@
   PerformConnect(
       device, &pairing_delegate,
       base::BindLambdaForTesting(
-          [&](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             callback_called = true;
             EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, error);
             run_loop.Quit();
@@ -4254,7 +4254,7 @@
   PerformConnect(
       device, nullptr,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
@@ -5201,7 +5201,7 @@
   advertisement_monitor->delegate()->OnActivate();
   EXPECT_EQ(1u, delegate.sessions_started().size());
   std::pair<device::BluetoothLowEnergyScanSession*,
-            absl::optional<device::BluetoothLowEnergyScanSession::ErrorCode>>
+            std::optional<device::BluetoothLowEnergyScanSession::ErrorCode>>
       session_started_pair = delegate.sessions_started()[0];
 
   // Check that the correct scan session is started.
@@ -5241,7 +5241,7 @@
   EXPECT_EQ(1u, delegate.sessions_started().size());
 
   std::pair<device::BluetoothLowEnergyScanSession*,
-            absl::optional<device::BluetoothLowEnergyScanSession::ErrorCode>>
+            std::optional<device::BluetoothLowEnergyScanSession::ErrorCode>>
       session_started_pair = delegate.sessions_started()[0];
 
   // Check that the correct scan session.
diff --git a/device/bluetooth/bluez/bluetooth_device_bluez.cc b/device/bluetooth/bluez/bluetooth_device_bluez.cc
index 75d6e1b8..5f6a82d 100644
--- a/device/bluetooth/bluez/bluetooth_device_bluez.cc
+++ b/device/bluetooth/bluez/bluetooth_device_bluez.cc
@@ -245,7 +245,7 @@
 }
 
 void BluetoothDeviceBlueZ::CreateGattConnectionImpl(
-    absl::optional<BluetoothUUID> service_uuid) {
+    std::optional<BluetoothUUID> service_uuid) {
 // Once ConnectLE is supported on Linux, this buildflag will not be necessary
 // (this bluez code is only run on Chrome OS and Linux).
 #if BUILDFLAG(IS_CHROMEOS)
@@ -405,7 +405,7 @@
   return properties->appearance.value();
 }
 
-absl::optional<std::string> BluetoothDeviceBlueZ::GetName() const {
+std::optional<std::string> BluetoothDeviceBlueZ::GetName() const {
   bluez::BluetoothDeviceClient::Properties* properties =
       bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
           object_path_);
@@ -414,7 +414,7 @@
   if (properties->name.is_valid())
     return properties->name.value();
   else
-    return absl::nullopt;
+    return std::nullopt;
 }
 
 bool BluetoothDeviceBlueZ::IsPaired() const {
@@ -492,14 +492,14 @@
   return device_uuids_.GetUUIDs();
 }
 
-absl::optional<int8_t> BluetoothDeviceBlueZ::GetInquiryRSSI() const {
+std::optional<int8_t> BluetoothDeviceBlueZ::GetInquiryRSSI() const {
   bluez::BluetoothDeviceClient::Properties* properties =
       bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
           object_path_);
   DCHECK(properties);
 
   if (!properties->rssi.is_valid())
-    return absl::nullopt;
+    return std::nullopt;
 
   // BlueZ uses int16_t because there is no int8_t for DBus, so we should never
   // get an int16_t that cannot be represented by an int8_t. But just in case
@@ -507,14 +507,14 @@
   return ClampPower(properties->rssi.value());
 }
 
-absl::optional<int8_t> BluetoothDeviceBlueZ::GetInquiryTxPower() const {
+std::optional<int8_t> BluetoothDeviceBlueZ::GetInquiryTxPower() const {
   bluez::BluetoothDeviceClient::Properties* properties =
       bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
           object_path_);
   DCHECK(properties);
 
   if (!properties->tx_power.is_valid())
-    return absl::nullopt;
+    return std::nullopt;
 
   // BlueZ uses int16_t because there is no int8_t for DBus, so we should never
   // get an int16_t that cannot be represented by an int8_t. But just in case
@@ -1131,7 +1131,7 @@
   SetTrusted();
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
-  std::move(callback).Run(/*error_code=*/absl::nullopt);
+  std::move(callback).Run(/*error_code=*/std::nullopt);
 }
 
 void BluetoothDeviceBlueZ::OnConnectError(ConnectCallback callback,
@@ -1200,7 +1200,7 @@
 void BluetoothDeviceBlueZ::OnPair(ConnectCallback callback) {
   BLUETOOTH_LOG(EVENT) << object_path_.value() << ": Paired";
   EndPairing();
-  std::move(callback).Run(/*error_code=*/absl::nullopt);
+  std::move(callback).Run(/*error_code=*/std::nullopt);
 }
 
 void BluetoothDeviceBlueZ::OnPairError(ConnectCallback callback,
diff --git a/device/bluetooth/bluez/bluetooth_device_bluez.h b/device/bluetooth/bluez/bluetooth_device_bluez.h
index bc0d4272..b707957 100644
--- a/device/bluetooth/bluez/bluetooth_device_bluez.h
+++ b/device/bluetooth/bluez/bluetooth_device_bluez.h
@@ -64,7 +64,7 @@
   uint16_t GetProductID() const override;
   uint16_t GetDeviceID() const override;
   uint16_t GetAppearance() const override;
-  absl::optional<std::string> GetName() const override;
+  std::optional<std::string> GetName() const override;
   bool IsPaired() const override;
 #if BUILDFLAG(IS_CHROMEOS)
   bool IsBonded() const override;
@@ -74,8 +74,8 @@
   bool IsConnectable() const override;
   bool IsConnecting() const override;
   UUIDSet GetUUIDs() const override;
-  absl::optional<int8_t> GetInquiryRSSI() const override;
-  absl::optional<int8_t> GetInquiryTxPower() const override;
+  std::optional<int8_t> GetInquiryRSSI() const override;
+  std::optional<int8_t> GetInquiryTxPower() const override;
   bool ExpectingPinCode() const override;
   bool ExpectingPasskey() const override;
   bool ExpectingConfirmation() const override;
@@ -193,7 +193,7 @@
  protected:
   // BluetoothDevice override
   void CreateGattConnectionImpl(
-      absl::optional<device::BluetoothUUID> service_uuid) override;
+      std::optional<device::BluetoothUUID> service_uuid) override;
   void DisconnectGatt() override;
 
  private:
diff --git a/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc b/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc
index f3f9bbed..45807d6 100644
--- a/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc
+++ b/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc
@@ -272,7 +272,7 @@
   void SuccessCallback() { ++success_callback_count_; }
 
   void ValueCallback(
-      absl::optional<BluetoothGattService::GattErrorCode> error_code,
+      std::optional<BluetoothGattService::GattErrorCode> error_code,
       const std::vector<uint8_t>& value) {
     if (error_code.has_value()) {
       ++error_callback_count_;
@@ -285,7 +285,7 @@
 
   void GattConnectionCallback(
       std::unique_ptr<BluetoothGattConnection> conn,
-      absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+      std::optional<BluetoothDevice::ConnectErrorCode> error) {
     if (error.has_value()) {
       ++error_callback_count_;
       return;
@@ -1489,7 +1489,7 @@
                                      .value());
 
   characteristic->ReadRemoteCharacteristic(base::BindLambdaForTesting(
-      [&](absl::optional<BluetoothGattService::GattErrorCode> error_code,
+      [&](std::optional<BluetoothGattService::GattErrorCode> error_code,
           const std::vector<uint8_t>& data) {
         ValueCallback(error_code, data);
         EXPECT_EQ(1, success_callback_count_);
@@ -1648,7 +1648,7 @@
                                      .value());
 
   characteristic->ReadRemoteCharacteristic(base::BindLambdaForTesting(
-      [&](absl::optional<BluetoothGattService::GattErrorCode> error_code,
+      [&](std::optional<BluetoothGattService::GattErrorCode> error_code,
           const std::vector<uint8_t>& data) {
         ValueCallback(error_code, data);
         EXPECT_EQ(1, success_callback_count_);
@@ -1707,7 +1707,7 @@
                                      .value());
 
   characteristic->ReadRemoteCharacteristic(base::BindLambdaForTesting(
-      [&](absl::optional<BluetoothGattService::GattErrorCode> error_code,
+      [&](std::optional<BluetoothGattService::GattErrorCode> error_code,
           const std::vector<uint8_t>& data) {
         ValueCallback(error_code, data);
         EXPECT_EQ(1, success_callback_count_);
diff --git a/device/bluetooth/bluez/bluetooth_low_energy_scan_session_bluez.cc b/device/bluetooth/bluez/bluetooth_low_energy_scan_session_bluez.cc
index 48032bf6..732bb0f 100644
--- a/device/bluetooth/bluez/bluetooth_low_energy_scan_session_bluez.cc
+++ b/device/bluetooth/bluez/bluetooth_low_energy_scan_session_bluez.cc
@@ -4,6 +4,8 @@
 
 #include "device/bluetooth/bluez/bluetooth_low_energy_scan_session_bluez.h"
 
+#include <optional>
+
 #include "base/debug/dump_without_crashing.h"
 #include "base/functional/callback.h"
 #include "base/logging.h"
@@ -13,7 +15,6 @@
 #include "device/bluetooth/dbus/bluetooth_device_client.h"
 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
 #include "device/bluetooth/public/cpp/bluetooth_address.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace bluez {
 
@@ -37,7 +38,7 @@
     return;
   }
 
-  delegate_->OnSessionStarted(this, /*error_code=*/absl::nullopt);
+  delegate_->OnSessionStarted(this, /*error_code=*/std::nullopt);
 }
 
 void BluetoothLowEnergyScanSessionBlueZ::OnRelease() {
diff --git a/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.cc b/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.cc
index 6d0c945..4c0d2fb 100644
--- a/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.cc
+++ b/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.cc
@@ -411,7 +411,7 @@
   --num_of_characteristic_value_read_in_progress_;
   DCHECK_GE(num_of_characteristic_value_read_in_progress_, 0);
   std::move(callback).Run(
-      absl::make_optional(
+      std::make_optional(
           BluetoothGattServiceBlueZ::DBusErrorToServiceError(error_name)),
       /*value=*/std::vector<uint8_t>());
 }
diff --git a/device/bluetooth/bluez/bluetooth_service_attribute_value_bluez.cc b/device/bluetooth/bluez/bluetooth_service_attribute_value_bluez.cc
index 0555c266..0c8779e 100644
--- a/device/bluetooth/bluez/bluetooth_service_attribute_value_bluez.cc
+++ b/device/bluetooth/bluez/bluetooth_service_attribute_value_bluez.cc
@@ -4,22 +4,22 @@
 
 #include "device/bluetooth/bluez/bluetooth_service_attribute_value_bluez.h"
 
+#include <optional>
 #include <utility>
 
 #include "base/check_op.h"
 #include "base/memory/ptr_util.h"
 #include "base/values.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace bluez {
 
 BluetoothServiceAttributeValueBlueZ::BluetoothServiceAttributeValueBlueZ()
-    : type_(NULLTYPE), size_(0), value_(absl::in_place) {}
+    : type_(NULLTYPE), size_(0), value_(std::in_place) {}
 
 BluetoothServiceAttributeValueBlueZ::BluetoothServiceAttributeValueBlueZ(
     Type type,
     size_t size,
-    absl::optional<base::Value> value)
+    std::optional<base::Value> value)
     : type_(type), size_(size), value_(std::move(value)) {
   CHECK_NE(type, SEQUENCE);
 }
@@ -41,7 +41,7 @@
     type_ = attribute.type_;
     size_ = attribute.size_;
     if (attribute.type_ == SEQUENCE) {
-      value_ = absl::nullopt;
+      value_ = std::nullopt;
       sequence_ = std::make_unique<Sequence>(*attribute.sequence_);
     } else {
       value_ = attribute.value_->Clone();
diff --git a/device/bluetooth/bluez/bluetooth_service_attribute_value_bluez.h b/device/bluetooth/bluez/bluetooth_service_attribute_value_bluez.h
index d57d176..5367b2e3 100644
--- a/device/bluetooth/bluez/bluetooth_service_attribute_value_bluez.h
+++ b/device/bluetooth/bluez/bluetooth_service_attribute_value_bluez.h
@@ -7,11 +7,11 @@
 
 #include <cstddef>
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "base/values.h"
 #include "device/bluetooth/bluetooth_export.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace bluez {
 
@@ -34,7 +34,7 @@
   BluetoothServiceAttributeValueBlueZ();
   BluetoothServiceAttributeValueBlueZ(Type type,
                                       size_t size,
-                                      absl::optional<base::Value> value);
+                                      std::optional<base::Value> value);
   explicit BluetoothServiceAttributeValueBlueZ(
       std::unique_ptr<Sequence> sequence);
   BluetoothServiceAttributeValueBlueZ(
@@ -52,7 +52,7 @@
  private:
   Type type_;
   size_t size_;
-  absl::optional<base::Value> value_;
+  std::optional<base::Value> value_;
   std::unique_ptr<Sequence> sequence_;
 };
 
diff --git a/device/bluetooth/bluez/bluetooth_service_record_bluez_unittest.cc b/device/bluetooth/bluez/bluetooth_service_record_bluez_unittest.cc
index 1a72da40..eea1ff4 100644
--- a/device/bluetooth/bluez/bluetooth_service_record_bluez_unittest.cc
+++ b/device/bluetooth/bluez/bluetooth_service_record_bluez_unittest.cc
@@ -5,6 +5,7 @@
 #include "device/bluetooth/bluez/bluetooth_service_record_bluez.h"
 
 #include <memory>
+#include <optional>
 #include <string>
 
 #include "base/functional/bind.h"
@@ -21,7 +22,6 @@
 #include "device/bluetooth/dbus/fake_bluetooth_device_client.h"
 #include "device/bluetooth/test/bluetooth_test_bluez.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace bluez {
 
@@ -194,7 +194,7 @@
   device->Connect(
       nullptr,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<device::BluetoothDevice::ConnectErrorCode>
+          [&run_loop](std::optional<device::BluetoothDevice::ConnectErrorCode>
                           error_code) {
             EXPECT_FALSE(error_code.has_value());
             run_loop.Quit();
diff --git a/device/bluetooth/bluez/metrics_recorder.cc b/device/bluetooth/bluez/metrics_recorder.cc
index a8c3560..3d3b5d9 100644
--- a/device/bluetooth/bluez/metrics_recorder.cc
+++ b/device/bluetooth/bluez/metrics_recorder.cc
@@ -52,7 +52,7 @@
 
 }  // namespace
 
-absl::optional<ConnectToServiceInsecurelyResult> ExtractResultFromErrorString(
+std::optional<ConnectToServiceInsecurelyResult> ExtractResultFromErrorString(
     const std::string& error_string) {
   if (base::Contains(error_string, kBlueZInvalidArgumentsError))
     return ConnectToServiceInsecurelyResult::kInvalidArgumentsError;
@@ -96,7 +96,7 @@
   if (base::Contains(error_string, kBlueZFailedError))
     return ConnectToServiceInsecurelyResult::kFailedError;
 
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 ConnectToServiceFailureReason ExtractFailureReasonFromErrorString(
diff --git a/device/bluetooth/bluez/metrics_recorder.h b/device/bluetooth/bluez/metrics_recorder.h
index be8890c..2d162e9 100644
--- a/device/bluetooth/bluez/metrics_recorder.h
+++ b/device/bluetooth/bluez/metrics_recorder.h
@@ -5,10 +5,9 @@
 #ifndef DEVICE_BLUETOOTH_BLUEZ_METRICS_RECORDER_H_
 #define DEVICE_BLUETOOTH_BLUEZ_METRICS_RECORDER_H_
 
+#include <optional>
 #include <string>
 
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
 namespace bluetooth {
 
 // Result types for ConnectToServiceInsecurely(). Numerical values are used for
@@ -63,7 +62,7 @@
 
 // Returns the ConnectToServiceInsecurelyResult type associated with
 // |error_string|, or null if no result could be found.
-absl::optional<ConnectToServiceInsecurelyResult> ExtractResultFromErrorString(
+std::optional<ConnectToServiceInsecurelyResult> ExtractResultFromErrorString(
     const std::string& error_string);
 
 // Returns the ConnectToServiceFailureReason type associated with
diff --git a/device/bluetooth/cast/bluetooth_adapter_cast.cc b/device/bluetooth/cast/bluetooth_adapter_cast.cc
index b1f6046..6ca7ee89 100644
--- a/device/bluetooth/cast/bluetooth_adapter_cast.cc
+++ b/device/bluetooth/cast/bluetooth_adapter_cast.cc
@@ -176,7 +176,7 @@
 
 void BluetoothAdapterCast::ConnectDevice(
     const std::string& address,
-    const absl::optional<BluetoothDevice::AddressType>& address_type,
+    const std::optional<BluetoothDevice::AddressType>& address_type,
     ConnectDeviceCallback callback,
     ConnectDeviceErrorCallback error_callback) {
   NOTIMPLEMENTED() << __func__ << " GATT server mode not supported";
diff --git a/device/bluetooth/cast/bluetooth_adapter_cast.h b/device/bluetooth/cast/bluetooth_adapter_cast.h
index aa1896e0..1dbfdb0e 100644
--- a/device/bluetooth/cast/bluetooth_adapter_cast.h
+++ b/device/bluetooth/cast/bluetooth_adapter_cast.h
@@ -90,7 +90,7 @@
                         AdvertisementErrorCallback error_callback) override;
   void ConnectDevice(
       const std::string& address,
-      const absl::optional<BluetoothDevice::AddressType>& address_type,
+      const std::optional<BluetoothDevice::AddressType>& address_type,
       ConnectDeviceCallback callback,
       ConnectDeviceErrorCallback error_callback) override;
   BluetoothLocalGattService* GetGattService(
diff --git a/device/bluetooth/cast/bluetooth_device_cast.cc b/device/bluetooth/cast/bluetooth_device_cast.cc
index 75254a15..df642a39a 100644
--- a/device/bluetooth/cast/bluetooth_device_cast.cc
+++ b/device/bluetooth/cast/bluetooth_device_cast.cc
@@ -108,7 +108,7 @@
   return 0;
 }
 
-absl::optional<std::string> BluetoothDeviceCast::GetName() const {
+std::optional<std::string> BluetoothDeviceCast::GetName() const {
   return name_;
 }
 
@@ -133,12 +133,12 @@
   return pending_connect_;
 }
 
-absl::optional<int8_t> BluetoothDeviceCast::GetInquiryRSSI() const {
+std::optional<int8_t> BluetoothDeviceCast::GetInquiryRSSI() const {
   // TODO(slan): Plumb this from the type_to_data field of ScanResult.
   return BluetoothDevice::GetInquiryRSSI();
 }
 
-absl::optional<int8_t> BluetoothDeviceCast::GetInquiryTxPower() const {
+std::optional<int8_t> BluetoothDeviceCast::GetInquiryTxPower() const {
   // TODO(slan): Remove if we do not need this.
   return BluetoothDevice::GetInquiryTxPower();
 }
@@ -242,7 +242,7 @@
   DVLOG(3) << __func__;
   bool changed = false;
 
-  absl::optional<std::string> result_name = result.Name();
+  std::optional<std::string> result_name = result.Name();
 
   // Advertisements for the same device can use different names. For now, the
   // last name wins. An empty string represents no name.
@@ -293,7 +293,7 @@
   // Update state in the base class. This will cause pending callbacks to be
   // fired.
   if (!was_connected && connected) {
-    DidConnectGatt(/*error_code=*/absl::nullopt);
+    DidConnectGatt(/*error_code=*/std::nullopt);
     remote_device_->GetServices(base::BindOnce(
         &BluetoothDeviceCast::OnGetServices, weak_factory_.GetWeakPtr()));
   } else if (was_connected && !connected) {
@@ -345,7 +345,7 @@
 }
 
 void BluetoothDeviceCast::CreateGattConnectionImpl(
-    absl::optional<BluetoothUUID> service_uuid) {
+    std::optional<BluetoothUUID> service_uuid) {
   DVLOG(2) << __func__ << " " << pending_connect_;
   if (pending_connect_)
     return;
diff --git a/device/bluetooth/cast/bluetooth_device_cast.h b/device/bluetooth/cast/bluetooth_device_cast.h
index e1edbb6..ad42f8f3 100644
--- a/device/bluetooth/cast/bluetooth_device_cast.h
+++ b/device/bluetooth/cast/bluetooth_device_cast.h
@@ -9,6 +9,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -23,7 +24,6 @@
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/bluetooth_remote_gatt_service.h"
 #include "device/bluetooth/public/cpp/bluetooth_uuid.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -48,14 +48,14 @@
   uint16_t GetProductID() const override;
   uint16_t GetDeviceID() const override;
   uint16_t GetAppearance() const override;
-  absl::optional<std::string> GetName() const override;
+  std::optional<std::string> GetName() const override;
   bool IsPaired() const override;
   bool IsConnected() const override;
   bool IsGattConnected() const override;
   bool IsConnectable() const override;
   bool IsConnecting() const override;
-  absl::optional<int8_t> GetInquiryRSSI() const override;
-  absl::optional<int8_t> GetInquiryTxPower() const override;
+  std::optional<int8_t> GetInquiryRSSI() const override;
+  std::optional<int8_t> GetInquiryTxPower() const override;
   bool ExpectingPinCode() const override;
   bool ExpectingPasskey() const override;
   bool ExpectingConfirmation() const override;
@@ -114,7 +114,7 @@
   // DidDisconnectGatt immediately or asynchronously as the connection state
   // changes.
   void CreateGattConnectionImpl(
-      absl::optional<BluetoothUUID> service_uuid) override;
+      std::optional<BluetoothUUID> service_uuid) override;
 
   // Disconnects GATT connection on platforms that maintain a specific GATT
   // connection.
@@ -133,7 +133,7 @@
 
   const scoped_refptr<chromecast::bluetooth::RemoteDevice> remote_device_;
   const std::string address_;
-  absl::optional<std::string> name_;
+  std::optional<std::string> name_;
 
   base::WeakPtrFactory<BluetoothDeviceCast> weak_factory_;
 };
diff --git a/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.cc b/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.cc
index 5d37149..6e6bb325 100644
--- a/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.cc
+++ b/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.cc
@@ -213,7 +213,7 @@
     const std::vector<uint8_t>& result) {
   if (success) {
     value_ = result;
-    std::move(callback).Run(/*error_code=*/absl::nullopt, result);
+    std::move(callback).Run(/*error_code=*/std::nullopt, result);
     return;
   }
   std::move(callback).Run(BluetoothGattService::GattErrorCode::kFailed,
diff --git a/device/bluetooth/cast/bluetooth_remote_gatt_descriptor_cast.cc b/device/bluetooth/cast/bluetooth_remote_gatt_descriptor_cast.cc
index 9372410..856a45e 100644
--- a/device/bluetooth/cast/bluetooth_remote_gatt_descriptor_cast.cc
+++ b/device/bluetooth/cast/bluetooth_remote_gatt_descriptor_cast.cc
@@ -74,7 +74,7 @@
     const std::vector<uint8_t>& result) {
   if (success) {
     value_ = result;
-    std::move(callback).Run(/*error_code=*/absl::nullopt, result);
+    std::move(callback).Run(/*error_code=*/std::nullopt, result);
     return;
   }
   std::move(callback).Run(BluetoothGattService::GattErrorCode::kFailed,
diff --git a/device/bluetooth/chromeos/bluetooth_connection_logger.h b/device/bluetooth/chromeos/bluetooth_connection_logger.h
index 14d0b69..58c878f 100644
--- a/device/bluetooth/chromeos/bluetooth_connection_logger.h
+++ b/device/bluetooth/chromeos/bluetooth_connection_logger.h
@@ -5,9 +5,10 @@
 #ifndef DEVICE_BLUETOOTH_CHROMEOS_BLUETOOTH_CONNECTION_LOGGER_H_
 #define DEVICE_BLUETOOTH_CHROMEOS_BLUETOOTH_CONNECTION_LOGGER_H_
 
+#include <optional>
+
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_export.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
diff --git a/device/bluetooth/chromeos/bluetooth_utils.cc b/device/bluetooth/chromeos/bluetooth_utils.cc
index 7027f96..4ff25ba 100644
--- a/device/bluetooth/chromeos/bluetooth_utils.cc
+++ b/device/bluetooth/chromeos/bluetooth_utils.cc
@@ -4,6 +4,8 @@
 
 #include "device/bluetooth/chromeos/bluetooth_utils.h"
 
+#include <optional>
+
 #include "base/containers/contains.h"
 #include "base/containers/fixed_flat_set.h"
 #include "base/feature_list.h"
@@ -17,7 +19,6 @@
 #include "build/chromeos_buildflags.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "device/base/features.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include <string_view>
@@ -345,7 +346,7 @@
   return true;
 }
 
-void RecordPairingResult(absl::optional<ConnectionFailureReason> failure_reason,
+void RecordPairingResult(std::optional<ConnectionFailureReason> failure_reason,
                          BluetoothTransport transport,
                          base::TimeDelta duration) {
   RecordPairingTransport(transport);
@@ -383,7 +384,7 @@
 }
 
 void RecordUserInitiatedReconnectionAttemptResult(
-    absl::optional<ConnectionFailureReason> failure_reason,
+    std::optional<ConnectionFailureReason> failure_reason,
     UserInitiatedReconnectionUISurfaces surface) {
   bool success = !failure_reason.has_value();
   std::string base_histogram_name =
@@ -489,7 +490,7 @@
 }
 
 void RecordUserInitiatedReconnectionAttemptDuration(
-    absl::optional<ConnectionFailureReason> failure_reason,
+    std::optional<ConnectionFailureReason> failure_reason,
     BluetoothTransport transport,
     base::TimeDelta duration) {
   bool success = !failure_reason.has_value();
diff --git a/device/bluetooth/chromeos/bluetooth_utils.h b/device/bluetooth/chromeos/bluetooth_utils.h
index e83127a..7aacf74 100644
--- a/device/bluetooth/chromeos/bluetooth_utils.h
+++ b/device/bluetooth/chromeos/bluetooth_utils.h
@@ -5,10 +5,11 @@
 #ifndef DEVICE_BLUETOOTH_CHROMEOS_BLUETOOTH_UTILS_H_
 #define DEVICE_BLUETOOTH_CHROMEOS_BLUETOOTH_UTILS_H_
 
+#include <optional>
+
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/bluetooth_export.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
 class TimeDelta;
@@ -126,13 +127,13 @@
 
 // Record outcome of user attempting to pair to a device.
 DEVICE_BLUETOOTH_EXPORT void RecordPairingResult(
-    absl::optional<ConnectionFailureReason> failure_reason,
+    std::optional<ConnectionFailureReason> failure_reason,
     BluetoothTransport transport,
     base::TimeDelta duration);
 
 // Record outcome of user attempting to reconnect to a previously paired device.
 DEVICE_BLUETOOTH_EXPORT void RecordUserInitiatedReconnectionAttemptResult(
-    absl::optional<ConnectionFailureReason> failure_reason,
+    std::optional<ConnectionFailureReason> failure_reason,
     UserInitiatedReconnectionUISurfaces surface);
 
 // Record how long it took for a user to find and select the device they wished
@@ -170,7 +171,7 @@
 // Record how long it took for an attempted user initiated bluetooth device
 // reconnection to occur.
 DEVICE_BLUETOOTH_EXPORT void RecordUserInitiatedReconnectionAttemptDuration(
-    absl::optional<ConnectionFailureReason> failure_reason,
+    std::optional<ConnectionFailureReason> failure_reason,
     BluetoothTransport transport,
     base::TimeDelta duration);
 
diff --git a/device/bluetooth/chromeos/bluetooth_utils_unittest.cc b/device/bluetooth/chromeos/bluetooth_utils_unittest.cc
index 5e72db47..713dec45 100644
--- a/device/bluetooth/chromeos/bluetooth_utils_unittest.cc
+++ b/device/bluetooth/chromeos/bluetooth_utils_unittest.cc
@@ -86,7 +86,7 @@
     MockBluetoothDevice* mock_bluetooth_device =
         AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_CLASSIC);
     ON_CALL(*mock_bluetooth_device, GetName)
-        .WillByDefault(testing::Return(absl::nullopt));
+        .WillByDefault(testing::Return(std::nullopt));
     ON_CALL(*mock_bluetooth_device, GetDeviceType)
         .WillByDefault(testing::Return(BluetoothDeviceType::UNKNOWN));
     ON_CALL(*mock_bluetooth_device, GetAddress)
@@ -213,7 +213,7 @@
   auto* mock_bluetooth_device =
       AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_CLASSIC);
   EXPECT_CALL(*mock_bluetooth_device, GetName)
-      .WillOnce(testing::Return(absl::nullopt));
+      .WillOnce(testing::Return(std::nullopt));
 
   VerifyFilterBluetoothDeviceList(BluetoothFilterType::KNOWN,
                                   0u /* num_expected_remaining_devices */);
@@ -238,7 +238,7 @@
   mock_bluetooth_device_1->AddUUID(device::BluetoothUUID(kHIDServiceUUID));
   mock_bluetooth_device_1->UpdateAdvertisementData(
       1 /* rssi */, 0 /* flags */, BluetoothDevice::UUIDList(),
-      absl::nullopt /* tx_power */, kTestServiceDataMap,
+      std::nullopt /* tx_power */, kTestServiceDataMap,
       BluetoothDevice::ManufacturerDataMap());
 
   auto* mock_bluetooth_device_2 =
@@ -246,7 +246,7 @@
   mock_bluetooth_device_2->AddUUID(device::BluetoothUUID(kHIDServiceUUID));
   mock_bluetooth_device_2->UpdateAdvertisementData(
       1 /* rssi */, kLimitedDiscoveryFlag /* flags */,
-      BluetoothDevice::UUIDList(), absl::nullopt /* tx_power */,
+      BluetoothDevice::UUIDList(), std::nullopt /* tx_power */,
       kTestServiceDataMap, BluetoothDevice::ManufacturerDataMap());
 
   auto* mock_bluetooth_device_3 =
@@ -254,7 +254,7 @@
   mock_bluetooth_device_3->AddUUID(device::BluetoothUUID(kHIDServiceUUID));
   mock_bluetooth_device_3->UpdateAdvertisementData(
       1 /* rssi */, kGeneralDiscoveryFlag /* flags */,
-      BluetoothDevice::UUIDList(), absl::nullopt /* tx_power */,
+      BluetoothDevice::UUIDList(), std::nullopt /* tx_power */,
       kTestServiceDataMap, BluetoothDevice::ManufacturerDataMap());
 
   auto* mock_bluetooth_device_4 =
@@ -262,7 +262,7 @@
   mock_bluetooth_device_4->AddUUID(device::BluetoothUUID(kHIDServiceUUID));
   mock_bluetooth_device_4->UpdateAdvertisementData(
       1 /* rssi */, kLimitedDiscoveryFlag | kGeneralDiscoveryFlag /* flags */,
-      BluetoothDevice::UUIDList(), absl::nullopt /* tx_power */,
+      BluetoothDevice::UUIDList(), std::nullopt /* tx_power */,
       kTestServiceDataMap, BluetoothDevice::ManufacturerDataMap());
 
   VerifyFilterBluetoothDeviceList(BluetoothFilterType::KNOWN,
@@ -371,7 +371,7 @@
 
   // Test RemoveClassicDevicesWithoutNames
   EXPECT_CALL(*mock_bluetooth_device, GetName)
-      .WillRepeatedly(testing::Return(absl::nullopt));
+      .WillRepeatedly(testing::Return(std::nullopt));
   VerifyFilterBluetoothDeviceList(BluetoothFilterType::KNOWN,
                                   0u /* num_expected_remaining_devices */);
 
diff --git a/device/bluetooth/dbus/bluetooth_adapter_client.cc b/device/bluetooth/dbus/bluetooth_adapter_client.cc
index f811ae7..9028e31 100644
--- a/device/bluetooth/dbus/bluetooth_adapter_client.cc
+++ b/device/bluetooth/dbus/bluetooth_adapter_client.cc
@@ -121,7 +121,7 @@
 void OnResponseAdapter(
     base::OnceClosure callback,
     BluetoothAdapterClient::ErrorCallback error_callback,
-    const absl::optional<BluetoothAdapterClient::Error>& error) {
+    const std::optional<BluetoothAdapterClient::Error>& error) {
   if (!error) {
     std::move(callback).Run();
     return;
@@ -457,7 +457,7 @@
   // BluetoothAdapterClient override.
   void ConnectDevice(const dbus::ObjectPath& object_path,
                      const std::string& address,
-                     const absl::optional<AddressType>& address_type,
+                     const std::optional<AddressType>& address_type,
                      ConnectDeviceCallback callback,
                      ErrorCallback error_callback) override {
     dbus::MethodCall method_call(bluetooth_adapter::kBluetoothAdapterInterface,
@@ -563,7 +563,7 @@
                   dbus::Response* response,
                   dbus::ErrorResponse* error_response) {
     if (response) {
-      std::move(callback).Run(absl::nullopt);
+      std::move(callback).Run(std::nullopt);
       return;
     }
 
diff --git a/device/bluetooth/dbus/bluetooth_adapter_client.h b/device/bluetooth/dbus/bluetooth_adapter_client.h
index 7c31a986f..4f886418 100644
--- a/device/bluetooth/dbus/bluetooth_adapter_client.h
+++ b/device/bluetooth/dbus/bluetooth_adapter_client.h
@@ -7,6 +7,7 @@
 
 #include <cstdint>
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -15,7 +16,6 @@
 #include "dbus/property.h"
 #include "device/bluetooth/bluetooth_export.h"
 #include "device/bluetooth/dbus/bluez_dbus_client.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace dbus {
 class ObjectProxy;
@@ -171,7 +171,7 @@
   // Callback used by adapter methods to indicate that a response was
   // received with an optional Error in case an error occurred.
   using ResponseCallback =
-      base::OnceCallback<void(const absl::optional<Error>&)>;
+      base::OnceCallback<void(const std::optional<Error>&)>;
 
   // Starts a device discovery on the adapter with object path |object_path|.
   virtual void StartDiscovery(const dbus::ObjectPath& object_path,
@@ -228,7 +228,7 @@
   // |address_type| will create a BR/EDR device.
   virtual void ConnectDevice(const dbus::ObjectPath& object_path,
                              const std::string& address,
-                             const absl::optional<AddressType>& address_type,
+                             const std::optional<AddressType>& address_type,
                              ConnectDeviceCallback callback,
                              ErrorCallback error_callback) = 0;
 
diff --git a/device/bluetooth/dbus/bluetooth_advertisement_monitor_application_service_provider_unittest.cc b/device/bluetooth/dbus/bluetooth_advertisement_monitor_application_service_provider_unittest.cc
index 7648fe4..4f2447e 100644
--- a/device/bluetooth/dbus/bluetooth_advertisement_monitor_application_service_provider_unittest.cc
+++ b/device/bluetooth/dbus/bluetooth_advertisement_monitor_application_service_provider_unittest.cc
@@ -4,6 +4,7 @@
 
 #include <cstdint>
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "base/functional/callback_helpers.h"
@@ -20,7 +21,6 @@
 #include "device/bluetooth/dbus/bluetooth_advertisement_monitor_service_provider_impl.h"
 #include "device/bluetooth/dbus/fake_bluetooth_advertisement_monitor_service_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
 namespace bluez {
@@ -164,7 +164,7 @@
   // BluetoothLowEnergyScanSession::Delegate
   void OnSessionStarted(
       device::BluetoothLowEnergyScanSession* scan_session,
-      absl::optional<device::BluetoothLowEnergyScanSession::ErrorCode>
+      std::optional<device::BluetoothLowEnergyScanSession::ErrorCode>
           error_code) override {}
   void OnDeviceFound(device::BluetoothLowEnergyScanSession* scan_session,
                      device::BluetoothDevice* device) override {}
@@ -260,7 +260,7 @@
       std::move(pattern_value));
   auto filter = device::BluetoothLowEnergyScanFilter::Create(
       device::BluetoothLowEnergyScanFilter::Range::kNear, kDeviceFoundTimeout,
-      kDeviceLostTimeout, {pattern}, /*rssi_sampling_period=*/absl::nullopt);
+      kDeviceLostTimeout, {pattern}, /*rssi_sampling_period=*/std::nullopt);
 
   SetupExpectedMockAdvertisementMonitorDbusCalls(
       mock_bus.get(), mock_exported_object.get(), monitor_object_path);
diff --git a/device/bluetooth/dbus/bluetooth_device_client.cc b/device/bluetooth/dbus/bluetooth_device_client.cc
index 16a1be0b..24a95a9f 100644
--- a/device/bluetooth/dbus/bluetooth_device_client.cc
+++ b/device/bluetooth/dbus/bluetooth_device_client.cc
@@ -48,7 +48,7 @@
   if (!struct_reader->PopUint32(&size))
     return nullptr;
 
-  absl::optional<base::Value> value;
+  std::optional<base::Value> value;
   switch (type) {
     case bluez::BluetoothServiceAttributeValueBlueZ::NULLTYPE: {
       break;
diff --git a/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc b/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc
index b6d3c54..643001a 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc
+++ b/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc
@@ -313,7 +313,7 @@
     if (bytes)
       value.assign(bytes, bytes + length);
 
-    std::move(callback).Run(/*error_code=*/absl::nullopt, value);
+    std::move(callback).Run(/*error_code=*/std::nullopt, value);
   }
 
   // Called when a response for a failed method call is received.
diff --git a/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.h b/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.h
index edae7fa..1bdc032 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.h
+++ b/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include <optional>
 #include <string>
 #include <string_view>
 #include <vector>
@@ -17,7 +18,6 @@
 #include "device/bluetooth/bluetooth_export.h"
 #include "device/bluetooth/bluetooth_gatt_characteristic.h"
 #include "device/bluetooth/dbus/bluez_dbus_client.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace bluez {
 
@@ -81,7 +81,7 @@
       base::OnceCallback<void(const std::string& error_name,
                               const std::string& error_message)>;
   using ValueCallback = base::OnceCallback<void(
-      absl::optional<device::BluetoothGattService::GattErrorCode> error_code,
+      std::optional<device::BluetoothGattService::GattErrorCode> error_code,
       const std::vector<uint8_t>& value)>;
 
   BluetoothGattCharacteristicClient(const BluetoothGattCharacteristicClient&) =
diff --git a/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.cc b/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.cc
index 0153149..a14b43ee 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.cc
+++ b/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.cc
@@ -496,7 +496,7 @@
 void BluetoothGattCharacteristicServiceProviderImpl::OnReadValue(
     dbus::MethodCall* method_call,
     dbus::ExportedObject::ResponseSender response_sender,
-    absl::optional<device::BluetoothGattService::GattErrorCode> error_code,
+    std::optional<device::BluetoothGattService::GattErrorCode> error_code,
     const std::vector<uint8_t>& value) {
   if (error_code.has_value()) {
     DVLOG(2) << "Failed to read characteristic value. Report error.";
diff --git a/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.h b/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.h
index a23b9b9..c05b3cd 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.h
+++ b/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.h
@@ -117,7 +117,7 @@
   void OnReadValue(
       dbus::MethodCall* method_call,
       dbus::ExportedObject::ResponseSender response_sender,
-      absl::optional<device::BluetoothGattService::GattErrorCode> error_code,
+      std::optional<device::BluetoothGattService::GattErrorCode> error_code,
       const std::vector<uint8_t>& value);
 
   // Called by the Delegate in response to a method to call to write the value
diff --git a/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_unittest.cc b/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_unittest.cc
index 95ff888..61a1885e 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_unittest.cc
+++ b/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_unittest.cc
@@ -4,6 +4,7 @@
 
 #include <cstdint>
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -16,7 +17,6 @@
 #include "device/bluetooth/dbus/bluetooth_gatt_characteristic_delegate_wrapper.h"
 #include "device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace bluez {
 
@@ -49,7 +49,7 @@
         EXPECT_EQ(length, read_value.size());
         callback_called = true;
       }),
-      /*error_code=*/absl::nullopt, read_value);
+      /*error_code=*/std::nullopt, read_value);
 
   EXPECT_TRUE(callback_called);
 }
diff --git a/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.cc b/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.cc
index d79c34a..14d342d 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.cc
+++ b/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.cc
@@ -224,7 +224,7 @@
     if (bytes)
       value.assign(bytes, bytes + length);
 
-    std::move(callback).Run(/*error_code=*/absl::nullopt, value);
+    std::move(callback).Run(/*error_code=*/std::nullopt, value);
   }
 
   // Called when a response for a failed method call is received.
diff --git a/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.h b/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.h
index 07b3a09..44475f34 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.h
+++ b/device/bluetooth/dbus/bluetooth_gatt_descriptor_client.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -16,7 +17,6 @@
 #include "device/bluetooth/bluetooth_export.h"
 #include "device/bluetooth/bluetooth_gatt_service.h"
 #include "device/bluetooth/dbus/bluez_dbus_client.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace bluez {
 
@@ -70,7 +70,7 @@
       base::OnceCallback<void(const std::string& error_name,
                               const std::string& error_message)>;
   using ValueCallback = base::OnceCallback<void(
-      absl::optional<device::BluetoothGattService::GattErrorCode> error_code,
+      std::optional<device::BluetoothGattService::GattErrorCode> error_code,
       const std::vector<uint8_t>& value)>;
 
   BluetoothGattDescriptorClient(const BluetoothGattDescriptorClient&) = delete;
diff --git a/device/bluetooth/dbus/bluetooth_gatt_descriptor_service_provider_impl.cc b/device/bluetooth/dbus/bluetooth_gatt_descriptor_service_provider_impl.cc
index cc2f149..62300b9 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_descriptor_service_provider_impl.cc
+++ b/device/bluetooth/dbus/bluetooth_gatt_descriptor_service_provider_impl.cc
@@ -342,7 +342,7 @@
 void BluetoothGattDescriptorServiceProviderImpl::OnReadValue(
     dbus::MethodCall* method_call,
     dbus::ExportedObject::ResponseSender response_sender,
-    absl::optional<device::BluetoothGattService::GattErrorCode> error_code,
+    std::optional<device::BluetoothGattService::GattErrorCode> error_code,
     const std::vector<uint8_t>& value) {
   if (error_code.has_value()) {
     DVLOG(2) << "Failed to get descriptor value. Report error.";
diff --git a/device/bluetooth/dbus/bluetooth_gatt_descriptor_service_provider_impl.h b/device/bluetooth/dbus/bluetooth_gatt_descriptor_service_provider_impl.h
index 349789a..434894b 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_descriptor_service_provider_impl.h
+++ b/device/bluetooth/dbus/bluetooth_gatt_descriptor_service_provider_impl.h
@@ -87,7 +87,7 @@
   void OnReadValue(
       dbus::MethodCall* method_call,
       dbus::ExportedObject::ResponseSender response_sender,
-      absl::optional<device::BluetoothGattService::GattErrorCode> error_code,
+      std::optional<device::BluetoothGattService::GattErrorCode> error_code,
       const std::vector<uint8_t>& value);
 
   // Called by the Delegate in response to a method to call to write the value
diff --git a/device/bluetooth/dbus/bluetooth_le_advertisement_service_provider.cc b/device/bluetooth/dbus/bluetooth_le_advertisement_service_provider.cc
index a23699d..3b153bc 100644
--- a/device/bluetooth/dbus/bluetooth_le_advertisement_service_provider.cc
+++ b/device/bluetooth/dbus/bluetooth_le_advertisement_service_provider.cc
@@ -34,11 +34,11 @@
       const dbus::ObjectPath& object_path,
       Delegate* delegate,
       AdvertisementType type,
-      absl::optional<UUIDList> service_uuids,
-      absl::optional<ManufacturerData> manufacturer_data,
-      absl::optional<UUIDList> solicit_uuids,
-      absl::optional<ServiceData> service_data,
-      absl::optional<ScanResponseData> scan_response_data)
+      std::optional<UUIDList> service_uuids,
+      std::optional<ManufacturerData> manufacturer_data,
+      std::optional<UUIDList> solicit_uuids,
+      std::optional<ServiceData> service_data,
+      std::optional<ScanResponseData> scan_response_data)
       : origin_thread_id_(base::PlatformThread::CurrentId()),
         bus_(bus),
         delegate_(delegate),
@@ -433,11 +433,11 @@
 
   // Advertisement data that needs to be provided to BlueZ when requested.
   AdvertisementType type_;
-  absl::optional<UUIDList> service_uuids_;
-  absl::optional<ManufacturerData> manufacturer_data_;
-  absl::optional<UUIDList> solicit_uuids_;
-  absl::optional<ServiceData> service_data_;
-  absl::optional<ScanResponseData> scan_response_data_;
+  std::optional<UUIDList> service_uuids_;
+  std::optional<ManufacturerData> manufacturer_data_;
+  std::optional<UUIDList> solicit_uuids_;
+  std::optional<ServiceData> service_data_;
+  std::optional<ScanResponseData> scan_response_data_;
 
   // D-Bus object we are exporting, owned by this object.
   scoped_refptr<dbus::ExportedObject> exported_object_;
@@ -463,11 +463,11 @@
     const dbus::ObjectPath& object_path,
     Delegate* delegate,
     AdvertisementType type,
-    absl::optional<UUIDList> service_uuids,
-    absl::optional<ManufacturerData> manufacturer_data,
-    absl::optional<UUIDList> solicit_uuids,
-    absl::optional<ServiceData> service_data,
-    absl::optional<ScanResponseData> scan_response_data) {
+    std::optional<UUIDList> service_uuids,
+    std::optional<ManufacturerData> manufacturer_data,
+    std::optional<UUIDList> solicit_uuids,
+    std::optional<ServiceData> service_data,
+    std::optional<ScanResponseData> scan_response_data) {
   if (!bluez::BluezDBusManager::Get()->IsUsingFakes()) {
     return std::make_unique<BluetoothAdvertisementServiceProviderImpl>(
         bus, object_path, delegate, type, std::move(service_uuids),
diff --git a/device/bluetooth/dbus/bluetooth_le_advertisement_service_provider.h b/device/bluetooth/dbus/bluetooth_le_advertisement_service_provider.h
index de6b194..e79b7ac6 100644
--- a/device/bluetooth/dbus/bluetooth_le_advertisement_service_provider.h
+++ b/device/bluetooth/dbus/bluetooth_le_advertisement_service_provider.h
@@ -9,6 +9,7 @@
 
 #include <map>
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -16,7 +17,6 @@
 #include "dbus/bus.h"
 #include "dbus/object_path.h"
 #include "device/bluetooth/bluetooth_export.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace bluez {
 
@@ -66,11 +66,11 @@
       const dbus::ObjectPath& object_path,
       Delegate* delegate,
       AdvertisementType type,
-      absl::optional<UUIDList> service_uuids,
-      absl::optional<ManufacturerData> manufacturer_data,
-      absl::optional<UUIDList> solicit_uuids,
-      absl::optional<ServiceData> service_data,
-      absl::optional<ScanResponseData> scan_response_data);
+      std::optional<UUIDList> service_uuids,
+      std::optional<ManufacturerData> manufacturer_data,
+      std::optional<UUIDList> solicit_uuids,
+      std::optional<ServiceData> service_data,
+      std::optional<ScanResponseData> scan_response_data);
 
  protected:
   BluetoothLEAdvertisementServiceProvider();
diff --git a/device/bluetooth/dbus/fake_bluetooth_adapter_client.cc b/device/bluetooth/dbus/fake_bluetooth_adapter_client.cc
index f997c17d..c1472cc 100644
--- a/device/bluetooth/dbus/fake_bluetooth_adapter_client.cc
+++ b/device/bluetooth/dbus/fake_bluetooth_adapter_client.cc
@@ -144,7 +144,7 @@
   ++discovering_count_;
   DVLOG(1) << "StartDiscovery: " << object_path.value() << ", "
            << "count is now " << discovering_count_;
-  PostDelayedTask(base::BindOnce(std::move(callback), absl::nullopt));
+  PostDelayedTask(base::BindOnce(std::move(callback), std::nullopt));
 
   if (discovering_count_ == 1) {
     PostDelayedTask(
@@ -178,7 +178,7 @@
   --discovering_count_;
   DVLOG(1) << "StopDiscovery: " << object_path.value() << ", "
            << "count is now " << discovering_count_;
-  PostDelayedTask(base::BindOnce(std::move(callback), absl::nullopt));
+  PostDelayedTask(base::BindOnce(std::move(callback), std::nullopt));
 
   if (discovering_count_ == 0) {
     FakeBluetoothDeviceClient* device_client =
@@ -288,7 +288,7 @@
 void FakeBluetoothAdapterClient::ConnectDevice(
     const dbus::ObjectPath& object_path,
     const std::string& address,
-    const absl::optional<AddressType>& address_type,
+    const std::optional<AddressType>& address_type,
     ConnectDeviceCallback callback,
     ErrorCallback error_callback) {
   NOTIMPLEMENTED();
diff --git a/device/bluetooth/dbus/fake_bluetooth_adapter_client.h b/device/bluetooth/dbus/fake_bluetooth_adapter_client.h
index 97db46e..96ee1fa 100644
--- a/device/bluetooth/dbus/fake_bluetooth_adapter_client.h
+++ b/device/bluetooth/dbus/fake_bluetooth_adapter_client.h
@@ -69,7 +69,7 @@
                            ErrorCallback error_callback) override;
   void ConnectDevice(const dbus::ObjectPath& object_path,
                      const std::string& address,
-                     const absl::optional<AddressType>& address_type,
+                     const std::optional<AddressType>& address_type,
                      ConnectDeviceCallback callback,
                      ErrorCallback error_callback) override;
 
diff --git a/device/bluetooth/dbus/fake_bluetooth_device_client.cc b/device/bluetooth/dbus/fake_bluetooth_device_client.cc
index d8dabc5..7888fa5b 100644
--- a/device/bluetooth/dbus/fake_bluetooth_device_client.cc
+++ b/device/bluetooth/dbus/fake_bluetooth_device_client.cc
@@ -1930,7 +1930,7 @@
 
 void FakeBluetoothDeviceClient::CreateTestDevice(
     const dbus::ObjectPath& adapter_path,
-    const absl::optional<std::string> name,
+    const std::optional<std::string> name,
     const std::string alias,
     const std::string device_address,
     const std::vector<std::string>& service_uuids,
diff --git a/device/bluetooth/dbus/fake_bluetooth_device_client.h b/device/bluetooth/dbus/fake_bluetooth_device_client.h
index c9e6f90e..8d1cb34 100644
--- a/device/bluetooth/dbus/fake_bluetooth_device_client.h
+++ b/device/bluetooth/dbus/fake_bluetooth_device_client.h
@@ -8,6 +8,7 @@
 #include <cstdint>
 #include <map>
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "base/functional/bind.h"
@@ -20,7 +21,6 @@
 #include "device/bluetooth/dbus/bluetooth_agent_service_provider.h"
 #include "device/bluetooth/dbus/bluetooth_device_client.h"
 #include "device/bluetooth/dbus/bluetooth_profile_service_provider.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace bluez {
 
@@ -179,7 +179,7 @@
   // Create a test Bluetooth device with the given properties.
   void CreateTestDevice(
       const dbus::ObjectPath& adapter_path,
-      const absl::optional<std::string> name,
+      const std::optional<std::string> name,
       const std::string alias,
       const std::string device_address,
       const std::vector<std::string>& service_uuids,
diff --git a/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.cc b/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.cc
index d7c6a4a..5e93026 100644
--- a/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.cc
+++ b/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.cc
@@ -572,7 +572,7 @@
   DCHECK(properties);
 
   properties->value.ReplaceValue(value);
-  std::move(callback).Run(/*error_code=*/absl::nullopt, value);
+  std::move(callback).Run(/*error_code=*/std::nullopt, value);
 }
 
 std::vector<uint8_t>
diff --git a/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_client.cc b/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_client.cc
index 351b91c..35930c2d 100644
--- a/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_client.cc
+++ b/device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_client.cc
@@ -122,7 +122,7 @@
     }
   }
 
-  std::move(callback).Run(/*error_code=*/absl::nullopt,
+  std::move(callback).Run(/*error_code=*/std::nullopt,
                           iter->second->properties->value.value());
 }
 
diff --git a/device/bluetooth/device.cc b/device/bluetooth/device.cc
index 20c7b0b6..e7776cf9 100644
--- a/device/bluetooth/device.cc
+++ b/device/bluetooth/device.cc
@@ -117,7 +117,7 @@
   device::BluetoothRemoteGattService* service =
       device->GetGattService(service_id);
   if (service == nullptr) {
-    std::move(callback).Run(absl::nullopt);
+    std::move(callback).Run(std::nullopt);
     return;
   }
 
@@ -148,7 +148,7 @@
       device->GetGattService(service_id);
   if (service == nullptr) {
     std::move(callback).Run(mojom::GattResult::SERVICE_NOT_FOUND,
-                            absl::nullopt /* value */);
+                            std::nullopt /* value */);
     return;
   }
 
@@ -156,7 +156,7 @@
       service->GetCharacteristic(characteristic_id);
   if (characteristic == nullptr) {
     std::move(callback).Run(mojom::GattResult::CHARACTERISTIC_NOT_FOUND,
-                            absl::nullopt /* value */);
+                            std::nullopt /* value */);
     return;
   }
 
@@ -203,21 +203,21 @@
                             GetDescriptorsCallback callback) {
   device::BluetoothDevice* device = adapter_->GetDevice(GetAddress());
   if (!device) {
-    std::move(callback).Run(absl::nullopt);
+    std::move(callback).Run(std::nullopt);
     return;
   }
 
   device::BluetoothRemoteGattService* service =
       device->GetGattService(service_id);
   if (!service) {
-    std::move(callback).Run(absl::nullopt);
+    std::move(callback).Run(std::nullopt);
     return;
   }
 
   device::BluetoothRemoteGattCharacteristic* characteristic =
       service->GetCharacteristic(characteristic_id);
   if (!characteristic) {
-    std::move(callback).Run(absl::nullopt);
+    std::move(callback).Run(std::nullopt);
     return;
   }
 
@@ -247,7 +247,7 @@
       device->GetGattService(service_id);
   if (!service) {
     std::move(callback).Run(mojom::GattResult::SERVICE_NOT_FOUND,
-                            absl::nullopt /* value */);
+                            std::nullopt /* value */);
     return;
   }
 
@@ -255,7 +255,7 @@
       service->GetCharacteristic(characteristic_id);
   if (!characteristic) {
     std::move(callback).Run(mojom::GattResult::CHARACTERISTIC_NOT_FOUND,
-                            absl::nullopt /* value */);
+                            std::nullopt /* value */);
     return;
   }
 
@@ -263,7 +263,7 @@
       characteristic->GetDescriptor(descriptor_id);
   if (!descriptor) {
     std::move(callback).Run(mojom::GattResult::DESCRIPTOR_NOT_FOUND,
-                            absl::nullopt /* value */);
+                            std::nullopt /* value */);
     return;
   }
 
@@ -345,12 +345,12 @@
 
 void Device::OnReadRemoteCharacteristic(
     ReadValueForCharacteristicCallback callback,
-    absl::optional<device::BluetoothGattService::GattErrorCode> error_code,
+    std::optional<device::BluetoothGattService::GattErrorCode> error_code,
     const std::vector<uint8_t>& value) {
   if (error_code.has_value()) {
     std::move(callback).Run(
         mojo::ConvertTo<mojom::GattResult>(error_code.value()),
-        absl::nullopt /* value */);
+        std::nullopt /* value */);
     return;
   }
   std::move(callback).Run(mojom::GattResult::SUCCESS, std::move(value));
@@ -369,12 +369,12 @@
 
 void Device::OnReadRemoteDescriptor(
     ReadValueForDescriptorCallback callback,
-    absl::optional<device::BluetoothGattService::GattErrorCode> error_code,
+    std::optional<device::BluetoothGattService::GattErrorCode> error_code,
     const std::vector<uint8_t>& value) {
   if (error_code.has_value()) {
     std::move(callback).Run(
         mojo::ConvertTo<mojom::GattResult>(error_code.value()),
-        /*value=*/absl::nullopt);
+        /*value=*/std::nullopt);
     return;
   }
   std::move(callback).Run(mojom::GattResult::SUCCESS, std::move(value));
diff --git a/device/bluetooth/device.h b/device/bluetooth/device.h
index 8798f685..6ab759e 100644
--- a/device/bluetooth/device.h
+++ b/device/bluetooth/device.h
@@ -91,7 +91,7 @@
 
   void OnReadRemoteCharacteristic(
       ReadValueForCharacteristicCallback callback,
-      absl::optional<device::BluetoothGattService::GattErrorCode> error_code,
+      std::optional<device::BluetoothGattService::GattErrorCode> error_code,
       const std::vector<uint8_t>& value);
 
   void OnWriteRemoteCharacteristic(
@@ -103,7 +103,7 @@
 
   void OnReadRemoteDescriptor(
       ReadValueForDescriptorCallback callback,
-      absl::optional<device::BluetoothGattService::GattErrorCode> error_code,
+      std::optional<device::BluetoothGattService::GattErrorCode> error_code,
       const std::vector<uint8_t>& value);
 
   void OnWriteRemoteDescriptor(WriteValueForDescriptorCallback callback);
diff --git a/device/bluetooth/event_utils_winrt.h b/device/bluetooth/event_utils_winrt.h
index b911257..92198c17 100644
--- a/device/bluetooth/event_utils_winrt.h
+++ b/device/bluetooth/event_utils_winrt.h
@@ -9,13 +9,13 @@
 #include <wrl/client.h>
 #include <wrl/event.h>
 
+#include <optional>
 #include <type_traits>
 #include <utility>
 
 #include "base/functional/bind.h"
 #include "base/logging.h"
 #include "base/task/single_thread_task_runner.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -29,14 +29,14 @@
 // Convenience template function to construct a TypedEventHandler from a
 // base::RepeatingCallback of a matching signature. In case of success, the
 // EventRegistrationToken is returned to the caller. A return value of
-// absl::nullopt indicates a failure. Events are posted to the same thread the
+// std::nullopt indicates a failure. Events are posted to the same thread the
 // event handler was created on.
 template <typename Interface,
           typename Sender,
           typename Args,
           typename SenderAbi,
           typename ArgsAbi>
-absl::optional<EventRegistrationToken> AddTypedEventHandler(
+std::optional<EventRegistrationToken> AddTypedEventHandler(
     Interface* i,
     internal::IMemberFunction<
         Interface,
@@ -65,7 +65,7 @@
   if (FAILED(hr)) {
     DVLOG(2) << "Adding EventHandler failed: "
              << logging::SystemErrorCodeToString(hr);
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return token;
diff --git a/device/bluetooth/floss/bluetooth_adapter_floss.cc b/device/bluetooth/floss/bluetooth_adapter_floss.cc
index ad1b7097..93347443 100644
--- a/device/bluetooth/floss/bluetooth_adapter_floss.cc
+++ b/device/bluetooth/floss/bluetooth_adapter_floss.cc
@@ -100,7 +100,7 @@
   // Empty device::BluetoothLowEnergyScanSession::Delegate overrides
   void OnSessionStarted(
       device::BluetoothLowEnergyScanSession* scan_session,
-      absl::optional<device::BluetoothLowEnergyScanSession::ErrorCode>
+      std::optional<device::BluetoothLowEnergyScanSession::ErrorCode>
           error_code) override {}
   void OnDeviceFound(device::BluetoothLowEnergyScanSession* scan_session,
                      device::BluetoothDevice* device) override {}
@@ -602,7 +602,7 @@
   }
 
   device->SetBondState(static_cast<FlossAdapterClient::BondState>(*ret),
-                       absl::nullopt);
+                       std::nullopt);
   if (device->HasReadProperties()) {
     NotifyDevicePairedChanged(device, device->IsPaired());
   }
@@ -1134,7 +1134,7 @@
     return;
   }
 
-  device->SetBondState(bond_state, absl::nullopt);
+  device->SetBondState(bond_state, std::nullopt);
   NotifyDeviceChanged(device);
   NotifyDevicePairedChanged(device, device->IsPaired());
 
@@ -1175,7 +1175,7 @@
   }
 }
 
-absl::optional<device::BluetoothDevice::BatteryType> variant_to_battery_type(
+std::optional<device::BluetoothDevice::BatteryType> variant_to_battery_type(
     const std::string& variant) {
   std::unordered_map<std::string, device::BluetoothDevice::BatteryType>
       battery_type_lookup = {
@@ -1186,7 +1186,7 @@
           {"case", device::BluetoothDevice::BatteryType::kCaseTrueWireless},
       };
   if (!base::Contains(battery_type_lookup, variant)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   return battery_type_lookup[variant];
 }
@@ -1201,7 +1201,7 @@
   }
 
   for (const auto& battery : battery_set.batteries) {
-    absl::optional<device::BluetoothDevice::BatteryType> battery_type =
+    std::optional<device::BluetoothDevice::BatteryType> battery_type =
         variant_to_battery_type(battery.variant);
     if (!battery_type) {
       LOG(WARNING) << "Unable to convert to battery_type from "
@@ -1245,7 +1245,7 @@
 #if BUILDFLAG(IS_CHROMEOS)
 void BluetoothAdapterFloss::DevicePolicyEffectChanged(
     const FlossDeviceId& device_id,
-    const absl::optional<PolicyEffect>& effect) {
+    const std::optional<PolicyEffect>& effect) {
   BLUETOOTH_LOG(EVENT) << __func__ << ": " << device_id;
 
   BluetoothDeviceFloss* device =
@@ -1379,7 +1379,7 @@
 
 void BluetoothAdapterFloss::ConnectDevice(
     const std::string& address,
-    const absl::optional<device::BluetoothDevice::AddressType>& address_type,
+    const std::optional<device::BluetoothDevice::AddressType>& address_type,
     ConnectDeviceCallback callback,
     ConnectDeviceErrorCallback error_callback) {
   // On Floss, ACL and RFCOMM connection are done with
@@ -1572,7 +1572,7 @@
   FlossDBusManager::Get()->GetLEScanClient()->StartScan(
       base::BindOnce(&BluetoothAdapterFloss::OnStartScan,
                      weak_ptr_factory_.GetWeakPtr(), uuid, scanner_id),
-      scanner_id, absl::nullopt, scanners_[uuid]->GetFlossScanFilter());
+      scanner_id, std::nullopt, scanners_[uuid]->GetFlossScanFilter());
 }
 
 void BluetoothAdapterFloss::ScanResultReceived(ScanResult scan_result) {
diff --git a/device/bluetooth/floss/bluetooth_adapter_floss.h b/device/bluetooth/floss/bluetooth_adapter_floss.h
index 169e56a1..b5a9b27 100644
--- a/device/bluetooth/floss/bluetooth_adapter_floss.h
+++ b/device/bluetooth/floss/bluetooth_adapter_floss.h
@@ -130,7 +130,7 @@
 
   void ConnectDevice(
       const std::string& address,
-      const absl::optional<device::BluetoothDevice::AddressType>& address_type,
+      const std::optional<device::BluetoothDevice::AddressType>& address_type,
       ConnectDeviceCallback callback,
       ConnectDeviceErrorCallback error_callback) override;
 
@@ -312,7 +312,7 @@
   // floss::FlossAdminClientObserver override.
   void DevicePolicyEffectChanged(
       const FlossDeviceId& device_id,
-      const absl::optional<PolicyEffect>& effect) override;
+      const std::optional<PolicyEffect>& effect) override;
   void ServiceAllowlistChanged(
       const std::vector<device::BluetoothUUID>& allowlist) override;
 #endif  // BUILDFLAG(IS_CHROMEOS)
diff --git a/device/bluetooth/floss/bluetooth_advertisement_floss.cc b/device/bluetooth/floss/bluetooth_advertisement_floss.cc
index fa3c8c9..d9ffe4f 100644
--- a/device/bluetooth/floss/bluetooth_advertisement_floss.cc
+++ b/device/bluetooth/floss/bluetooth_advertisement_floss.cc
@@ -56,25 +56,25 @@
       params_.connectable ? OwnAddressType::kPublic : OwnAddressType::kRandom;
 
   // Initializing advertise data.
-  absl::optional<UUIDList> service_uuids = data->service_uuids();
+  std::optional<UUIDList> service_uuids = data->service_uuids();
   if (service_uuids) {
     for (auto& uuid : *service_uuids) {
       adv_data_.service_uuids.emplace_back(uuid);
     }
   }
-  absl::optional<ManufacturerData> manuf_data = data->manufacturer_data();
+  std::optional<ManufacturerData> manuf_data = data->manufacturer_data();
   if (manuf_data) {
     for (auto& [key, val] : *manuf_data) {
       adv_data_.manufacturer_data.emplace(key, std::move(val));
     }
   }
-  absl::optional<UUIDList> solicit_uuids = data->solicit_uuids();
+  std::optional<UUIDList> solicit_uuids = data->solicit_uuids();
   if (solicit_uuids) {
     for (auto& uuid : *service_uuids) {
       adv_data_.solicit_uuids.emplace_back(uuid);
     }
   }
-  absl::optional<ServiceData> service_data = data->service_data();
+  std::optional<ServiceData> service_data = data->service_data();
   if (service_data) {
     for (auto& [key, val] : *service_data) {
       adv_data_.service_data.emplace(std::move(key), std::move(val));
@@ -85,7 +85,7 @@
   adv_data_.include_device_name = false;
 
   // Initializing scan response data.
-  absl::optional<ScanResponseData> scan_response_data =
+  std::optional<ScanResponseData> scan_response_data =
       data->scan_response_data();
   if (scan_response_data) {
     params_.scannable = true;
@@ -133,9 +133,9 @@
 
   FlossDBusManager::Get()->GetAdvertiserClient()->StartAdvertisingSet(
       params_, adv_data_,
-      (params_.scannable ? absl::optional<AdvertiseData>(scan_rsp_)
-                         : absl::nullopt),
-      absl::nullopt, absl::nullopt, kUnlimitedDuration, kUnlimitedAdvEvents,
+      (params_.scannable ? std::optional<AdvertiseData>(scan_rsp_)
+                         : std::nullopt),
+      std::nullopt, std::nullopt, kUnlimitedDuration, kUnlimitedAdvEvents,
       base::BindOnce(&BluetoothAdvertisementFloss::OnStartSuccess,
                      weak_ptr_factory_.GetWeakPtr(),
                      std::move(success_callback)),
diff --git a/device/bluetooth/floss/bluetooth_device_floss.cc b/device/bluetooth/floss/bluetooth_device_floss.cc
index 13e4c19..0de2a59b 100644
--- a/device/bluetooth/floss/bluetooth_device_floss.cc
+++ b/device/bluetooth/floss/bluetooth_device_floss.cc
@@ -108,9 +108,9 @@
   return appearance_;
 }
 
-absl::optional<std::string> BluetoothDeviceFloss::GetName() const {
+std::optional<std::string> BluetoothDeviceFloss::GetName() const {
   if (name_.length() == 0)
-    return absl::nullopt;
+    return std::nullopt;
 
   return name_;
 }
@@ -158,10 +158,10 @@
   return device_uuids_.GetUUIDs();
 }
 
-absl::optional<int8_t> BluetoothDeviceFloss::GetInquiryTxPower() const {
+std::optional<int8_t> BluetoothDeviceFloss::GetInquiryTxPower() const {
   NOTIMPLEMENTED();
 
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 bool BluetoothDeviceFloss::ExpectingPinCode() const {
@@ -247,7 +247,7 @@
     auto& [pending_cb, pending_error_cb] =
         pending_set_connection_latency_.value();
     std::move(pending_error_cb).Run();
-    pending_set_connection_latency_ = absl::nullopt;
+    pending_set_connection_latency_ = std::nullopt;
   }
 
   // If there is no active connection, UpdateConnectionParameters succeeds
@@ -268,7 +268,7 @@
 
   if ((connecting_state_ == ConnectingState::kACLConnecting) ||
       (connecting_state_ == ConnectingState::kProfilesConnecting) ||
-      (pending_callback_on_connect_profiles_ != absl::nullopt)) {
+      (pending_callback_on_connect_profiles_ != std::nullopt)) {
     std::move(callback).Run(
         BluetoothDevice::ConnectErrorCode::ERROR_INPROGRESS);
     return;
@@ -527,7 +527,7 @@
     std::move(pending_execute_write_->first).Run();
   }
 
-  pending_execute_write_ = absl::nullopt;
+  pending_execute_write_ = std::nullopt;
 }
 
 #endif  // BUILDFLAG(IS_CHROMEOS)
@@ -542,7 +542,7 @@
 
 void BluetoothDeviceFloss::SetBondState(
     FlossAdapterClient::BondState bond_state,
-    absl::optional<BluetoothDevice::ConnectErrorCode> error_code) {
+    std::optional<BluetoothDevice::ConnectErrorCode> error_code) {
   bond_state_ = bond_state;
 
   switch (bond_state_) {
@@ -550,7 +550,7 @@
       UpdateConnectingState(ConnectingState::kIdle, error_code);
       break;
     case FlossAdapterClient::BondState::kBondingInProgress:
-      UpdateConnectingState(ConnectingState::kACLConnecting, absl::nullopt);
+      UpdateConnectingState(ConnectingState::kACLConnecting, std::nullopt);
       break;
     case FlossAdapterClient::BondState::kBonded:
       if (connecting_state_ == ConnectingState::kACLConnecting) {
@@ -586,7 +586,7 @@
         BluetoothDevice::ConnectErrorCode::ERROR_DEVICE_UNCONNECTED);
   } else if (is_connected &&
              connecting_state_ == ConnectingState::kProfilesConnecting) {
-    UpdateConnectingState(ConnectingState::kProfilesConnected, absl::nullopt);
+    UpdateConnectingState(ConnectingState::kProfilesConnected, std::nullopt);
   }
 }
 
@@ -595,7 +595,7 @@
 }
 
 void BluetoothDeviceFloss::ConnectAllEnabledProfiles() {
-  UpdateConnectingState(ConnectingState::kProfilesConnecting, absl::nullopt);
+  UpdateConnectingState(ConnectingState::kProfilesConnecting, std::nullopt);
 
   connection_incomplete_timer_.Start(
       FROM_HERE, kDefaultConnectTimeout,
@@ -613,7 +613,7 @@
 }
 
 void BluetoothDeviceFloss::CreateGattConnectionImpl(
-    absl::optional<device::BluetoothUUID> service_uuid) {
+    std::optional<device::BluetoothUUID> service_uuid) {
   // Generally, the first ever connection to a device should be direct and
   // subsequent connections to known devices should be invoked with is_direct =
   // false. Refer to |autoConnect| on BluetoothGatt.java.
@@ -793,13 +793,13 @@
   // Floss does not send any notifications that profiles have successfully
   // connected if we are already ACL connected.
   if (is_acl_connected_) {
-    UpdateConnectingState(ConnectingState::kProfilesConnected, absl::nullopt);
+    UpdateConnectingState(ConnectingState::kProfilesConnected, std::nullopt);
   }
 }
 
 void BluetoothDeviceFloss::UpdateConnectingState(
     ConnectingState state,
-    absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+    std::optional<BluetoothDevice::ConnectErrorCode> error) {
   if ((state == ConnectingState::kIdle) &&
       ((connecting_state_ == ConnectingState::kACLConnecting) ||
        (connecting_state_ == ConnectingState::kProfilesConnecting))) {
@@ -808,7 +808,7 @@
   } else if ((state == ConnectingState::kProfilesConnected) &&
              (connecting_state_ == ConnectingState::kProfilesConnecting)) {
     // Successful profile connection
-    TriggerConnectCallback(absl::nullopt);
+    TriggerConnectCallback(std::nullopt);
   }
 
   if (connecting_state_ != state) {
@@ -826,7 +826,7 @@
 }
 
 void BluetoothDeviceFloss::TriggerConnectCallback(
-    absl::optional<BluetoothDevice::ConnectErrorCode> error_code) {
+    std::optional<BluetoothDevice::ConnectErrorCode> error_code) {
   connection_incomplete_timer_.Stop();
 
   if (pending_callback_on_connect_profiles_) {
@@ -834,7 +834,7 @@
     // to nullopt before Run-ing the callback, because this may trigger arriving
     // at this same location.
     auto callback = std::move(*pending_callback_on_connect_profiles_);
-    pending_callback_on_connect_profiles_ = absl::nullopt;
+    pending_callback_on_connect_profiles_ = std::nullopt;
     std::move(callback).Run(error_code);
   }
 
@@ -961,7 +961,7 @@
         property_reads_completed_ | property_reads_triggered_);
     property_reads_triggered_ = PropertiesState::kNotRead;
     std::move(*pending_callback_on_init_props_).Run();
-    pending_callback_on_init_props_ = absl::nullopt;
+    pending_callback_on_init_props_ = std::nullopt;
   }
 
   DCHECK(num_pending_properties_ >= 0);
@@ -975,7 +975,7 @@
   if (address != address_)
     return;
 
-  absl::optional<ConnectErrorCode> err = absl::nullopt;
+  std::optional<ConnectErrorCode> err = std::nullopt;
 
   if (status != GattStatus::kSuccess) {
     // TODO(b/193686094) - Convert GattStatus to other connect error codes.
@@ -1061,7 +1061,7 @@
       std::move(pending_error_cb).Run();
     }
 
-    pending_set_connection_latency_ = absl::nullopt;
+    pending_set_connection_latency_ = std::nullopt;
   }
 }
 
@@ -1092,7 +1092,7 @@
         base::DoNothing(), address_);
   }
 
-  DidConnectGatt(absl::nullopt);
+  DidConnectGatt(std::nullopt);
 }
 
 #if BUILDFLAG(IS_CHROMEOS)
diff --git a/device/bluetooth/floss/bluetooth_device_floss.h b/device/bluetooth/floss/bluetooth_device_floss.h
index 673bcfa..35ce733 100644
--- a/device/bluetooth/floss/bluetooth_device_floss.h
+++ b/device/bluetooth/floss/bluetooth_device_floss.h
@@ -70,7 +70,7 @@
   uint16_t GetProductID() const override;
   uint16_t GetDeviceID() const override;
   uint16_t GetAppearance() const override;
-  absl::optional<std::string> GetName() const override;
+  std::optional<std::string> GetName() const override;
   bool IsPaired() const override;
 #if BUILDFLAG(IS_CHROMEOS)
   bool IsBonded() const override;
@@ -80,7 +80,7 @@
   bool IsConnectable() const override;
   bool IsConnecting() const override;
   UUIDSet GetUUIDs() const override;
-  absl::optional<int8_t> GetInquiryTxPower() const override;
+  std::optional<int8_t> GetInquiryTxPower() const override;
   bool ExpectingPinCode() const override;
   bool ExpectingPasskey() const override;
   bool ExpectingConfirmation() const override;
@@ -136,7 +136,7 @@
   FlossAdapterClient::BondState GetBondState() { return bond_state_; }
   void SetBondState(
       FlossAdapterClient::BondState bond_state,
-      absl::optional<BluetoothDevice::ConnectErrorCode> error_code);
+      std::optional<BluetoothDevice::ConnectErrorCode> error_code);
   void SetIsConnected(bool is_connected);
   void SetConnectionState(uint32_t state);
   void ResetPairing();
@@ -192,7 +192,7 @@
  protected:
   // BluetoothDevice override
   void CreateGattConnectionImpl(
-      absl::optional<device::BluetoothUUID> service_uuid) override;
+      std::optional<device::BluetoothUUID> service_uuid) override;
   void UpgradeToFullDiscovery() override;
   void DisconnectGatt() override;
 
@@ -204,12 +204,12 @@
   // Updates the state of connecting and calls callbacks accordingly.
   void UpdateConnectingState(
       ConnectingState state,
-      absl::optional<BluetoothDevice::ConnectErrorCode> error);
+      std::optional<BluetoothDevice::ConnectErrorCode> error);
   // Updates the state of gatt connecting.
   void UpdateGattConnectingState(GattConnectingState state);
   // Triggers the pending callback of Connect() method.
   void TriggerConnectCallback(
-      absl::optional<BluetoothDevice::ConnectErrorCode> error_code);
+      std::optional<BluetoothDevice::ConnectErrorCode> error_code);
 
   void OnGetRemoteType(base::OnceClosure callback,
                        DBusResult<FlossAdapterClient::BluetoothDeviceType> ret);
@@ -248,23 +248,23 @@
                       DBusResult<Void> ret);
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
-  absl::optional<ConnectCallback> pending_callback_on_connect_profiles_ =
-      absl::nullopt;
+  std::optional<ConnectCallback> pending_callback_on_connect_profiles_ =
+      std::nullopt;
 
   // Timer to stop waiting for a successful connect complete.
   base::OneShotTimer connection_incomplete_timer_;
 
-  absl::optional<base::OnceClosure> pending_callback_on_init_props_ =
-      absl::nullopt;
+  std::optional<base::OnceClosure> pending_callback_on_init_props_ =
+      std::nullopt;
 
   // Callbacks for a pending |SetConnectionLatency|.
-  absl::optional<std::pair<base::OnceClosure, ErrorCallback>>
-      pending_set_connection_latency_ = absl::nullopt;
+  std::optional<std::pair<base::OnceClosure, ErrorCallback>>
+      pending_set_connection_latency_ = std::nullopt;
 
 #if BUILDFLAG(IS_CHROMEOS)
   // Callbacks for a pending |ExecuteWrite| or |AbortWrite|.
-  absl::optional<std::pair<base::OnceClosure, ExecuteWriteErrorCallback>>
-      pending_execute_write_ = absl::nullopt;
+  std::optional<std::pair<base::OnceClosure, ExecuteWriteErrorCallback>>
+      pending_execute_write_ = std::nullopt;
 
   // Writes are using reliable writes.
   bool using_reliable_write_ = false;
@@ -316,7 +316,7 @@
 
   // Specific uuid to search for after gatt connection is established. If this
   // is not set, then we do full discovery.
-  absl::optional<device::BluetoothUUID> search_uuid;
+  std::optional<device::BluetoothUUID> search_uuid;
 
   // Similar to is_acl_connected_ but contains the full connection state
   // (including encryption). This is updated when |SetConnectionState| is called
diff --git a/device/bluetooth/floss/bluetooth_floss_unittest.cc b/device/bluetooth/floss/bluetooth_floss_unittest.cc
index cf9f9b5..436dded 100644
--- a/device/bluetooth/floss/bluetooth_floss_unittest.cc
+++ b/device/bluetooth/floss/bluetooth_floss_unittest.cc
@@ -50,7 +50,7 @@
 
   void OnSessionStarted(
       device::BluetoothLowEnergyScanSession* scan_session,
-      absl::optional<device::BluetoothLowEnergyScanSession::ErrorCode>
+      std::optional<device::BluetoothLowEnergyScanSession::ErrorCode>
           error_code) override {
     sessions_started_++;
   }
@@ -236,7 +236,7 @@
   device->Connect(
       &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_TRUE(error.has_value());
             run_loop.Quit();
           }));
@@ -260,7 +260,7 @@
   device->Connect(
       &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
@@ -283,14 +283,14 @@
   device->Connect(
       &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
   device->Connect(
       &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_TRUE(error.has_value());
             run_loop.Quit();
           }));
@@ -317,7 +317,7 @@
   device->Connect(
       &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
@@ -352,7 +352,7 @@
   device->Connect(
       &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
@@ -389,7 +389,7 @@
   device->Connect(
       &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_TRUE(error.has_value());
             run_loop.Quit();
           }));
@@ -415,7 +415,7 @@
   device->Connect(
       &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
@@ -442,7 +442,7 @@
   device->Connect(
       &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
@@ -513,7 +513,7 @@
   device->Connect(
       &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
@@ -547,7 +547,7 @@
   device->Connect(
       &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
@@ -571,7 +571,7 @@
   device->Connect(
       &pairing_delegate,
       base::BindLambdaForTesting(
-          [&run_loop](absl::optional<BluetoothDevice::ConnectErrorCode> error) {
+          [&run_loop](std::optional<BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             run_loop.Quit();
           }));
diff --git a/device/bluetooth/floss/bluetooth_gatt_floss_unittest.cc b/device/bluetooth/floss/bluetooth_gatt_floss_unittest.cc
index 80bcf762..b39af11c 100644
--- a/device/bluetooth/floss/bluetooth_gatt_floss_unittest.cc
+++ b/device/bluetooth/floss/bluetooth_gatt_floss_unittest.cc
@@ -170,14 +170,14 @@
       base::BindLambdaForTesting(
           [&paired_device, &loop](
               std::unique_ptr<device::BluetoothGattConnection> conn,
-              absl::optional<device::BluetoothDevice::ConnectErrorCode> error) {
+              std::optional<device::BluetoothDevice::ConnectErrorCode> error) {
             EXPECT_FALSE(error.has_value());
             EXPECT_TRUE(conn->IsConnected());
             EXPECT_EQ(paired_device->GetAddress(), conn->GetDeviceAddress());
 
             loop.Quit();
           }),
-      /*service_uuid=*/absl::nullopt);
+      /*service_uuid=*/std::nullopt);
 
   // Fake a connection completion. First you should get the ACL connection
   // completed and then the GattConnectionState.
@@ -205,7 +205,7 @@
   ASSERT_TRUE(paired_device != nullptr);
 
   device::BluetoothUUID fake_uuid(kFakeUuidShort);
-  absl::optional<device::BluetoothUUID> fake_uuid_optional = fake_uuid;
+  std::optional<device::BluetoothUUID> fake_uuid_optional = fake_uuid;
   GattService fake_service = CreateFakeServiceFor(fake_uuid);
 
   // Create a gatt connection with partial service discovery.
@@ -230,7 +230,7 @@
 
   // Now try to upgrade to full discovery by connecting with no services.
   paired_device->CreateGattConnection(base::DoNothing(),
-                                      /*service_uuid=*/absl::nullopt);
+                                      /*service_uuid=*/std::nullopt);
   EXPECT_FALSE(paired_device->IsGattServicesDiscoveryComplete());
 
   // Wait for discovery to complete again.
diff --git a/device/bluetooth/floss/bluetooth_low_energy_scan_session_floss.cc b/device/bluetooth/floss/bluetooth_low_energy_scan_session_floss.cc
index 90dca54..c5b60d5 100644
--- a/device/bluetooth/floss/bluetooth_low_energy_scan_session_floss.cc
+++ b/device/bluetooth/floss/bluetooth_low_energy_scan_session_floss.cc
@@ -36,7 +36,7 @@
   }
 
   has_activated_ = true;
-  delegate_->OnSessionStarted(this, /*error_code=*/absl::nullopt);
+  delegate_->OnSessionStarted(this, /*error_code=*/std::nullopt);
 }
 
 void BluetoothLowEnergyScanSessionFloss::OnRelease() {
@@ -76,10 +76,10 @@
   uuid_ = uuid;
 }
 
-absl::optional<ScanFilter>
+std::optional<ScanFilter>
 BluetoothLowEnergyScanSessionFloss::GetFlossScanFilter() {
   if (!filter_)
-    return absl::nullopt;
+    return std::nullopt;
 
   ScanFilter filter;
   filter.rssi_high_threshold = filter_->device_found_rssi_threshold();
diff --git a/device/bluetooth/floss/bluetooth_low_energy_scan_session_floss.h b/device/bluetooth/floss/bluetooth_low_energy_scan_session_floss.h
index 2239e1a3..c41460d 100644
--- a/device/bluetooth/floss/bluetooth_low_energy_scan_session_floss.h
+++ b/device/bluetooth/floss/bluetooth_low_energy_scan_session_floss.h
@@ -30,7 +30,7 @@
   void OnDeviceLost(device::BluetoothDevice* device);
   void OnRegistered(device::BluetoothUUID uuid);
   uint8_t GetScannerId() { return scanner_id_; }
-  absl::optional<ScanFilter> GetFlossScanFilter();
+  std::optional<ScanFilter> GetFlossScanFilter();
 
   base::WeakPtr<BluetoothLowEnergyScanSessionFloss> GetWeakPtr() {
     return weak_ptr_factory_.GetWeakPtr();
diff --git a/device/bluetooth/floss/bluetooth_remote_gatt_characteristic_floss.cc b/device/bluetooth/floss/bluetooth_remote_gatt_characteristic_floss.cc
index 5e6b8fa..c3dd306 100644
--- a/device/bluetooth/floss/bluetooth_remote_gatt_characteristic_floss.cc
+++ b/device/bluetooth/floss/bluetooth_remote_gatt_characteristic_floss.cc
@@ -192,7 +192,7 @@
     cached_data_ = data;
 
     std::move(pending_read_callback_)
-        .Run(/*error_code=*/absl::nullopt, cached_data_);
+        .Run(/*error_code=*/std::nullopt, cached_data_);
   } else {
     std::move(pending_read_callback_)
         .Run(BluetoothGattServiceFloss::GattStatusToServiceError(status), {});
diff --git a/device/bluetooth/floss/bluetooth_remote_gatt_descriptor_floss.cc b/device/bluetooth/floss/bluetooth_remote_gatt_descriptor_floss.cc
index 3193f6d4..f9fe1f6 100644
--- a/device/bluetooth/floss/bluetooth_remote_gatt_descriptor_floss.cc
+++ b/device/bluetooth/floss/bluetooth_remote_gatt_descriptor_floss.cc
@@ -120,7 +120,7 @@
     cached_data_ = data;
 
     std::move(pending_read_callback_)
-        .Run(/*error_code=*/absl::nullopt, cached_data_);
+        .Run(/*error_code=*/std::nullopt, cached_data_);
   } else {
     std::move(pending_read_callback_)
         .Run(BluetoothGattServiceFloss::GattStatusToServiceError(status), {});
diff --git a/device/bluetooth/floss/bluetooth_socket_floss.cc b/device/bluetooth/floss/bluetooth_socket_floss.cc
index 1825575..376a8157 100644
--- a/device/bluetooth/floss/bluetooth_socket_floss.cc
+++ b/device/bluetooth/floss/bluetooth_socket_floss.cc
@@ -73,7 +73,7 @@
 void BluetoothSocketFloss::Listen(
     scoped_refptr<device::BluetoothAdapter> adapter,
     FlossSocketManager::SocketType socket_type,
-    const absl::optional<device::BluetoothUUID>& uuid,
+    const std::optional<device::BluetoothUUID>& uuid,
     const device::BluetoothAdapter::ServiceOptions& service_options,
     base::OnceClosure success_callback,
     ErrorCompletionCallback error_callback) {
@@ -148,7 +148,7 @@
         listening_socket_info_->id,
         base::BindOnce(&BluetoothSocketFloss::CompleteClose,
                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-    listening_socket_info_ = absl::nullopt;
+    listening_socket_info_ = std::nullopt;
     pending_accept_socket_.reset();
   } else {
     if (pending_listen_ready_callback_) {
@@ -201,7 +201,7 @@
   // to accepting.
   if (!is_accepting_) {
     FlossDBusManager::Get()->GetSocketManager()->Accept(
-        listening_socket_info_->id, absl::nullopt,
+        listening_socket_info_->id, std::nullopt,
         base::BindOnce(&BluetoothSocketFloss::CompleteAccept,
                        weak_ptr_factory_.GetWeakPtr()));
   }
@@ -231,7 +231,7 @@
   if (state == FlossSocketManager::ServerSocketState::kReady &&
       status == FlossDBusClient::BtifStatus::kSuccess) {
     FlossDBusManager::Get()->GetSocketManager()->Accept(
-        listening_socket_info_->id, absl::nullopt,
+        listening_socket_info_->id, std::nullopt,
         base::BindOnce(&BluetoothSocketFloss::CompleteAccept,
                        weak_ptr_factory_.GetWeakPtr()));
     return;
@@ -289,7 +289,7 @@
     base::OnceClosure success_callback,
     ErrorCompletionCallback error_callback,
     FlossDBusClient::BtifStatus status,
-    absl::optional<FlossSocketManager::FlossSocket>&& socket) {
+    std::optional<FlossSocketManager::FlossSocket>&& socket) {
   DCHECK(ui_task_runner()->RunsTasksInCurrentSequence());
 
   if (status == FlossDBusClient::BtifStatus::kSuccess && socket) {
@@ -316,7 +316,7 @@
     base::OnceClosure success_callback,
     ErrorCompletionCallback error_callback,
     FlossDBusClient::BtifStatus status,
-    absl::optional<FlossSocketManager::FlossSocket>&& socket) {
+    std::optional<FlossSocketManager::FlossSocket>&& socket) {
   DCHECK(socket_thread()->task_runner()->RunsTasksInCurrentSequence());
 
   if (status != FlossDBusClient::BtifStatus::kSuccess || !socket) {
@@ -423,7 +423,7 @@
   pending_accept_socket_ = BluetoothSocketFloss::CreateBluetoothSocket(
       ui_task_runner(), socket_thread());
 
-  absl::optional<FlossSocketManager::FlossSocket> sock(
+  std::optional<FlossSocketManager::FlossSocket> sock(
       std::move(connection_request_queue_.front()));
   connection_request_queue_.pop();
 
diff --git a/device/bluetooth/floss/bluetooth_socket_floss.h b/device/bluetooth/floss/bluetooth_socket_floss.h
index bd1ad10..aebe943 100644
--- a/device/bluetooth/floss/bluetooth_socket_floss.h
+++ b/device/bluetooth/floss/bluetooth_socket_floss.h
@@ -62,7 +62,7 @@
   virtual void Listen(
       scoped_refptr<device::BluetoothAdapter> adapter,
       FlossSocketManager::SocketType socket_type,
-      const absl::optional<device::BluetoothUUID>& uuid,
+      const std::optional<device::BluetoothUUID>& uuid,
       const device::BluetoothAdapter::ServiceOptions& service_options,
       base::OnceClosure success_callback,
       ErrorCompletionCallback error_callback);
@@ -89,11 +89,10 @@
                       DBusResult<FlossDBusClient::BtifStatus> result);
 
   // Callback that handles completion of a socket connection.
-  void CompleteConnect(
-      base::OnceClosure success_callback,
-      ErrorCompletionCallback error_callback,
-      FlossDBusClient::BtifStatus status,
-      absl::optional<FlossSocketManager::FlossSocket>&& socket);
+  void CompleteConnect(base::OnceClosure success_callback,
+                       ErrorCompletionCallback error_callback,
+                       FlossDBusClient::BtifStatus status,
+                       std::optional<FlossSocketManager::FlossSocket>&& socket);
 
   // Callback that handles completion of socket accept.
   void CompleteAccept(DBusResult<FlossDBusClient::BtifStatus> result);
@@ -114,7 +113,7 @@
       base::OnceClosure success_callback,
       ErrorCompletionCallback error_callback,
       FlossDBusClient::BtifStatus status,
-      absl::optional<FlossSocketManager::FlossSocket>&& socket);
+      std::optional<FlossSocketManager::FlossSocket>&& socket);
 
  private:
   struct AcceptRequest {
@@ -138,7 +137,7 @@
   std::string device_address_;
 
   // Information about a listening socket. Empty if this socket isn't listening.
-  absl::optional<FlossSocketManager::FlossListeningSocket>
+  std::optional<FlossSocketManager::FlossListeningSocket>
       listening_socket_info_;
 
   // Is this socket currently accepting? This status reflects what the listening
diff --git a/device/bluetooth/floss/exported_callback_manager_unittest.cc b/device/bluetooth/floss/exported_callback_manager_unittest.cc
index c7f28b6..8f6e9f4 100644
--- a/device/bluetooth/floss/exported_callback_manager_unittest.cc
+++ b/device/bluetooth/floss/exported_callback_manager_unittest.cc
@@ -66,7 +66,7 @@
   void SomeMethod() override { some_method_called = true; }
 
   // For test inspections.
-  absl::optional<std::tuple<std::string, uint32_t, bool>>
+  std::optional<std::tuple<std::string, uint32_t, bool>>
       last_something_happened;
   bool some_method_called = false;
 
diff --git a/device/bluetooth/floss/fake_floss_adapter_client.cc b/device/bluetooth/floss/fake_floss_adapter_client.cc
index 4226db1..2f6df9c 100644
--- a/device/bluetooth/floss/fake_floss_adapter_client.cc
+++ b/device/bluetooth/floss/fake_floss_adapter_client.cc
@@ -139,7 +139,7 @@
 void FakeFlossAdapterClient::StartDiscovery(ResponseCallback<Void> callback) {
   // Fail fast if we're meant to fail discovery
   if (fail_discovery_) {
-    fail_discovery_ = absl::nullopt;
+    fail_discovery_ = std::nullopt;
 
     std::move(callback).Run(base::unexpected(
         Error("org.chromium.bluetooth.Bluetooth.FooError", "Foo error")));
@@ -188,7 +188,7 @@
                                         FlossDeviceId device,
                                         BluetoothTransport transport) {
   if (fail_bonding_) {
-    fail_bonding_ = absl::nullopt;
+    fail_bonding_ = std::nullopt;
 
     std::move(callback).Run(base::unexpected(
         Error("org.chromium.Error.Failed", "Bonding failed by request")));
@@ -408,11 +408,11 @@
 }
 
 void FakeFlossAdapterClient::FailNextDiscovery() {
-  fail_discovery_ = absl::make_optional(true);
+  fail_discovery_ = std::make_optional(true);
 }
 
 void FakeFlossAdapterClient::FailNextBonding() {
-  fail_bonding_ = absl::make_optional(true);
+  fail_bonding_ = std::make_optional(true);
 }
 
 void FakeFlossAdapterClient::SetPairingConfirmation(
diff --git a/device/bluetooth/floss/fake_floss_adapter_client.h b/device/bluetooth/floss/fake_floss_adapter_client.h
index 7e79d46..851506d 100644
--- a/device/bluetooth/floss/fake_floss_adapter_client.h
+++ b/device/bluetooth/floss/fake_floss_adapter_client.h
@@ -122,8 +122,8 @@
  private:
   std::unordered_set<std::string> bonded_addresses_;
   std::unordered_set<std::string> connected_addresses_;
-  absl::optional<bool> fail_discovery_;
-  absl::optional<bool> fail_bonding_;
+  std::optional<bool> fail_discovery_;
+  std::optional<bool> fail_bonding_;
   base::WeakPtrFactory<FakeFlossAdapterClient> weak_ptr_factory_{this};
 };
 
diff --git a/device/bluetooth/floss/fake_floss_advertiser_client.cc b/device/bluetooth/floss/fake_floss_advertiser_client.cc
index b8bc6dc..b516b9d3 100644
--- a/device/bluetooth/floss/fake_floss_advertiser_client.cc
+++ b/device/bluetooth/floss/fake_floss_advertiser_client.cc
@@ -27,9 +27,9 @@
 void FakeFlossAdvertiserClient::StartAdvertisingSet(
     const AdvertisingSetParameters& params,
     const AdvertiseData& adv_data,
-    const absl::optional<AdvertiseData> scan_rsp,
-    const absl::optional<PeriodicAdvertisingParameters> periodic_params,
-    const absl::optional<AdvertiseData> periodic_data,
+    const std::optional<AdvertiseData> scan_rsp,
+    const std::optional<PeriodicAdvertisingParameters> periodic_params,
+    const std::optional<AdvertiseData> periodic_data,
     const int32_t duration,
     const int32_t max_ext_adv_events,
     StartSuccessCallback success_callback,
diff --git a/device/bluetooth/floss/fake_floss_advertiser_client.h b/device/bluetooth/floss/fake_floss_advertiser_client.h
index 01ab8b86..c39f268 100644
--- a/device/bluetooth/floss/fake_floss_advertiser_client.h
+++ b/device/bluetooth/floss/fake_floss_advertiser_client.h
@@ -27,9 +27,9 @@
   void StartAdvertisingSet(
       const AdvertisingSetParameters& params,
       const AdvertiseData& adv_data,
-      const absl::optional<AdvertiseData> scan_rsp,
-      const absl::optional<PeriodicAdvertisingParameters> periodic_params,
-      const absl::optional<AdvertiseData> periodic_data,
+      const std::optional<AdvertiseData> scan_rsp,
+      const std::optional<PeriodicAdvertisingParameters> periodic_params,
+      const std::optional<AdvertiseData> periodic_data,
       const int32_t duration,
       const int32_t max_ext_adv_events,
       StartSuccessCallback success_callback,
diff --git a/device/bluetooth/floss/fake_floss_battery_manager_client.cc b/device/bluetooth/floss/fake_floss_battery_manager_client.cc
index 09da4772..9c977aa 100644
--- a/device/bluetooth/floss/fake_floss_battery_manager_client.cc
+++ b/device/bluetooth/floss/fake_floss_battery_manager_client.cc
@@ -21,9 +21,9 @@
 }
 
 void FakeFlossBatteryManagerClient::GetBatteryInformation(
-    ResponseCallback<absl::optional<BatterySet>> callback,
+    ResponseCallback<std::optional<BatterySet>> callback,
     const FlossDeviceId& device) {
-  std::move(callback).Run(DBusResult<absl::optional<BatterySet>>({}));
+  std::move(callback).Run(DBusResult<std::optional<BatterySet>>({}));
 }
 
 void FakeFlossBatteryManagerClient::AddObserver(
diff --git a/device/bluetooth/floss/fake_floss_battery_manager_client.h b/device/bluetooth/floss/fake_floss_battery_manager_client.h
index 36b66980..998bc94 100644
--- a/device/bluetooth/floss/fake_floss_battery_manager_client.h
+++ b/device/bluetooth/floss/fake_floss_battery_manager_client.h
@@ -21,7 +21,7 @@
             base::OnceClosure on_ready) override;
 
   void GetBatteryInformation(
-      ResponseCallback<absl::optional<BatterySet>> callback,
+      ResponseCallback<std::optional<BatterySet>> callback,
       const FlossDeviceId& device) override;
 
   void AddObserver(FlossBatteryManagerClientObserver* observer) override;
diff --git a/device/bluetooth/floss/fake_floss_lescan_client.cc b/device/bluetooth/floss/fake_floss_lescan_client.cc
index 976b6c8..2c53318 100644
--- a/device/bluetooth/floss/fake_floss_lescan_client.cc
+++ b/device/bluetooth/floss/fake_floss_lescan_client.cc
@@ -44,8 +44,8 @@
 void FakeFlossLEScanClient::StartScan(
     ResponseCallback<BtifStatus> callback,
     uint8_t scanner_id,
-    const absl::optional<ScanSettings>& scan_settings,
-    const absl::optional<ScanFilter>& filters) {
+    const std::optional<ScanSettings>& scan_settings,
+    const std::optional<ScanFilter>& filters) {
   scanner_ids_.insert(scanner_id);
   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), BtifStatus::kSuccess));
diff --git a/device/bluetooth/floss/fake_floss_lescan_client.h b/device/bluetooth/floss/fake_floss_lescan_client.h
index c9d30e0..d94599daa 100644
--- a/device/bluetooth/floss/fake_floss_lescan_client.h
+++ b/device/bluetooth/floss/fake_floss_lescan_client.h
@@ -31,8 +31,8 @@
                          uint8_t scanner_id) override;
   void StartScan(ResponseCallback<BtifStatus> callback,
                  uint8_t scanner_id,
-                 const absl::optional<ScanSettings>& scan_settings,
-                 const absl::optional<ScanFilter>& filters) override;
+                 const std::optional<ScanSettings>& scan_settings,
+                 const std::optional<ScanFilter>& filters) override;
 
   void SetNextScannerUUID(const device::BluetoothUUID& uuid) {
     next_scanner_uuid_ = uuid;
diff --git a/device/bluetooth/floss/fake_floss_socket_manager.cc b/device/bluetooth/floss/fake_floss_socket_manager.cc
index 3f9e1b1..c5f4a5de 100644
--- a/device/bluetooth/floss/fake_floss_socket_manager.cc
+++ b/device/bluetooth/floss/fake_floss_socket_manager.cc
@@ -144,7 +144,7 @@
     const int psm,
     const Security security_level,
     ConnectionCompleted callback) {
-  std::move(callback).Run(BtifStatus::kFail, /*socket=*/absl::nullopt);
+  std::move(callback).Run(BtifStatus::kFail, /*socket=*/std::nullopt);
 }
 
 void FakeFlossSocketManager::ConnectUsingL2capLe(
@@ -152,7 +152,7 @@
     const int psm,
     const Security security_level,
     ConnectionCompleted callback) {
-  std::move(callback).Run(BtifStatus::kFail, /*socket=*/absl::nullopt);
+  std::move(callback).Run(BtifStatus::kFail, /*socket=*/std::nullopt);
 }
 
 void FakeFlossSocketManager::ConnectUsingRfcomm(
@@ -163,7 +163,7 @@
   // Check for the supported uuid or return error.
   if (uuid.canonical_value() !=
       device::BluetoothUUID(kRfcommUuid).canonical_value()) {
-    std::move(callback).Run(BtifStatus::kFail, /*socket=*/absl::nullopt);
+    std::move(callback).Run(BtifStatus::kFail, /*socket=*/std::nullopt);
     return;
   }
 
@@ -175,12 +175,12 @@
   socket.uuid = uuid;
   socket.fd = base::ScopedFD(SimulateSocket());
 
-  absl::optional<FlossSocket> sockout(std::move(socket));
+  std::optional<FlossSocket> sockout(std::move(socket));
   std::move(callback).Run(BtifStatus::kSuccess, std::move(sockout));
 }
 
 void FakeFlossSocketManager::Accept(const SocketId id,
-                                    absl::optional<uint32_t> timeout_ms,
+                                    std::optional<uint32_t> timeout_ms,
                                     ResponseCallback<BtifStatus> callback) {
   auto found = listening_sockets_to_callbacks_.find(id);
   if (found != listening_sockets_to_callbacks_.end()) {
diff --git a/device/bluetooth/floss/fake_floss_socket_manager.h b/device/bluetooth/floss/fake_floss_socket_manager.h
index daf0ae7..ae52de2 100644
--- a/device/bluetooth/floss/fake_floss_socket_manager.h
+++ b/device/bluetooth/floss/fake_floss_socket_manager.h
@@ -58,7 +58,7 @@
                           const Security security_level,
                           ConnectionCompleted callback) override;
   void Accept(const SocketId id,
-              absl::optional<uint32_t> timeout_ms,
+              std::optional<uint32_t> timeout_ms,
               ResponseCallback<BtifStatus> callback) override;
   void Close(const SocketId id, ResponseCallback<BtifStatus> callback) override;
 
diff --git a/device/bluetooth/floss/floss_adapter_client.h b/device/bluetooth/floss/floss_adapter_client.h
index 54cb4fde..578fed28 100644
--- a/device/bluetooth/floss/floss_adapter_client.h
+++ b/device/bluetooth/floss/floss_adapter_client.h
@@ -499,10 +499,10 @@
   int pending_register_calls_ = 0;
 
   // Callback ID used for callbacks registered to this client.
-  absl::optional<uint32_t> callback_id_;
+  std::optional<uint32_t> callback_id_;
 
   // Callback ID used for connection callbacks registered to this client.
-  absl::optional<uint32_t> connection_callback_id_;
+  std::optional<uint32_t> connection_callback_id_;
 
   base::WeakPtrFactory<FlossAdapterClient> weak_ptr_factory_{this};
 };
diff --git a/device/bluetooth/floss/floss_admin_client.cc b/device/bluetooth/floss/floss_admin_client.cc
index 98025e5d..1667f58 100644
--- a/device/bluetooth/floss/floss_admin_client.cc
+++ b/device/bluetooth/floss/floss_admin_client.cc
@@ -209,7 +209,7 @@
 
 void FlossAdminClient::OnDevicePolicyEffectChanged(
     const FlossDeviceId& device_id,
-    const absl::optional<PolicyEffect>& effect) {
+    const std::optional<PolicyEffect>& effect) {
   for (auto& observer : observers_) {
     observer.DevicePolicyEffectChanged(device_id, effect);
   }
diff --git a/device/bluetooth/floss/floss_admin_client.h b/device/bluetooth/floss/floss_admin_client.h
index c0ffd84..cf47617 100644
--- a/device/bluetooth/floss/floss_admin_client.h
+++ b/device/bluetooth/floss/floss_admin_client.h
@@ -44,7 +44,7 @@
   // Notification sent when the policy effect to a device changed.
   virtual void DevicePolicyEffectChanged(
       const FlossDeviceId& device_id,
-      const absl::optional<PolicyEffect>& effect) {}
+      const std::optional<PolicyEffect>& effect) {}
 
   // Notification sent when the service allowlist changed.
   virtual void ServiceAllowlistChanged(
@@ -90,7 +90,7 @@
  protected:
   // Handle callback |OnDevicePolicyEffectChanged| on exported object path.
   void OnDevicePolicyEffectChanged(const FlossDeviceId& device_id,
-                                   const absl::optional<PolicyEffect>& effect);
+                                   const std::optional<PolicyEffect>& effect);
   // Handle callback |OnServiceAllowlistChanged| on exported object path
   void OnServiceAllowlistChanged(
       const std::vector<std::vector<uint8_t>>& allowlist);
@@ -137,7 +137,7 @@
       admin::kCallbackInterface};
 
   // Callback ID used for callbacks registered to this client.
-  absl::optional<uint32_t> callback_id_;
+  std::optional<uint32_t> callback_id_;
 
   // Signal when the client is ready to be used.
   base::OnceClosure on_ready_;
diff --git a/device/bluetooth/floss/floss_admin_client_unittest.cc b/device/bluetooth/floss/floss_admin_client_unittest.cc
index c2c191c..14aabd5 100644
--- a/device/bluetooth/floss/floss_admin_client_unittest.cc
+++ b/device/bluetooth/floss/floss_admin_client_unittest.cc
@@ -92,7 +92,7 @@
   // AdminClientObserver overrides
   void DevicePolicyEffectChanged(
       const FlossDeviceId& device_id,
-      const absl::optional<PolicyEffect>& effect) override {
+      const std::optional<PolicyEffect>& effect) override {
     fake_device_policy_effect_info_ = {device_id, effect};
   }
 
@@ -214,7 +214,7 @@
   std::unique_ptr<FlossAdminClient> client_;
 
   // For observer test inspections.
-  absl::optional<std::tuple<FlossDeviceId, absl::optional<PolicyEffect>>>
+  std::optional<std::tuple<FlossDeviceId, std::optional<PolicyEffect>>>
       fake_device_policy_effect_info_;
   std::vector<device::BluetoothUUID> fake_service_allowlist_info_;
 
diff --git a/device/bluetooth/floss/floss_advertiser_client.cc b/device/bluetooth/floss/floss_advertiser_client.cc
index 200b91a..45aba8e 100644
--- a/device/bluetooth/floss/floss_advertiser_client.cc
+++ b/device/bluetooth/floss/floss_advertiser_client.cc
@@ -236,9 +236,9 @@
 void FlossAdvertiserClient::StartAdvertisingSet(
     const AdvertisingSetParameters& params,
     const AdvertiseData& adv_data,
-    const absl::optional<AdvertiseData> scan_rsp,
-    const absl::optional<PeriodicAdvertisingParameters> periodic_params,
-    const absl::optional<AdvertiseData> periodic_data,
+    const std::optional<AdvertiseData> scan_rsp,
+    const std::optional<PeriodicAdvertisingParameters> periodic_params,
+    const std::optional<AdvertiseData> periodic_data,
     const int32_t duration,
     const int32_t max_ext_adv_events,
     StartSuccessCallback success_callback,
diff --git a/device/bluetooth/floss/floss_advertiser_client.h b/device/bluetooth/floss/floss_advertiser_client.h
index 825810b..a46cc0b6 100644
--- a/device/bluetooth/floss/floss_advertiser_client.h
+++ b/device/bluetooth/floss/floss_advertiser_client.h
@@ -181,9 +181,9 @@
   virtual void StartAdvertisingSet(
       const AdvertisingSetParameters& params,
       const AdvertiseData& adv_data,
-      const absl::optional<AdvertiseData> scan_rsp,
-      const absl::optional<PeriodicAdvertisingParameters> periodic_params,
-      const absl::optional<AdvertiseData> periodic_data,
+      const std::optional<AdvertiseData> scan_rsp,
+      const std::optional<PeriodicAdvertisingParameters> periodic_params,
+      const std::optional<AdvertiseData> periodic_data,
       const int32_t duration,
       const int32_t max_ext_adv_events,
       StartSuccessCallback success_callback,
diff --git a/device/bluetooth/floss/floss_advertiser_client_unittest.cc b/device/bluetooth/floss/floss_advertiser_client_unittest.cc
index dd1d044..9805ac2 100644
--- a/device/bluetooth/floss/floss_advertiser_client_unittest.cc
+++ b/device/bluetooth/floss/floss_advertiser_client_unittest.cc
@@ -236,8 +236,8 @@
 
   base::RunLoop run_loop0;
   advclient_->StartAdvertisingSet(
-      params, adv_data, /*scan_rsp=*/absl::nullopt,
-      /*periodic_params=*/absl::nullopt, /*periodic_data=*/absl::nullopt,
+      params, adv_data, /*scan_rsp=*/std::nullopt,
+      /*periodic_params=*/std::nullopt, /*periodic_data=*/std::nullopt,
       /*duration=*/1,
       /*max_ext_adv_events=*/1,
       base::BindLambdaForTesting([&run_loop0](AdvertiserId adv_id) {
diff --git a/device/bluetooth/floss/floss_battery_manager_client.cc b/device/bluetooth/floss/floss_battery_manager_client.cc
index 07127f7..22e53a74 100644
--- a/device/bluetooth/floss/floss_battery_manager_client.cc
+++ b/device/bluetooth/floss/floss_battery_manager_client.cc
@@ -112,9 +112,9 @@
 }
 
 void FlossBatteryManagerClient::GetBatteryInformation(
-    ResponseCallback<absl::optional<BatterySet>> callback,
+    ResponseCallback<std::optional<BatterySet>> callback,
     const FlossDeviceId& device) {
-  CallBatteryManagerMethod<absl::optional<BatterySet>>(
+  CallBatteryManagerMethod<std::optional<BatterySet>>(
       std::move(callback), battery_manager::kGetBatteryInformation,
       device.address);
 }
diff --git a/device/bluetooth/floss/floss_battery_manager_client.h b/device/bluetooth/floss/floss_battery_manager_client.h
index b6dd241..4219e4ae 100644
--- a/device/bluetooth/floss/floss_battery_manager_client.h
+++ b/device/bluetooth/floss/floss_battery_manager_client.h
@@ -75,7 +75,7 @@
 
   // Get the current cached battery info from BatteryManager.
   virtual void GetBatteryInformation(
-      ResponseCallback<absl::optional<BatterySet>> callback,
+      ResponseCallback<std::optional<BatterySet>> callback,
       const FlossDeviceId& device);
 
   // Initialize the BatteryManager client for the given adapter.
@@ -133,7 +133,7 @@
       exported_callback_manager_{battery_manager::kCallbackInterface};
 
   // Callback ID used for callbacks registered to this client.
-  absl::optional<uint32_t> battery_manager_callback_id_;
+  std::optional<uint32_t> battery_manager_callback_id_;
 
   // Signal when the client is ready to be used.
   base::OnceClosure on_ready_;
diff --git a/device/bluetooth/floss/floss_battery_manager_client_unittest.cc b/device/bluetooth/floss/floss_battery_manager_client_unittest.cc
index bd72f0c..052dbd2 100644
--- a/device/bluetooth/floss/floss_battery_manager_client_unittest.cc
+++ b/device/bluetooth/floss/floss_battery_manager_client_unittest.cc
@@ -106,7 +106,7 @@
     std::move(*cb).Run(response.get(), nullptr);
   }
 
-  void HandleGetBatteryInfo(DBusResult<absl::optional<BatterySet>> result) {
+  void HandleGetBatteryInfo(DBusResult<std::optional<BatterySet>> result) {
     callback_count_++;
   }
 
diff --git a/device/bluetooth/floss/floss_dbus_client.cc b/device/bluetooth/floss/floss_dbus_client.cc
index 8e48704..01bcd866 100644
--- a/device/bluetooth/floss/floss_dbus_client.cc
+++ b/device/bluetooth/floss/floss_dbus_client.cc
@@ -290,7 +290,7 @@
 // static
 template bool FlossDBusClient::ReadDBusParam<int32_t>(
     dbus::MessageReader* reader,
-    absl::optional<int32_t>* value);
+    std::optional<int32_t>* value);
 
 // static
 template <>
@@ -302,7 +302,7 @@
 // static
 template bool FlossDBusClient::ReadDBusParam<std::string>(
     dbus::MessageReader* reader,
-    absl::optional<std::string>* value);
+    std::optional<std::string>* value);
 
 // static
 template <>
@@ -345,7 +345,7 @@
 // static
 template bool FlossDBusClient::ReadDBusParam<device::BluetoothUUID>(
     dbus::MessageReader* reader,
-    absl::optional<device::BluetoothUUID>* uuid);
+    std::optional<device::BluetoothUUID>* uuid);
 
 // static
 template <>
@@ -371,7 +371,7 @@
 // static
 template bool FlossDBusClient::ReadDBusParam<base::ScopedFD>(
     dbus::MessageReader* reader,
-    absl::optional<base::ScopedFD>* fd);
+    std::optional<base::ScopedFD>* fd);
 
 // static
 template <>
@@ -469,7 +469,7 @@
 
 template void FlossDBusClient::WriteDBusParam<uint32_t>(
     dbus::MessageWriter* writer,
-    const absl::optional<uint32_t>& data);
+    const std::optional<uint32_t>& data);
 
 template <>
 void FlossDBusClient::WriteDBusParam(dbus::MessageWriter* writer,
diff --git a/device/bluetooth/floss/floss_dbus_client.h b/device/bluetooth/floss/floss_dbus_client.h
index 6db0978e2..b3436880 100644
--- a/device/bluetooth/floss/floss_dbus_client.h
+++ b/device/bluetooth/floss/floss_dbus_client.h
@@ -7,6 +7,7 @@
 
 #include <map>
 #include <memory>
+#include <optional>
 #include <ostream>
 #include <string>
 #include <unordered_map>
@@ -28,7 +29,6 @@
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/bluetooth_export.h"
 #include "device/bluetooth/floss/floss_version.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace floss {
 
@@ -494,7 +494,7 @@
 }
 
 template <typename T>
-const DBusTypeInfo& GetDBusTypeInfo(const absl::optional<T>*) {
+const DBusTypeInfo& GetDBusTypeInfo(const std::optional<T>*) {
   static const base::NoDestructor<DBusTypeInfo> elem_info(
       GetDBusTypeInfo(static_cast<T*>(nullptr)));
   static const base::NoDestructor<DBusTypeInfo> info{
@@ -618,7 +618,7 @@
   // Optional container type needs to be explicitly listed here.
   template <typename T>
   static void WriteDBusParam(dbus::MessageWriter* writer,
-                             const absl::optional<T>& data) {
+                             const std::optional<T>& data) {
     dbus::MessageWriter array(nullptr);
     dbus::MessageWriter dict(nullptr);
 
@@ -636,7 +636,7 @@
 
   template <typename T>
   static void WriteDBusParamIntoVariant(dbus::MessageWriter* writer,
-                                        const absl::optional<T>& data) {
+                                        const std::optional<T>& data) {
     dbus::MessageWriter variant(nullptr);
     writer->OpenVariant("a{sv}", &variant);
     WriteDBusParam(&variant, data);
@@ -730,7 +730,7 @@
   // Optional container type needs to be explicitly implemented here.
   template <typename T>
   static bool ReadDBusParam(dbus::MessageReader* reader,
-                            absl::optional<T>* value) {
+                            std::optional<T>* value) {
     dbus::MessageReader array(nullptr);
     dbus::MessageReader dict(nullptr);
 
@@ -749,7 +749,7 @@
           return false;
         }
 
-        *value = std::move(absl::optional<T>(std::move(inner)));
+        *value = std::move(std::optional<T>(std::move(inner)));
       }
     }
 
diff --git a/device/bluetooth/floss/floss_lescan_client.cc b/device/bluetooth/floss/floss_lescan_client.cc
index 909c6c1..c2017a7 100644
--- a/device/bluetooth/floss/floss_lescan_client.cc
+++ b/device/bluetooth/floss/floss_lescan_client.cc
@@ -166,8 +166,8 @@
 void FlossLEScanClient::StartScan(
     ResponseCallback<BtifStatus> callback,
     uint8_t scanner_id,
-    const absl::optional<ScanSettings>& scan_settings,
-    const absl::optional<ScanFilter>& filter) {
+    const std::optional<ScanSettings>& scan_settings,
+    const std::optional<ScanFilter>& filter) {
   if (version_ >= base::Version("0.3")) {
     CallLEScanMethod<>(std::move(callback), adapter::kStartScan, scanner_id,
                        scan_settings, filter);
diff --git a/device/bluetooth/floss/floss_lescan_client.h b/device/bluetooth/floss/floss_lescan_client.h
index 69d6295..5abbffd6 100644
--- a/device/bluetooth/floss/floss_lescan_client.h
+++ b/device/bluetooth/floss/floss_lescan_client.h
@@ -178,8 +178,8 @@
                                  uint8_t scanner_id);
   virtual void StartScan(ResponseCallback<BtifStatus> callback,
                          uint8_t scanner_id,
-                         const absl::optional<ScanSettings>& scan_settings,
-                         const absl::optional<ScanFilter>& filter);
+                         const std::optional<ScanSettings>& scan_settings,
+                         const std::optional<ScanFilter>& filter);
   virtual void StopScan(ResponseCallback<BtifStatus> callback,
                         uint8_t scanner_id);
 
@@ -205,7 +205,7 @@
   std::string service_name_;
 
  private:
-  absl::optional<uint32_t> le_scan_callback_id_;
+  std::optional<uint32_t> le_scan_callback_id_;
 
   ExportedCallbackManager<ScannerClientObserver>
       exported_scanner_callback_manager_{kScannerCallbackInterfaceName};
diff --git a/device/bluetooth/floss/floss_lescan_client_unittest.cc b/device/bluetooth/floss/floss_lescan_client_unittest.cc
index 60796f4..37d9c30 100644
--- a/device/bluetooth/floss/floss_lescan_client_unittest.cc
+++ b/device/bluetooth/floss/floss_lescan_client_unittest.cc
@@ -244,7 +244,7 @@
   std::unique_ptr<FlossLEScanClient> client_;
 
   // For observer test inspections.
-  absl::optional<std::tuple<device::BluetoothUUID, uint8_t, GattStatus>>
+  std::optional<std::tuple<device::BluetoothUUID, uint8_t, GattStatus>>
       fake_scanner_registered_info_;
   ScanResult fake_scan_result_;
 
@@ -253,11 +253,11 @@
 };
 
 static bool ReadNullOptDBusParam(dbus::MessageReader* reader) {
-  absl::optional<int32_t> param;
+  std::optional<int32_t> param;
   if (!FlossDBusClient::ReadDBusParam(reader, &param)) {
     return false;
   }
-  return param == absl::nullopt;
+  return param == std::nullopt;
 }
 
 TEST_F(FlossLEScanClientTest, TestInitExportRegisterScanner) {
@@ -428,8 +428,8 @@
                            EXPECT_EQ(ret.value(),
                                      FlossDBusClient::BtifStatus::kSuccess);
                          }),
-                     kTestScannerId, absl::nullopt /* ScanSettings */,
-                     absl::nullopt /* ScanFilter*/);
+                     kTestScannerId, std::nullopt /* ScanSettings */,
+                     std::nullopt /* ScanFilter*/);
 
   // Method of 1 parameter with no return.
   EXPECT_CALL(*object_proxy_.get(), DoCallMethodWithErrorResponse(
diff --git a/device/bluetooth/floss/floss_manager_client.cc b/device/bluetooth/floss/floss_manager_client.cc
index f27b3d2..6f5497dd 100644
--- a/device/bluetooth/floss/floss_manager_client.cc
+++ b/device/bluetooth/floss/floss_manager_client.cc
@@ -130,7 +130,7 @@
     bool enabled,
     int retry,
     int retry_wait_ms,
-    absl::optional<ResponseCallback<bool>> cb) {
+    std::optional<ResponseCallback<bool>> cb) {
   if (cb) {
     set_floss_enabled_callback_ =
         WeaklyOwnedResponseCallback<bool>::Create(std::move(*cb));
@@ -496,7 +496,7 @@
           FROM_HERE,
           base::BindOnce(&FlossManagerClient::SetFlossEnabled,
                          weak_ptr_factory_.GetWeakPtr(), target, retry - 1,
-                         retry_wait_ms, absl::nullopt),
+                         retry_wait_ms, std::nullopt),
           base::Milliseconds(retry_wait_ms));
     } else if (set_floss_enabled_callback_) {
       set_floss_enabled_callback_->Run(base::unexpected(response.error()));
@@ -538,7 +538,7 @@
         FROM_HERE,
         base::BindOnce(&FlossManagerClient::SetFlossEnabled,
                        weak_ptr_factory_.GetWeakPtr(), target, retry - 1,
-                       retry_wait_ms, absl::nullopt),
+                       retry_wait_ms, std::nullopt),
         base::Milliseconds(kSetFlossRetryDelayMs));
   } else {
     DVLOG(1) << "Floss is currently "
diff --git a/device/bluetooth/floss/floss_manager_client.h b/device/bluetooth/floss/floss_manager_client.h
index c1cfd358..532dc4d 100644
--- a/device/bluetooth/floss/floss_manager_client.h
+++ b/device/bluetooth/floss/floss_manager_client.h
@@ -161,7 +161,7 @@
   void SetFlossEnabled(bool enable,
                        int retry,
                        int retry_wait_ms,
-                       absl::optional<ResponseCallback<bool>> cb);
+                       std::optional<ResponseCallback<bool>> cb);
 
   // Make actual D-Bus call to retrieve Floss API version from daemon.
   void DoGetFlossApiVersion();
diff --git a/device/bluetooth/floss/floss_sdp_types.cc b/device/bluetooth/floss/floss_sdp_types.cc
index ed4dfc2..43f2ff6 100644
--- a/device/bluetooth/floss/floss_sdp_types.cc
+++ b/device/bluetooth/floss/floss_sdp_types.cc
@@ -53,7 +53,7 @@
 constexpr char kSdpDipRecordPropVersion[] = "version";
 constexpr char kSdpDipRecordPropPrimaryRecord[] = "primary_record";
 
-absl::optional<floss::BtSdpHeaderOverlay> GetHeaderOverlayFromSdpRecord(
+std::optional<floss::BtSdpHeaderOverlay> GetHeaderOverlayFromSdpRecord(
     const floss::BtSdpRecord& record) {
   if (absl::holds_alternative<floss::BtSdpHeaderOverlay>(record)) {
     return absl::get<floss::BtSdpHeaderOverlay>(record);
@@ -72,16 +72,16 @@
   } else if (absl::holds_alternative<floss::BtSdpDipRecord>(record)) {
     return absl::get<floss::BtSdpDipRecord>(record).hdr;
   } else {
-    return absl::nullopt;
+    return std::nullopt;
   }
 }
 
-absl::optional<device::BluetoothUUID> GetUUIDFromSdpRecord(
+std::optional<device::BluetoothUUID> GetUUIDFromSdpRecord(
     const floss::BtSdpRecord& record) {
-  absl::optional<floss::BtSdpHeaderOverlay> header =
+  std::optional<floss::BtSdpHeaderOverlay> header =
       GetHeaderOverlayFromSdpRecord(record);
   if (!header.has_value()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   return header->uuid;
 }
diff --git a/device/bluetooth/floss/floss_sdp_types.h b/device/bluetooth/floss/floss_sdp_types.h
index ee38eccb..0866fe5 100644
--- a/device/bluetooth/floss/floss_sdp_types.h
+++ b/device/bluetooth/floss/floss_sdp_types.h
@@ -92,10 +92,10 @@
                                   BtSdpOpsRecord,
                                   BtSdpSapRecord,
                                   BtSdpDipRecord>;
-absl::optional<floss::BtSdpHeaderOverlay> DEVICE_BLUETOOTH_EXPORT
+std::optional<floss::BtSdpHeaderOverlay> DEVICE_BLUETOOTH_EXPORT
 GetHeaderOverlayFromSdpRecord(const floss::BtSdpRecord& record);
 
-absl::optional<device::BluetoothUUID> DEVICE_BLUETOOTH_EXPORT
+std::optional<device::BluetoothUUID> DEVICE_BLUETOOTH_EXPORT
 GetUUIDFromSdpRecord(const floss::BtSdpRecord& record);
 
 }  // namespace floss
diff --git a/device/bluetooth/floss/floss_socket_manager.cc b/device/bluetooth/floss/floss_socket_manager.cc
index 1fe21e88..7034048f 100644
--- a/device/bluetooth/floss/floss_socket_manager.cc
+++ b/device/bluetooth/floss/floss_socket_manager.cc
@@ -90,17 +90,17 @@
         required_keys[key] = ReadDBusParamFromVariant(&dict, &socket->flags);
       } else if (key == kListeningPropPsm) {
         required_keys[key] =
-            ReadDBusParamFromVariant<absl::optional<int>>(&dict, &socket->psm);
+            ReadDBusParamFromVariant<std::optional<int>>(&dict, &socket->psm);
       } else if (key == kListeningPropChannel) {
-        required_keys[key] = ReadDBusParamFromVariant<absl::optional<int>>(
+        required_keys[key] = ReadDBusParamFromVariant<std::optional<int>>(
             &dict, &socket->channel);
       } else if (key == kListeningPropName) {
         required_keys[key] =
-            ReadDBusParamFromVariant<absl::optional<std::string>>(
-                &dict, &socket->name);
+            ReadDBusParamFromVariant<std::optional<std::string>>(&dict,
+                                                                 &socket->name);
       } else if (key == kListeningPropUuid) {
         required_keys[key] =
-            ReadDBusParamFromVariant<absl::optional<device::BluetoothUUID>>(
+            ReadDBusParamFromVariant<std::optional<device::BluetoothUUID>>(
                 &dict, &socket->uuid);
       }
     }
@@ -118,7 +118,7 @@
 template bool
 FlossDBusClient::ReadDBusParam<FlossSocketManager::FlossListeningSocket>(
     dbus::MessageReader* reader,
-    absl::optional<FlossSocketManager::FlossListeningSocket>* socket);
+    std::optional<FlossSocketManager::FlossListeningSocket>* socket);
 
 template <>
 void FlossDBusClient::WriteDBusParam(
@@ -172,13 +172,13 @@
         required_keys[key] = ReadDBusParamFromVariant(&dict, &socket->flags);
       } else if (key == kConnectingPropFd) {
         required_keys[key] =
-            ReadDBusParamFromVariant<absl::optional<base::ScopedFD>>(
+            ReadDBusParamFromVariant<std::optional<base::ScopedFD>>(
                 &dict, &socket->fd);
       } else if (key == kConnectingPropPort) {
         required_keys[key] = ReadDBusParamFromVariant(&dict, &socket->port);
       } else if (key == kConnectingPropUuid) {
         required_keys[key] =
-            ReadDBusParamFromVariant<absl::optional<device::BluetoothUUID>>(
+            ReadDBusParamFromVariant<std::optional<device::BluetoothUUID>>(
                 &dict, &socket->uuid);
       } else if (key == kConnectingPropMaxRxSize) {
         required_keys[key] =
@@ -201,7 +201,7 @@
 
 template bool FlossDBusClient::ReadDBusParam<FlossSocketManager::FlossSocket>(
     dbus::MessageReader* reader,
-    absl::optional<FlossSocketManager::FlossSocket>* socket);
+    std::optional<FlossSocketManager::FlossSocket>* socket);
 
 template <>
 void FlossDBusClient::WriteDBusParam(
@@ -391,10 +391,10 @@
 }
 
 void FlossSocketManager::ListenUsingRfcommAlt(
-    const absl::optional<std::string> name,
-    const absl::optional<device::BluetoothUUID> application_uuid,
-    const absl::optional<int> channel,
-    const absl::optional<int> flags,
+    const std::optional<std::string> name,
+    const std::optional<device::BluetoothUUID> application_uuid,
+    const std::optional<int> channel,
+    const std::optional<int> flags,
     ResponseCallback<BtifStatus> callback,
     ConnectionStateChanged ready_cb,
     ConnectionAccepted new_connection_cb) {
@@ -440,7 +440,7 @@
                                            const Security security_level,
                                            ConnectionCompleted callback) {
   if (callback_id_ == kInvalidCallbackId) {
-    std::move(callback).Run(BtifStatus::kFail, /*socket=*/absl::nullopt);
+    std::move(callback).Run(BtifStatus::kFail, /*socket=*/std::nullopt);
     return;
   }
 
@@ -459,7 +459,7 @@
                                              const Security security_level,
                                              ConnectionCompleted callback) {
   if (callback_id_ == kInvalidCallbackId) {
-    std::move(callback).Run(BtifStatus::kFail, /*socket=*/absl::nullopt);
+    std::move(callback).Run(BtifStatus::kFail, /*socket=*/std::nullopt);
     return;
   }
 
@@ -478,7 +478,7 @@
                                             const Security security_level,
                                             ConnectionCompleted callback) {
   if (callback_id_ == kInvalidCallbackId) {
-    std::move(callback).Run(BtifStatus::kFail, /*socket=*/absl::nullopt);
+    std::move(callback).Run(BtifStatus::kFail, /*socket=*/std::nullopt);
     return;
   }
 
@@ -494,7 +494,7 @@
 }
 
 void FlossSocketManager::Accept(const SocketId id,
-                                absl::optional<uint32_t> timeout_ms,
+                                std::optional<uint32_t> timeout_ms,
                                 ResponseCallback<BtifStatus> callback) {
   if (callback_id_ == kInvalidCallbackId) {
     std::move(callback).Run(base::unexpected(Error(kErrorInvalidCallback, "")));
@@ -635,7 +635,7 @@
 void FlossSocketManager::CompleteConnect(ConnectionCompleted callback,
                                          DBusResult<SocketResult> result) {
   if (!result.has_value()) {
-    std::move(callback).Run(BtifStatus::kFail, /*socket=*/absl::nullopt);
+    std::move(callback).Run(BtifStatus::kFail, /*socket=*/std::nullopt);
     return;
   }
 
@@ -647,7 +647,7 @@
         std::move(callback),
     });
   } else {
-    std::move(callback).Run(result->status, /*socket=*/absl::nullopt);
+    std::move(callback).Run(result->status, /*socket=*/std::nullopt);
   }
 }
 
@@ -741,7 +741,7 @@
   dbus::MessageReader reader(method_call);
   SocketId id;
   BtifStatus status;
-  absl::optional<FlossSocket> socket;
+  std::optional<FlossSocket> socket;
 
   if (!ReadAllDBusParams(&reader, &id, &status, &socket)) {
     std::move(response_sender)
diff --git a/device/bluetooth/floss/floss_socket_manager.h b/device/bluetooth/floss/floss_socket_manager.h
index f2d319a..2d8ebae 100644
--- a/device/bluetooth/floss/floss_socket_manager.h
+++ b/device/bluetooth/floss/floss_socket_manager.h
@@ -77,10 +77,10 @@
     SocketId id = FlossSocketManager::kInvalidSocketId;
     SocketType type;
     int flags;
-    absl::optional<int> psm;
-    absl::optional<int> channel;
-    absl::optional<std::string> name;
-    absl::optional<device::BluetoothUUID> uuid;
+    std::optional<int> psm;
+    std::optional<int> channel;
+    std::optional<std::string> name;
+    std::optional<device::BluetoothUUID> uuid;
 
     FlossListeningSocket();
     FlossListeningSocket(const FlossListeningSocket&);
@@ -95,9 +95,9 @@
     FlossDeviceId remote_device;
     SocketType type;
     int flags;
-    absl::optional<base::ScopedFD> fd;
+    std::optional<base::ScopedFD> fd;
     int port;
-    absl::optional<device::BluetoothUUID> uuid;
+    std::optional<device::BluetoothUUID> uuid;
     int max_rx_size;
     int max_tx_size;
 
@@ -130,7 +130,7 @@
 
   // Callback when a connection socket completes.
   using ConnectionCompleted =
-      base::OnceCallback<void(BtifStatus, absl::optional<FlossSocket>&&)>;
+      base::OnceCallback<void(BtifStatus, std::optional<FlossSocket>&&)>;
 
   // Error: Callback id is invalid.
   static const char kErrorInvalidCallback[];
@@ -166,10 +166,10 @@
   // variants capable of supporting a use-case, such as when manually
   // constructing SDP records for a listening socket.
   virtual void ListenUsingRfcommAlt(
-      const absl::optional<std::string> name,
-      const absl::optional<device::BluetoothUUID> application_uuid,
-      const absl::optional<int> channel,
-      const absl::optional<int> flags,
+      const std::optional<std::string> name,
+      const std::optional<device::BluetoothUUID> application_uuid,
+      const std::optional<int> channel,
+      const std::optional<int> flags,
       ResponseCallback<BtifStatus> callback,
       ConnectionStateChanged ready_cb,
       ConnectionAccepted new_connection_cb);
@@ -204,7 +204,7 @@
   // Accept new connections on |id|. If the given SocketId is not a listening
   // socket or closed, the callback will receive a failing |BtifStatus| value.
   virtual void Accept(const SocketId id,
-                      absl::optional<uint32_t> timeout_ms,
+                      std::optional<uint32_t> timeout_ms,
                       ResponseCallback<BtifStatus> callback);
 
   // Closes the socket on |id|. Only works for listening sockets. For connecting
diff --git a/device/bluetooth/floss/floss_socket_manager_unittest.cc b/device/bluetooth/floss/floss_socket_manager_unittest.cc
index 927f5e9..fb18b0f 100644
--- a/device/bluetooth/floss/floss_socket_manager_unittest.cc
+++ b/device/bluetooth/floss/floss_socket_manager_unittest.cc
@@ -168,7 +168,7 @@
   void SendOutgoingConnectionResult(
       FlossSocketManager::SocketId id,
       BtifStatus status,
-      const absl::optional<FlossSocketManager::FlossSocket>& socket,
+      const std::optional<FlossSocketManager::FlossSocket>& socket,
       dbus::ExportedObject::ResponseSender response) {
     dbus::MethodCall method_call(socket_manager::kCallbackInterface,
                                  socket_manager::kOnOutgoingConnectionResult);
@@ -383,7 +383,7 @@
         base::BindOnce(
             [](bool* complete, BtifStatus* cb_status, int* fpsm,
                BtifStatus status,
-               absl::optional<FlossSocketManager::FlossSocket>&& socket) {
+               std::optional<FlossSocketManager::FlossSocket>&& socket) {
               *complete = true;
               *cb_status = status;
               if (socket) {
@@ -397,7 +397,7 @@
     EXPECT_FALSE(callback_completed);
     EXPECT_EQ(BtifStatus::kNotReady, callback_status);
 
-    absl::optional<FlossSocketManager::FlossSocket> sock =
+    std::optional<FlossSocketManager::FlossSocket> sock =
         FlossSocketManager::FlossSocket();
     sock->id = socket_id_ctr_ - 1;
     sock->port = psm;
@@ -428,7 +428,7 @@
         base::BindOnce(
             [](bool* complete, BtifStatus* cb_status, int* fpsm,
                BtifStatus status,
-               absl::optional<FlossSocketManager::FlossSocket>&& socket) {
+               std::optional<FlossSocketManager::FlossSocket>&& socket) {
               *complete = true;
               *cb_status = status;
               if (socket) {
@@ -442,7 +442,7 @@
     EXPECT_FALSE(callback_completed);
     EXPECT_EQ(BtifStatus::kNotReady, callback_status);
 
-    absl::optional<FlossSocketManager::FlossSocket> sock =
+    std::optional<FlossSocketManager::FlossSocket> sock =
         FlossSocketManager::FlossSocket();
     sock->id = socket_id_ctr_ - 1;
     sock->port = psm;
@@ -473,7 +473,7 @@
         base::BindOnce(
             [](bool* complete, BtifStatus* cb_status, device::BluetoothUUID* uu,
                BtifStatus status,
-               absl::optional<FlossSocketManager::FlossSocket>&& socket) {
+               std::optional<FlossSocketManager::FlossSocket>&& socket) {
               *complete = true;
               *cb_status = status;
               if (socket && socket->uuid) {
@@ -487,7 +487,7 @@
     EXPECT_FALSE(callback_completed);
     EXPECT_EQ(BtifStatus::kNotReady, callback_status);
 
-    absl::optional<FlossSocketManager::FlossSocket> sock =
+    std::optional<FlossSocketManager::FlossSocket> sock =
         FlossSocketManager::FlossSocket();
     sock->id = socket_id_ctr_ - 1;
     sock->uuid = uuid;
diff --git a/device/bluetooth/server_socket_unittest.cc b/device/bluetooth/server_socket_unittest.cc
index dd7a28a..f819d6b 100644
--- a/device/bluetooth/server_socket_unittest.cc
+++ b/device/bluetooth/server_socket_unittest.cc
@@ -5,6 +5,7 @@
 #include "device/bluetooth/server_socket.h"
 
 #include <memory>
+#include <optional>
 #include <tuple>
 #include <vector>
 
@@ -22,7 +23,6 @@
 #include "net/base/io_buffer.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace bluetooth {
 
diff --git a/device/bluetooth/socket_unittest.cc b/device/bluetooth/socket_unittest.cc
index 68c321a..48cc4237 100644
--- a/device/bluetooth/socket_unittest.cc
+++ b/device/bluetooth/socket_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "device/bluetooth/socket.h"
 
+#include <optional>
 #include <tuple>
 #include <vector>
 
@@ -18,7 +19,6 @@
 #include "net/base/io_buffer.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace bluetooth {
 
diff --git a/device/bluetooth/test/bluetooth_test.cc b/device/bluetooth/test/bluetooth_test.cc
index 26a4523..687804c 100644
--- a/device/bluetooth/test/bluetooth_test.cc
+++ b/device/bluetooth/test/bluetooth_test.cc
@@ -129,18 +129,18 @@
 
 bool BluetoothTestBase::ConnectGatt(
     BluetoothDevice* device,
-    absl::optional<BluetoothUUID> service_uuid,
-    absl::optional<base::OnceCallback<void(BluetoothDevice*)>>
+    std::optional<BluetoothUUID> service_uuid,
+    std::optional<base::OnceCallback<void(BluetoothDevice*)>>
         simulate_callback) {
   base::RunLoop run_loop;
-  absl::optional<bool> result;
-  absl::optional<std::unique_ptr<BluetoothGattConnection>> connection;
+  std::optional<bool> result;
+  std::optional<std::unique_ptr<BluetoothGattConnection>> connection;
 
   device->CreateGattConnection(
       base::BindLambdaForTesting(
           [this, &result, &connection, &run_loop](
               std::unique_ptr<BluetoothGattConnection> new_connection,
-              absl::optional<BluetoothDevice::ConnectErrorCode> error_code) {
+              std::optional<BluetoothDevice::ConnectErrorCode> error_code) {
             if (error_code.has_value()) {
               result = false;
               last_connect_error_code_ = error_code.value();
@@ -170,9 +170,9 @@
   return true;
 }
 
-absl::optional<BluetoothUUID> BluetoothTestBase::GetTargetGattService(
+std::optional<BluetoothUUID> BluetoothTestBase::GetTargetGattService(
     BluetoothDevice* device) {
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 void BluetoothTestBase::SimulateDeviceBreaksConnection(
@@ -265,7 +265,7 @@
     Call expected,
     Result expected_result,
     std::unique_ptr<BluetoothGattConnection> connection,
-    absl::optional<BluetoothDevice::ConnectErrorCode> error_code) {
+    std::optional<BluetoothDevice::ConnectErrorCode> error_code) {
   Result actual_result;
   if (error_code) {
     ++error_callback_count_;
@@ -337,7 +337,7 @@
 void BluetoothTestBase::ReadValueCallback(
     Call expected,
     Result expected_result,
-    absl::optional<BluetoothGattService::GattErrorCode> error_code,
+    std::optional<BluetoothGattService::GattErrorCode> error_code,
     const std::vector<uint8_t>& value) {
   if (expected_result == Result::FAILURE) {
     if (error_code.has_value())
diff --git a/device/bluetooth/test/bluetooth_test.h b/device/bluetooth/test/bluetooth_test.h
index 8de640d..ec3a79e9 100644
--- a/device/bluetooth/test/bluetooth_test.h
+++ b/device/bluetooth/test/bluetooth_test.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -25,7 +26,6 @@
 #include "device/bluetooth/bluetooth_remote_gatt_descriptor.h"
 #include "device/bluetooth/bluetooth_remote_gatt_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -76,12 +76,12 @@
 
     ~LowEnergyDeviceData();
 
-    absl::optional<std::string> name;
+    std::optional<std::string> name;
     std::string address;
     int8_t rssi = 0;
-    absl::optional<uint8_t> flags;
+    std::optional<uint8_t> flags;
     BluetoothDevice::UUIDList advertised_uuids;
-    absl::optional<int8_t> tx_power;
+    std::optional<int8_t> tx_power;
     BluetoothDevice::ServiceDataMap service_data;
     BluetoothDevice::ManufacturerDataMap manufacturer_data;
     BluetoothTransport transport = BLUETOOTH_TRANSPORT_LE;
@@ -325,15 +325,15 @@
   // there. The callback is called to complete the GATT connection. If not
   // given, |SimulateGattConnection| is called but the callback argument lets
   // one override that.
-  bool ConnectGatt(BluetoothDevice* device,
-                   absl::optional<BluetoothUUID> service_uuid = absl::nullopt,
-                   absl::optional<base::OnceCallback<void(BluetoothDevice*)>> =
-                       absl::nullopt);
+  bool ConnectGatt(
+      BluetoothDevice* device,
+      std::optional<BluetoothUUID> service_uuid = std::nullopt,
+      std::optional<base::OnceCallback<void(BluetoothDevice*)>> = std::nullopt);
 
   // GetTargetGattService returns the specific GATT service, if any, that was
   // targeted for discovery, i.e. via the |service_uuid| argument to
   // |CreateGattConnection|.
-  virtual absl::optional<BluetoothUUID> GetTargetGattService(
+  virtual std::optional<BluetoothUUID> GetTargetGattService(
       BluetoothDevice* device);
 
   // Simulates success of implementation details of CreateGattConnection.
@@ -609,11 +609,10 @@
                                    scoped_refptr<BluetoothAdvertisement>);
   void DiscoverySessionCallback(Call expected,
                                 std::unique_ptr<BluetoothDiscoverySession>);
-  void GattConnectionCallback(
-      Call expected,
-      Result expected_result,
-      std::unique_ptr<BluetoothGattConnection>,
-      absl::optional<BluetoothDevice::ConnectErrorCode>);
+  void GattConnectionCallback(Call expected,
+                              Result expected_result,
+                              std::unique_ptr<BluetoothGattConnection>,
+                              std::optional<BluetoothDevice::ConnectErrorCode>);
   void NotifyCallback(Call expected,
                       std::unique_ptr<BluetoothGattNotifySession>);
   void NotifyCheckForPrecedingCalls(
@@ -624,14 +623,14 @@
   void ReadValueCallback(
       Call expected,
       Result expected_result,
-      absl::optional<BluetoothGattService::GattErrorCode> error_code,
+      std::optional<BluetoothGattService::GattErrorCode> error_code,
       const std::vector<uint8_t>& value);
   void ErrorCallback(Call expected);
   void AdvertisementErrorCallback(Call expected,
                                   BluetoothAdvertisement::ErrorCode error_code);
   void OnConnectCallback(Call expected,
                          Result expected_result,
-                         absl::optional<BluetoothDevice::ConnectErrorCode>);
+                         std::optional<BluetoothDevice::ConnectErrorCode>);
   void GattErrorCallback(Call expected, BluetoothGattService::GattErrorCode);
   void ReentrantStartNotifySessionSuccessCallback(
       Call expected,
diff --git a/device/bluetooth/test/bluetooth_test_bluez.cc b/device/bluetooth/test/bluetooth_test_bluez.cc
index 07106e80..e755305 100644
--- a/device/bluetooth/test/bluetooth_test_bluez.cc
+++ b/device/bluetooth/test/bluetooth_test_bluez.cc
@@ -35,7 +35,7 @@
 void GetValueCallback(
     base::OnceClosure quit_closure,
     BluetoothLocalGattService::Delegate::ValueCallback value_callback,
-    absl::optional<BluetoothGattService::GattErrorCode> error_code,
+    std::optional<BluetoothGattService::GattErrorCode> error_code,
     const std::vector<uint8_t>& value) {
   std::move(value_callback).Run(error_code, value);
   std::move(quit_closure).Run();
diff --git a/device/bluetooth/test/bluetooth_test_cast.cc b/device/bluetooth/test/bluetooth_test_cast.cc
index 9978b486..dc91554 100644
--- a/device/bluetooth/test/bluetooth_test_cast.cc
+++ b/device/bluetooth/test/bluetooth_test_cast.cc
@@ -77,7 +77,7 @@
   if (device_ordinal > 7 || device_ordinal < 1)
     return nullptr;
 
-  absl::optional<std::string> device_name = std::string(kTestDeviceName);
+  std::optional<std::string> device_name = std::string(kTestDeviceName);
   std::string device_address = kTestDeviceAddress1;
   std::vector<std::string> service_uuids;
   std::map<std::string, std::vector<uint8_t>> service_data;
@@ -105,7 +105,7 @@
       device_address = kTestDeviceAddress2;
       break;
     case 5:
-      device_name = absl::nullopt;
+      device_name = std::nullopt;
       break;
     default:
       NOTREACHED();
@@ -117,7 +117,7 @@
 
 void BluetoothTestCast::UpdateAdapter(
     const std::string& address,
-    const absl::optional<std::string>& name,
+    const std::optional<std::string>& name,
     const std::vector<std::string>& service_uuids,
     const std::map<std::string, std::vector<uint8_t>>& service_data,
     const std::map<uint16_t, std::vector<uint8_t>>& manufacturer_data) {
diff --git a/device/bluetooth/test/bluetooth_test_cast.h b/device/bluetooth/test/bluetooth_test_cast.h
index d17bbd2..9be5ea5 100644
--- a/device/bluetooth/test/bluetooth_test_cast.h
+++ b/device/bluetooth/test/bluetooth_test_cast.h
@@ -7,6 +7,7 @@
 
 #include <cstdint>
 #include <map>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -16,7 +17,6 @@
 #include "device/bluetooth/bluetooth_local_gatt_characteristic.h"
 #include "device/bluetooth/bluetooth_local_gatt_descriptor.h"
 #include "device/bluetooth/test/bluetooth_test.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -40,7 +40,7 @@
 
   void UpdateAdapter(
       const std::string& address,
-      const absl::optional<std::string>& name,
+      const std::optional<std::string>& name,
       const std::vector<std::string>& service_uuids,
       const std::map<std::string, std::vector<uint8_t>>& service_data,
       const std::map<uint16_t, std::vector<uint8_t>>& manufacturer_data);
diff --git a/device/bluetooth/test/bluetooth_test_win.cc b/device/bluetooth/test/bluetooth_test_win.cc
index 04edd9e..dcc8565 100644
--- a/device/bluetooth/test/bluetooth_test_win.cc
+++ b/device/bluetooth/test/bluetooth_test_win.cc
@@ -281,7 +281,7 @@
   NOTREACHED_NORETURN();
 }
 
-absl::optional<BluetoothUUID> BluetoothTestWin::GetTargetGattService(
+std::optional<BluetoothUUID> BluetoothTestWin::GetTargetGattService(
     BluetoothDevice* device) {
   auto* const ble_device =
       static_cast<TestBluetoothDeviceWinrt*>(device)->ble_device();
diff --git a/device/bluetooth/test/bluetooth_test_win.h b/device/bluetooth/test/bluetooth_test_win.h
index 5fc41262..6ce67cb 100644
--- a/device/bluetooth/test/bluetooth_test_win.h
+++ b/device/bluetooth/test/bluetooth_test_win.h
@@ -5,10 +5,9 @@
 #ifndef DEVICE_BLUETOOTH_TEST_BLUETOOTH_TEST_WIN_H_
 #define DEVICE_BLUETOOTH_TEST_BLUETOOTH_TEST_WIN_H_
 
-#include "device/bluetooth/test/bluetooth_test.h"
-
 #include <Windows.Devices.Enumeration.h>
 
+#include <optional>
 #include <string>
 #include <string_view>
 #include <vector>
@@ -21,7 +20,7 @@
 #include "base/win/scoped_winrt_initializer.h"
 #include "device/bluetooth/bluetooth_classic_win_fake.h"
 #include "device/bluetooth/bluetooth_task_manager_win.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "device/bluetooth/test/bluetooth_test.h"
 
 namespace device {
 
@@ -39,7 +38,7 @@
   bool DenyPermission() override;
   void StartLowEnergyDiscoverySession() override;
   BluetoothDevice* SimulateLowEnergyDevice(int device_ordinal) override;
-  absl::optional<BluetoothUUID> GetTargetGattService(
+  std::optional<BluetoothUUID> GetTargetGattService(
       BluetoothDevice* device) override;
   void SimulateGattConnection(BluetoothDevice* device) override;
   void SimulateStatusChangeToDisconnect(BluetoothDevice* device) override;
diff --git a/device/bluetooth/test/fake_bluetooth_le_advertisement_winrt.cc b/device/bluetooth/test/fake_bluetooth_le_advertisement_winrt.cc
index 49e29002..7c21e71 100644
--- a/device/bluetooth/test/fake_bluetooth_le_advertisement_winrt.cc
+++ b/device/bluetooth/test/fake_bluetooth_le_advertisement_winrt.cc
@@ -116,10 +116,10 @@
     default;
 
 FakeBluetoothLEAdvertisementWinrt::FakeBluetoothLEAdvertisementWinrt(
-    absl::optional<std::string> local_name,
-    absl::optional<uint8_t> flags,
+    std::optional<std::string> local_name,
+    std::optional<uint8_t> flags,
     BluetoothDevice::UUIDList advertised_uuids,
-    absl::optional<int8_t> tx_power,
+    std::optional<int8_t> tx_power,
     BluetoothDevice::ServiceDataMap service_data,
     BluetoothDevice::ManufacturerDataMap manufacturer_data)
     : local_name_(std::move(local_name)),
diff --git a/device/bluetooth/test/fake_bluetooth_le_advertisement_winrt.h b/device/bluetooth/test/fake_bluetooth_le_advertisement_winrt.h
index 68490948..ebd4da3 100644
--- a/device/bluetooth/test/fake_bluetooth_le_advertisement_winrt.h
+++ b/device/bluetooth/test/fake_bluetooth_le_advertisement_winrt.h
@@ -5,16 +5,15 @@
 #ifndef DEVICE_BLUETOOTH_TEST_FAKE_BLUETOOTH_LE_ADVERTISEMENT_WINRT_H_
 #define DEVICE_BLUETOOTH_TEST_FAKE_BLUETOOTH_LE_ADVERTISEMENT_WINRT_H_
 
+#include <stdint.h>
 #include <windows.devices.bluetooth.advertisement.h>
 #include <wrl/client.h>
 #include <wrl/implements.h>
 
-#include <stdint.h>
-
+#include <optional>
 #include <string>
 
 #include "device/bluetooth/bluetooth_device.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -27,10 +26,10 @@
  public:
   FakeBluetoothLEAdvertisementWinrt();
   FakeBluetoothLEAdvertisementWinrt(
-      absl::optional<std::string> local_name,
-      absl::optional<uint8_t> flags,
+      std::optional<std::string> local_name,
+      std::optional<uint8_t> flags,
       BluetoothDevice::UUIDList advertised_uuids,
-      absl::optional<int8_t> tx_power,
+      std::optional<int8_t> tx_power,
       BluetoothDevice::ServiceDataMap service_data,
       BluetoothDevice::ManufacturerDataMap manufacturer_data);
 
@@ -72,10 +71,10 @@
               BluetoothLEAdvertisementDataSection*>** section_list) override;
 
  private:
-  absl::optional<std::string> local_name_;
-  absl::optional<uint8_t> flags_;
+  std::optional<std::string> local_name_;
+  std::optional<uint8_t> flags_;
   BluetoothDevice::UUIDList advertised_uuids_;
-  absl::optional<int8_t> tx_power_;
+  std::optional<int8_t> tx_power_;
   BluetoothDevice::ServiceDataMap service_data_;
   BluetoothDevice::ManufacturerDataMap manufacturer_data_;
 };
diff --git a/device/bluetooth/test/fake_bluetooth_le_device_winrt.cc b/device/bluetooth/test/fake_bluetooth_le_device_winrt.cc
index 8ff04df..09710fb 100644
--- a/device/bluetooth/test/fake_bluetooth_le_device_winrt.cc
+++ b/device/bluetooth/test/fake_bluetooth_le_device_winrt.cc
@@ -276,10 +276,10 @@
           DevicePairingKinds_ConfirmPinMatch, display_pin));
 }
 
-absl::optional<BluetoothUUID> FakeBluetoothLEDeviceWinrt::GetTargetGattService()
+std::optional<BluetoothUUID> FakeBluetoothLEDeviceWinrt::GetTargetGattService()
     const {
   if (!service_uuid_)
-    return absl::nullopt;
+    return std::nullopt;
   return BluetoothUUID(*service_uuid_);
 }
 
diff --git a/device/bluetooth/test/fake_bluetooth_le_device_winrt.h b/device/bluetooth/test/fake_bluetooth_le_device_winrt.h
index 2858a89..d53d94a1 100644
--- a/device/bluetooth/test/fake_bluetooth_le_device_winrt.h
+++ b/device/bluetooth/test/fake_bluetooth_le_device_winrt.h
@@ -136,7 +136,7 @@
   void SimulatePairingPinCode(std::string pin_code);
   void SimulateConfirmOnly();
   void SimulateDisplayPin(std::string_view display_pin);
-  absl::optional<BluetoothUUID> GetTargetGattService() const;
+  std::optional<BluetoothUUID> GetTargetGattService() const;
   void SimulateGattConnection();
   void SimulateGattConnectionError(
       BluetoothDevice::ConnectErrorCode error_code);
@@ -159,7 +159,7 @@
  private:
   raw_ptr<BluetoothTestWinrt> bluetooth_test_winrt_ = nullptr;
   uint32_t reference_count_ = 1u;
-  absl::optional<std::string> name_;
+  std::optional<std::string> name_;
 
   ABI::Windows::Devices::Bluetooth::BluetoothConnectionStatus status_ =
       ABI::Windows::Devices::Bluetooth::BluetoothConnectionStatus_Disconnected;
@@ -183,7 +183,7 @@
               IGattDeviceServicesResult>)>
       gatt_services_callback_;
   // Contains the last GUID passed to GetGattServicesForUuidAsync.
-  absl::optional<GUID> service_uuid_;
+  std::optional<GUID> service_uuid_;
 
   std::vector<Microsoft::WRL::ComPtr<FakeGattDeviceServiceWinrt>>
       fake_services_;
diff --git a/device/bluetooth/test/fake_central.cc b/device/bluetooth/test/fake_central.cc
index a7857c77..493c2fe 100644
--- a/device/bluetooth/test/fake_central.cc
+++ b/device/bluetooth/test/fake_central.cc
@@ -101,16 +101,16 @@
     observer.DeviceAdvertisementReceived(
         scan_result_ptr->device_address, scan_record->name, scan_record->name,
         scan_result_ptr->rssi, scan_record->tx_power->value,
-        absl::nullopt, /* TODO(crbug.com/588083) Implement appearance */
+        std::nullopt, /* TODO(crbug.com/588083) Implement appearance */
         uuids, service_data, manufacturer_data);
   }
 
   fake_peripheral->SetName(std::move(scan_record->name));
   fake_peripheral->UpdateAdvertisementData(
-      scan_result_ptr->rssi, absl::nullopt /* flags */, uuids,
+      scan_result_ptr->rssi, std::nullopt /* flags */, uuids,
       scan_record->tx_power->has_value
-          ? absl::make_optional(scan_record->tx_power->value)
-          : absl::nullopt,
+          ? std::make_optional(scan_record->tx_power->value)
+          : std::nullopt,
       service_data, manufacturer_data);
 
   if (is_new_device) {
@@ -236,7 +236,7 @@
                                  AddFakeServiceCallback callback) {
   FakePeripheral* fake_peripheral = GetFakePeripheral(peripheral_address);
   if (fake_peripheral == nullptr) {
-    std::move(callback).Run(absl::nullopt);
+    std::move(callback).Run(std::nullopt);
     return;
   }
 
@@ -263,7 +263,7 @@
   FakeRemoteGattService* fake_remote_gatt_service =
       GetFakeRemoteGattService(peripheral_address, service_id);
   if (fake_remote_gatt_service == nullptr) {
-    std::move(callback).Run(absl::nullopt);
+    std::move(callback).Run(std::nullopt);
     return;
   }
 
@@ -297,7 +297,7 @@
       GetFakeRemoteGattCharacteristic(peripheral_address, service_id,
                                       characteristic_id);
   if (fake_remote_gatt_characteristic == nullptr) {
-    std::move(callback).Run(absl::nullopt);
+    std::move(callback).Run(std::nullopt);
     return;
   }
 
@@ -324,7 +324,7 @@
 
 void FakeCentral::SetNextReadCharacteristicResponse(
     uint16_t gatt_code,
-    const absl::optional<std::vector<uint8_t>>& value,
+    const std::optional<std::vector<uint8_t>>& value,
     const std::string& characteristic_id,
     const std::string& service_id,
     const std::string& peripheral_address,
@@ -421,7 +421,7 @@
       GetFakeRemoteGattCharacteristic(peripheral_address, service_id,
                                       characteristic_id);
   if (fake_remote_gatt_characteristic == nullptr) {
-    std::move(callback).Run(false, absl::nullopt, mojom::WriteType::kNone);
+    std::move(callback).Run(false, std::nullopt, mojom::WriteType::kNone);
     return;
   }
 
@@ -432,7 +432,7 @@
 
 void FakeCentral::SetNextReadDescriptorResponse(
     uint16_t gatt_code,
-    const absl::optional<std::vector<uint8_t>>& value,
+    const std::optional<std::vector<uint8_t>>& value,
     const std::string& descriptor_id,
     const std::string& characteristic_id,
     const std::string& service_id,
@@ -479,7 +479,7 @@
       GetFakeRemoteGattDescriptor(peripheral_address, service_id,
                                   characteristic_id, descriptor_id);
   if (!fake_remote_gatt_descriptor) {
-    std::move(callback).Run(false, absl::nullopt);
+    std::move(callback).Run(false, std::nullopt);
     return;
   }
 
@@ -615,7 +615,7 @@
 }
 void FakeCentral::ConnectDevice(
     const std::string& address,
-    const absl::optional<device::BluetoothDevice::AddressType>& address_type,
+    const std::optional<device::BluetoothDevice::AddressType>& address_type,
     ConnectDeviceCallback callback,
     ConnectDeviceErrorCallback error_callback) {
   NOTREACHED();
diff --git a/device/bluetooth/test/fake_central.h b/device/bluetooth/test/fake_central.h
index 74c50b56c..2657645 100644
--- a/device/bluetooth/test/fake_central.h
+++ b/device/bluetooth/test/fake_central.h
@@ -94,7 +94,7 @@
                             RemoveFakeDescriptorCallback callback) override;
   void SetNextReadCharacteristicResponse(
       uint16_t gatt_code,
-      const absl::optional<std::vector<uint8_t>>& value,
+      const std::optional<std::vector<uint8_t>>& value,
       const std::string& characteristic_id,
       const std::string& service_id,
       const std::string& peripheral_address,
@@ -128,7 +128,7 @@
       GetLastWrittenCharacteristicValueCallback callback) override;
   void SetNextReadDescriptorResponse(
       uint16_t gatt_code,
-      const absl::optional<std::vector<uint8_t>>& value,
+      const std::optional<std::vector<uint8_t>>& value,
       const std::string& descriptor_id,
       const std::string& characteristic_id,
       const std::string& service_id,
@@ -195,7 +195,7 @@
                         AdvertisementErrorCallback error_callback) override;
   void ConnectDevice(
       const std::string& address,
-      const absl::optional<device::BluetoothDevice::AddressType>& address_type,
+      const std::optional<device::BluetoothDevice::AddressType>& address_type,
       ConnectDeviceCallback callback,
       ConnectDeviceErrorCallback error_callback) override;
 #endif
diff --git a/device/bluetooth/test/fake_device_information_custom_pairing_winrt.h b/device/bluetooth/test/fake_device_information_custom_pairing_winrt.h
index 84051f4a..4bc6198 100644
--- a/device/bluetooth/test/fake_device_information_custom_pairing_winrt.h
+++ b/device/bluetooth/test/fake_device_information_custom_pairing_winrt.h
@@ -9,13 +9,13 @@
 #include <wrl/client.h>
 #include <wrl/implements.h>
 
+#include <optional>
 #include <string>
 #include <string_view>
 
 #include "base/functional/callback.h"
 #include "base/memory/scoped_refptr.h"
 #include "device/bluetooth/test/fake_device_information_pairing_winrt.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
 class SequencedTaskRunner;
@@ -95,7 +95,7 @@
 
  private:
   Microsoft::WRL::ComPtr<FakeDeviceInformationPairingWinrt> pairing_;
-  const absl::optional<std::string> pin_;
+  const std::optional<std::string> pin_;
   std::string accepted_pin_;
   bool confirmed_ = false;
 
diff --git a/device/bluetooth/test/fake_peripheral.cc b/device/bluetooth/test/fake_peripheral.cc
index 607328e..33ff6a11 100644
--- a/device/bluetooth/test/fake_peripheral.cc
+++ b/device/bluetooth/test/fake_peripheral.cc
@@ -31,7 +31,7 @@
 
 FakePeripheral::~FakePeripheral() = default;
 
-void FakePeripheral::SetName(absl::optional<std::string> name) {
+void FakePeripheral::SetName(std::optional<std::string> name) {
   name_ = std::move(name);
 }
 
@@ -169,7 +169,7 @@
   return 0;
 }
 
-absl::optional<std::string> FakePeripheral::GetName() const {
+std::optional<std::string> FakePeripheral::GetName() const {
   return name_;
 }
 
@@ -294,13 +294,13 @@
 
 void FakePeripheral::CreateGattConnection(
     GattConnectionCallback callback,
-    absl::optional<device::BluetoothUUID> service_uuid) {
+    std::optional<device::BluetoothUUID> service_uuid) {
   create_gatt_connection_callbacks_.push_back(std::move(callback));
 
   // TODO(crbug.com/728870): Stop overriding CreateGattConnection once
   // IsGattConnected() is fixed. See issue for more details.
   if (gatt_connected_)
-    return DidConnectGatt(/*error_code=*/absl::nullopt);
+    return DidConnectGatt(/*error_code=*/std::nullopt);
 
   CreateGattConnectionImpl(std::move(service_uuid));
 }
@@ -331,7 +331,7 @@
 }
 
 void FakePeripheral::CreateGattConnectionImpl(
-    absl::optional<device::BluetoothUUID>) {
+    std::optional<device::BluetoothUUID>) {
   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
       FROM_HERE, base::BindOnce(&FakePeripheral::DispatchConnectionResponse,
                                 weak_ptr_factory_.GetWeakPtr()));
@@ -345,7 +345,7 @@
 
   if (code == mojom::kHCISuccess) {
     gatt_connected_ = true;
-    DidConnectGatt(/*error_code=*/absl::nullopt);
+    DidConnectGatt(/*error_code=*/std::nullopt);
   } else if (code == mojom::kHCIConnectionTimeout) {
     DidConnectGatt(ERROR_FAILED);
   } else {
diff --git a/device/bluetooth/test/fake_peripheral.h b/device/bluetooth/test/fake_peripheral.h
index bb138b3..b307ff55 100644
--- a/device/bluetooth/test/fake_peripheral.h
+++ b/device/bluetooth/test/fake_peripheral.h
@@ -4,6 +4,7 @@
 #ifndef DEVICE_BLUETOOTH_TEST_FAKE_PERIPHERAL_H_
 #define DEVICE_BLUETOOTH_TEST_FAKE_PERIPHERAL_H_
 
+#include <optional>
 #include <string>
 
 #include "base/compiler_specific.h"
@@ -12,7 +13,6 @@
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/test/fake_central.h"
 #include "device/bluetooth/test/fake_remote_gatt_service.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 class BluetoothUUID;
@@ -34,7 +34,7 @@
   ~FakePeripheral() override;
 
   // Changes the name of the device.
-  void SetName(absl::optional<std::string> name);
+  void SetName(std::optional<std::string> name);
 
   // Set it to indicate if the system has connected to the Peripheral outside of
   // the Bluetooth interface e.g. the user connected to the device through
@@ -87,7 +87,7 @@
   uint16_t GetProductID() const override;
   uint16_t GetDeviceID() const override;
   uint16_t GetAppearance() const override;
-  absl::optional<std::string> GetName() const override;
+  std::optional<std::string> GetName() const override;
   std::u16string GetNameForDisplay() const override;
   bool IsPaired() const override;
 #if BUILDFLAG(IS_CHROMEOS)
@@ -128,7 +128,7 @@
       ConnectToServiceErrorCallback error_callback) override;
   void CreateGattConnection(
       GattConnectionCallback callback,
-      absl::optional<device::BluetoothUUID> service_uuid) override;
+      std::optional<device::BluetoothUUID> service_uuid) override;
   bool IsGattServicesDiscoveryComplete() const override;
 #if BUILDFLAG(IS_CHROMEOS)
   void ExecuteWrite(base::OnceClosure callback,
@@ -138,7 +138,7 @@
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
  protected:
-  void CreateGattConnectionImpl(absl::optional<device::BluetoothUUID>) override;
+  void CreateGattConnectionImpl(std::optional<device::BluetoothUUID>) override;
   void DisconnectGatt() override;
 
  private:
@@ -146,7 +146,7 @@
   void DispatchDiscoveryResponse();
 
   const std::string address_;
-  absl::optional<std::string> name_;
+  std::optional<std::string> name_;
   // True when the system has connected to the device outside of the Bluetooth
   // interface e.g. the user connected to the device through system settings.
   bool system_connected_;
@@ -164,10 +164,10 @@
 
   // Used to decide which callback should be called when
   // CreateGattConnection is called.
-  absl::optional<uint16_t> next_connection_response_;
+  std::optional<uint16_t> next_connection_response_;
 
   // Used to decide if the GattServicesDiscovered method is called.
-  absl::optional<uint16_t> next_discovery_response_;
+  std::optional<uint16_t> next_discovery_response_;
 
   // Mutable because IsGattServicesDiscoveryComplete needs to post a task but
   // is const.
diff --git a/device/bluetooth/test/fake_read_response.cc b/device/bluetooth/test/fake_read_response.cc
index 9dc09e1e..4622c19 100644
--- a/device/bluetooth/test/fake_read_response.cc
+++ b/device/bluetooth/test/fake_read_response.cc
@@ -8,7 +8,7 @@
 
 FakeReadResponse::FakeReadResponse(
     uint16_t gatt_code,
-    const absl::optional<std::vector<uint8_t>>& value)
+    const std::optional<std::vector<uint8_t>>& value)
     : gatt_code_(gatt_code), value_(value) {}
 
 FakeReadResponse::~FakeReadResponse() = default;
diff --git a/device/bluetooth/test/fake_read_response.h b/device/bluetooth/test/fake_read_response.h
index c897e289..9351c66 100644
--- a/device/bluetooth/test/fake_read_response.h
+++ b/device/bluetooth/test/fake_read_response.h
@@ -5,10 +5,9 @@
 #define DEVICE_BLUETOOTH_TEST_FAKE_READ_RESPONSE_H_
 
 #include <cstdint>
+#include <optional>
 #include <vector>
 
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
 namespace bluetooth {
 
 // Holds the necessary values for dispatching a fake read response.
@@ -17,7 +16,7 @@
 class FakeReadResponse {
  public:
   FakeReadResponse(uint16_t gatt_code,
-                   const absl::optional<std::vector<uint8_t>>& value);
+                   const std::optional<std::vector<uint8_t>>& value);
 
   FakeReadResponse(const FakeReadResponse&) = delete;
   FakeReadResponse& operator=(const FakeReadResponse&) = delete;
@@ -25,11 +24,11 @@
   ~FakeReadResponse();
 
   uint16_t gatt_code() { return gatt_code_; }
-  const absl::optional<std::vector<uint8_t>>& value() { return value_; }
+  const std::optional<std::vector<uint8_t>>& value() { return value_; }
 
  private:
   uint16_t gatt_code_;
-  absl::optional<std::vector<uint8_t>> value_;
+  std::optional<std::vector<uint8_t>> value_;
 };
 
 }  // namespace bluetooth
diff --git a/device/bluetooth/test/fake_remote_gatt_characteristic.cc b/device/bluetooth/test/fake_remote_gatt_characteristic.cc
index 4e1811db..a925a7e 100644
--- a/device/bluetooth/test/fake_remote_gatt_characteristic.cc
+++ b/device/bluetooth/test/fake_remote_gatt_characteristic.cc
@@ -4,6 +4,7 @@
 
 #include "device/bluetooth/test/fake_remote_gatt_characteristic.h"
 
+#include <optional>
 #include <utility>
 #include <vector>
 
@@ -13,7 +14,6 @@
 #include "base/task/single_thread_task_runner.h"
 #include "device/bluetooth/public/cpp/bluetooth_uuid.h"
 #include "device/bluetooth/test/fake_read_response.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace bluetooth {
 
@@ -66,7 +66,7 @@
 
 void FakeRemoteGattCharacteristic::SetNextReadResponse(
     uint16_t gatt_code,
-    const absl::optional<std::vector<uint8_t>>& value) {
+    const std::optional<std::vector<uint8_t>>& value) {
   DCHECK(!next_read_response_);
   next_read_response_.emplace(gatt_code, value);
 }
@@ -233,14 +233,14 @@
     ValueCallback callback) {
   DCHECK(next_read_response_);
   uint16_t gatt_code = next_read_response_->gatt_code();
-  absl::optional<std::vector<uint8_t>> value = next_read_response_->value();
+  std::optional<std::vector<uint8_t>> value = next_read_response_->value();
   next_read_response_.reset();
 
   switch (gatt_code) {
     case mojom::kGATTSuccess:
       DCHECK(value);
       value_ = std::move(value.value());
-      std::move(callback).Run(absl::nullopt, value_);
+      std::move(callback).Run(std::nullopt, value_);
       break;
     case mojom::kGATTInvalidHandle:
       DCHECK(!value);
diff --git a/device/bluetooth/test/fake_remote_gatt_characteristic.h b/device/bluetooth/test/fake_remote_gatt_characteristic.h
index e4625b33..5ee8b9d 100644
--- a/device/bluetooth/test/fake_remote_gatt_characteristic.h
+++ b/device/bluetooth/test/fake_remote_gatt_characteristic.h
@@ -49,7 +49,7 @@
   // its success callback with |value|. Otherwise it will call its error
   // callback.
   void SetNextReadResponse(uint16_t gatt_code,
-                           const absl::optional<std::vector<uint8_t>>& value);
+                           const std::optional<std::vector<uint8_t>>& value);
 
   // If |gatt_code| is mojom::kGATTSuccess the next write with response request
   // will call its success callback. Otherwise it will call its error callback.
@@ -71,7 +71,7 @@
 
   // Returns the last successfully written value to the characteristic. Returns
   // nullopt if no value has been written yet.
-  const absl::optional<std::vector<uint8_t>>& last_written_value() {
+  const std::optional<std::vector<uint8_t>>& last_written_value() {
     return last_written_value_;
   }
 
@@ -142,26 +142,26 @@
   std::vector<uint8_t> value_;
 
   // Last successfully written value to the characteristic.
-  absl::optional<std::vector<uint8_t>> last_written_value_;
+  std::optional<std::vector<uint8_t>> last_written_value_;
 
   // Write type of last successfully written value to the characteristic.
   mojom::WriteType last_write_type_ = mojom::WriteType::kNone;
 
   // Used to decide which callback should be called when
   // ReadRemoteCharacteristic is called.
-  absl::optional<FakeReadResponse> next_read_response_;
+  std::optional<FakeReadResponse> next_read_response_;
 
   // Used to decide which callback should be called when
   // WriteRemoteCharacteristic is called.
-  absl::optional<uint16_t> next_write_response_;
+  std::optional<uint16_t> next_write_response_;
 
   // Used to decide which callback should be called when
   // SubscribeToNotifications is called.
-  absl::optional<uint16_t> next_subscribe_to_notifications_response_;
+  std::optional<uint16_t> next_subscribe_to_notifications_response_;
 
   // Used to decide which callback should be called when
   // UnsubscribeFromNotifications is called.
-  absl::optional<uint16_t> next_unsubscribe_from_notifications_response_;
+  std::optional<uint16_t> next_unsubscribe_from_notifications_response_;
 
   size_t last_descriptor_id_;
 
diff --git a/device/bluetooth/test/fake_remote_gatt_descriptor.cc b/device/bluetooth/test/fake_remote_gatt_descriptor.cc
index 43aa9cdf..d530e1b 100644
--- a/device/bluetooth/test/fake_remote_gatt_descriptor.cc
+++ b/device/bluetooth/test/fake_remote_gatt_descriptor.cc
@@ -24,7 +24,7 @@
 
 void FakeRemoteGattDescriptor::SetNextReadResponse(
     uint16_t gatt_code,
-    const absl::optional<std::vector<uint8_t>>& value) {
+    const std::optional<std::vector<uint8_t>>& value) {
   DCHECK(!next_read_response_);
   next_read_response_.emplace(gatt_code, value);
 }
@@ -83,14 +83,14 @@
 void FakeRemoteGattDescriptor::DispatchReadResponse(ValueCallback callback) {
   DCHECK(next_read_response_);
   uint16_t gatt_code = next_read_response_->gatt_code();
-  absl::optional<std::vector<uint8_t>> value = next_read_response_->value();
+  std::optional<std::vector<uint8_t>> value = next_read_response_->value();
   next_read_response_.reset();
 
   switch (gatt_code) {
     case mojom::kGATTSuccess:
       DCHECK(value);
       value_ = std::move(value.value());
-      std::move(callback).Run(/*error_code=*/absl::nullopt, value_);
+      std::move(callback).Run(/*error_code=*/std::nullopt, value_);
       break;
     case mojom::kGATTInvalidHandle:
       DCHECK(!value);
diff --git a/device/bluetooth/test/fake_remote_gatt_descriptor.h b/device/bluetooth/test/fake_remote_gatt_descriptor.h
index c4546af..8bd83ef 100644
--- a/device/bluetooth/test/fake_remote_gatt_descriptor.h
+++ b/device/bluetooth/test/fake_remote_gatt_descriptor.h
@@ -4,6 +4,7 @@
 #ifndef DEVICE_BLUETOOTH_TEST_FAKE_REMOTE_GATT_DESCRIPTOR_H_
 #define DEVICE_BLUETOOTH_TEST_FAKE_REMOTE_GATT_DESCRIPTOR_H_
 
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -13,7 +14,6 @@
 #include "device/bluetooth/bluetooth_remote_gatt_descriptor.h"
 #include "device/bluetooth/public/cpp/bluetooth_uuid.h"
 #include "device/bluetooth/test/fake_read_response.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace bluetooth {
 
@@ -34,7 +34,7 @@
   // its success callback with |value|. Otherwise it will call its error
   // callback.
   void SetNextReadResponse(uint16_t gatt_code,
-                           const absl::optional<std::vector<uint8_t>>& value);
+                           const std::optional<std::vector<uint8_t>>& value);
 
   // If |gatt_code| is mojom::kGATTSuccess the next write request will call its
   // success callback. Otherwise it will call its error callback.
@@ -42,7 +42,7 @@
 
   // Returns the last successfully written value to the descriptor. Returns
   // nullopt if no value has been written yet.
-  const absl::optional<std::vector<uint8_t>>& last_written_value() {
+  const std::optional<std::vector<uint8_t>>& last_written_value() {
     return last_written_value_;
   }
 
@@ -76,15 +76,15 @@
   std::vector<uint8_t> value_;
 
   // Last successfully written value to the descriptor.
-  absl::optional<std::vector<uint8_t>> last_written_value_;
+  std::optional<std::vector<uint8_t>> last_written_value_;
 
   // Used to decide which callback should be called when
   // ReadRemoteDescriptor is called.
-  absl::optional<FakeReadResponse> next_read_response_;
+  std::optional<FakeReadResponse> next_read_response_;
 
   // Used to decide which callback should be called when WriteRemoteDescriptor
   // is called.
-  absl::optional<uint16_t> next_write_response_;
+  std::optional<uint16_t> next_write_response_;
 
   base::WeakPtrFactory<FakeRemoteGattDescriptor> weak_ptr_factory_{this};
 };
diff --git a/device/bluetooth/test/mock_bluetooth_adapter.h b/device/bluetooth/test/mock_bluetooth_adapter.h
index 1e205c8..ce87405 100644
--- a/device/bluetooth/test/mock_bluetooth_adapter.h
+++ b/device/bluetooth/test/mock_bluetooth_adapter.h
@@ -145,7 +145,7 @@
   MOCK_METHOD4(
       ConnectDevice,
       void(const std::string& address,
-           const absl::optional<BluetoothDevice::AddressType>& address_type,
+           const std::optional<BluetoothDevice::AddressType>& address_type,
            ConnectDeviceCallback callback,
            ConnectDeviceErrorCallback error_callback));
 
diff --git a/device/bluetooth/test/mock_bluetooth_device.cc b/device/bluetooth/test/mock_bluetooth_device.cc
index c26d6ff3..1cb8f49 100644
--- a/device/bluetooth/test/mock_bluetooth_device.cc
+++ b/device/bluetooth/test/mock_bluetooth_device.cc
@@ -24,7 +24,7 @@
                                          bool connected)
     : BluetoothDevice(adapter),
       bluetooth_class_(bluetooth_class),
-      name_(name ? absl::optional<std::string>(name) : absl::nullopt),
+      name_(name ? std::optional<std::string>(name) : std::nullopt),
       address_(address),
       connected_(connected),
       paired_(initially_paired) {
diff --git a/device/bluetooth/test/mock_bluetooth_device.h b/device/bluetooth/test/mock_bluetooth_device.h
index 6ffe31bdd..0d0ab1c6e 100644
--- a/device/bluetooth/test/mock_bluetooth_device.h
+++ b/device/bluetooth/test/mock_bluetooth_device.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -17,7 +18,6 @@
 #include "device/bluetooth/public/cpp/bluetooth_uuid.h"
 #include "device/bluetooth/test/mock_bluetooth_gatt_service.h"
 #include "testing/gmock/include/gmock/gmock.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -44,7 +44,7 @@
   MOCK_CONST_METHOD0(GetProductID, uint16_t());
   MOCK_CONST_METHOD0(GetDeviceID, uint16_t());
   MOCK_CONST_METHOD0(GetAppearance, uint16_t());
-  MOCK_CONST_METHOD0(GetName, absl::optional<std::string>());
+  MOCK_CONST_METHOD0(GetName, std::optional<std::string>());
   MOCK_CONST_METHOD0(GetNameForDisplay, std::u16string());
   MOCK_CONST_METHOD0(GetDeviceType, BluetoothDeviceType());
   MOCK_CONST_METHOD0(IsPaired, bool());
@@ -56,8 +56,8 @@
   MOCK_CONST_METHOD0(IsConnectable, bool());
   MOCK_CONST_METHOD0(IsConnecting, bool());
   MOCK_CONST_METHOD0(GetUUIDs, UUIDSet());
-  MOCK_CONST_METHOD0(GetInquiryRSSI, absl::optional<int8_t>());
-  MOCK_CONST_METHOD0(GetInquiryTxPower, absl::optional<int8_t>());
+  MOCK_CONST_METHOD0(GetInquiryRSSI, std::optional<int8_t>());
+  MOCK_CONST_METHOD0(GetInquiryTxPower, std::optional<int8_t>());
   MOCK_CONST_METHOD0(ExpectingPinCode, bool());
   MOCK_CONST_METHOD0(ExpectingPasskey, bool());
   MOCK_CONST_METHOD0(ExpectingConfirmation, bool());
@@ -98,7 +98,7 @@
                     ConnectToServiceErrorCallback error_callback));
   MOCK_METHOD2(CreateGattConnection,
                void(GattConnectionCallback callback,
-                    absl::optional<BluetoothUUID> service_uuid));
+                    std::optional<BluetoothUUID> service_uuid));
   MOCK_CONST_METHOD0(IsGattServicesDiscoveryComplete, bool());
 
   MOCK_CONST_METHOD0(GetGattServices,
@@ -106,7 +106,7 @@
   MOCK_CONST_METHOD1(GetGattService,
                      BluetoothRemoteGattService*(const std::string&));
   MOCK_METHOD1(CreateGattConnectionImpl,
-               void(absl::optional<BluetoothUUID> service_uuid));
+               void(std::optional<BluetoothUUID> service_uuid));
   MOCK_METHOD0(DisconnectGatt, void());
 #if BUILDFLAG(IS_CHROMEOS)
   MOCK_METHOD2(ExecuteWrite,
@@ -159,7 +159,7 @@
 
  private:
   uint32_t bluetooth_class_;
-  absl::optional<std::string> name_;
+  std::optional<std::string> name_;
   std::string address_;
   BluetoothDevice::UUIDSet uuids_;
   bool connected_;
diff --git a/device/bluetooth/test/test_bluetooth_adapter_observer.cc b/device/bluetooth/test/test_bluetooth_adapter_observer.cc
index 31485e1..19d8f2c 100644
--- a/device/bluetooth/test/test_bluetooth_adapter_observer.cc
+++ b/device/bluetooth/test/test_bluetooth_adapter_observer.cc
@@ -175,11 +175,11 @@
 
 void TestBluetoothAdapterObserver::DeviceAdvertisementReceived(
     const std::string& device_address,
-    const absl::optional<std::string>& device_name,
-    const absl::optional<std::string>& advertisement_name,
-    absl::optional<int8_t> rssi,
-    absl::optional<int8_t> tx_power,
-    absl::optional<uint16_t> appearance,
+    const std::optional<std::string>& device_name,
+    const std::optional<std::string>& advertisement_name,
+    std::optional<int8_t> rssi,
+    std::optional<int8_t> tx_power,
+    std::optional<uint16_t> appearance,
     const device::BluetoothDevice::UUIDList& advertised_uuids,
     const device::BluetoothDevice::ServiceDataMap& service_data_map,
     const device::BluetoothDevice::ManufacturerDataMap& manufacturer_data_map) {
diff --git a/device/bluetooth/test/test_bluetooth_adapter_observer.h b/device/bluetooth/test/test_bluetooth_adapter_observer.h
index 96ea35e45..109e8551 100644
--- a/device/bluetooth/test/test_bluetooth_adapter_observer.h
+++ b/device/bluetooth/test/test_bluetooth_adapter_observer.h
@@ -53,11 +53,11 @@
                             const std::string& old_address) override;
   void DeviceAdvertisementReceived(
       const std::string& device_id,
-      const absl::optional<std::string>& device_name,
-      const absl::optional<std::string>& advertisement_name,
-      absl::optional<int8_t> rssi,
-      absl::optional<int8_t> tx_power,
-      absl::optional<uint16_t> appearance,
+      const std::optional<std::string>& device_name,
+      const std::optional<std::string>& advertisement_name,
+      std::optional<int8_t> rssi,
+      std::optional<int8_t> tx_power,
+      std::optional<uint16_t> appearance,
       const device::BluetoothDevice::UUIDList& advertised_uuids,
       const device::BluetoothDevice::ServiceDataMap& service_data_map,
       const device::BluetoothDevice::ManufacturerDataMap& manufacturer_data_map)
@@ -142,15 +142,15 @@
     return device_advertisement_raw_received_count_;
   }
   std::string last_device_address() const { return last_device_address_; }
-  const absl::optional<std::string>& last_device_name() const {
+  const std::optional<std::string>& last_device_name() const {
     return last_device_name_;
   }
-  const absl::optional<std::string>& last_advertisement_name() const {
+  const std::optional<std::string>& last_advertisement_name() const {
     return last_advertisement_name_;
   }
-  const absl::optional<int8_t>& last_rssi() const { return last_rssi_; }
-  const absl::optional<int8_t>& last_tx_power() const { return last_tx_power_; }
-  const absl::optional<uint16_t>& last_appearance() const {
+  const std::optional<int8_t>& last_rssi() const { return last_rssi_; }
+  const std::optional<int8_t>& last_tx_power() const { return last_tx_power_; }
+  const std::optional<uint16_t>& last_appearance() const {
     return last_appearance_;
   }
   const device::BluetoothDevice::UUIDList& last_advertised_uuids() const {
@@ -274,11 +274,11 @@
   // Advertisement related
   int device_advertisement_raw_received_count_;
   std::string last_device_address_;
-  absl::optional<std::string> last_device_name_;
-  absl::optional<std::string> last_advertisement_name_;
-  absl::optional<int8_t> last_rssi_;
-  absl::optional<int8_t> last_tx_power_;
-  absl::optional<uint16_t> last_appearance_;
+  std::optional<std::string> last_device_name_;
+  std::optional<std::string> last_advertisement_name_;
+  std::optional<int8_t> last_rssi_;
+  std::optional<int8_t> last_tx_power_;
+  std::optional<uint16_t> last_appearance_;
   device::BluetoothDevice::UUIDList last_advertised_uuids_;
   device::BluetoothDevice::ServiceDataMap last_service_data_map_;
   device::BluetoothDevice::ManufacturerDataMap last_manufacturer_data_map_;
diff --git a/device/bluetooth/test/test_bluetooth_local_gatt_service_delegate.cc b/device/bluetooth/test/test_bluetooth_local_gatt_service_delegate.cc
index c49aa6d..3c18796 100644
--- a/device/bluetooth/test/test_bluetooth_local_gatt_service_delegate.cc
+++ b/device/bluetooth/test/test_bluetooth_local_gatt_service_delegate.cc
@@ -33,7 +33,7 @@
     return;
   }
   last_seen_device_ = device->GetIdentifier();
-  std::move(callback).Run(/*error_code=*/absl::nullopt,
+  std::move(callback).Run(/*error_code=*/std::nullopt,
                           BluetoothGattServerTest::GetValue(value_to_write_));
 }
 
@@ -90,7 +90,7 @@
     return;
   }
   last_seen_device_ = device->GetIdentifier();
-  std::move(callback).Run(/*error_code=*/absl::nullopt,
+  std::move(callback).Run(/*error_code=*/std::nullopt,
                           BluetoothGattServerTest::GetValue(value_to_write_));
 }
 
diff --git a/device/fido/aoa/android_accessory_device.cc b/device/fido/aoa/android_accessory_device.cc
index e93e741..75e34b85 100644
--- a/device/fido/aoa/android_accessory_device.cc
+++ b/device/fido/aoa/android_accessory_device.cc
@@ -5,6 +5,7 @@
 #include "device/fido/aoa/android_accessory_device.h"
 
 #include <limits>
+#include <optional>
 #include <utility>
 
 #include "base/functional/bind.h"
@@ -12,7 +13,6 @@
 #include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "components/device_event_log/device_event_log.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -37,7 +37,7 @@
   if (static_cast<uint64_t>(command.size()) >
       std::numeric_limits<uint32_t>::max()) {
     NOTREACHED();
-    std::move(callback).Run(absl::nullopt);
+    std::move(callback).Run(std::nullopt);
     return 0;
   }
 
@@ -61,7 +61,7 @@
   if (result != mojom::UsbTransferStatus::COMPLETED) {
     FIDO_LOG(ERROR) << "Failed to write to USB device ("
                     << static_cast<int>(result) << ").";
-    std::move(callback).Run(absl::nullopt);
+    std::move(callback).Run(std::nullopt);
     return;
   }
 
@@ -79,14 +79,14 @@
       payload.size() != 1 + sizeof(uint32_t)) {
     FIDO_LOG(ERROR) << "Failed to read reply from USB device ("
                     << static_cast<int>(result) << ")";
-    std::move(callback).Run(absl::nullopt);
+    std::move(callback).Run(std::nullopt);
     return;
   }
 
   if (payload[0] != kCoaoaMsg) {
     FIDO_LOG(ERROR) << "Reply from USB device with wrong type ("
                     << static_cast<int>(payload[0]) << ")";
-    std::move(callback).Run(absl::nullopt);
+    std::move(callback).Run(std::nullopt);
     return;
   }
 
@@ -95,7 +95,7 @@
   if (length > (1 << 20)) {
     FIDO_LOG(ERROR) << "USB device sent excessive reply containing " << length
                     << " bytes";
-    std::move(callback).Run(absl::nullopt);
+    std::move(callback).Run(std::nullopt);
     return;
   }
 
@@ -121,7 +121,7 @@
       payload.size() + buffer_.size() > length) {
     FIDO_LOG(ERROR) << "Failed to read from USB device ("
                     << static_cast<int>(result) << ")";
-    std::move(callback).Run(absl::nullopt);
+    std::move(callback).Run(std::nullopt);
     return;
   }
 
diff --git a/device/fido/aoa/android_accessory_discovery.cc b/device/fido/aoa/android_accessory_discovery.cc
index 24c7d95..ca0c4ca1 100644
--- a/device/fido/aoa/android_accessory_discovery.cc
+++ b/device/fido/aoa/android_accessory_discovery.cc
@@ -115,7 +115,7 @@
                                   std::move(device)));
 }
 
-static absl::optional<AndroidAccessoryDiscovery::InterfaceInfo>
+static std::optional<AndroidAccessoryDiscovery::InterfaceInfo>
 FindAccessoryInterface(const device::mojom::UsbDeviceInfoPtr& device_info) {
   for (const device::mojom::UsbConfigurationInfoPtr& config :
        device_info->configurations) {
@@ -131,8 +131,8 @@
       if (info->class_code == 0xff && info->subclass_code == 0xff &&
           info->endpoints.size() == 2) {
         // This is the AOA interface. (ADB, if enabled, has a subclass of 66.)
-        absl::optional<uint8_t> in_endpoint_num;
-        absl::optional<uint8_t> out_endpoint_num;
+        std::optional<uint8_t> in_endpoint_num;
+        std::optional<uint8_t> out_endpoint_num;
 
         for (const device::mojom::UsbEndpointInfoPtr& endpoint :
              info->endpoints) {
@@ -158,7 +158,7 @@
     }
   }
 
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 void AndroidAccessoryDiscovery::HandleAccessoryDevice(
diff --git a/device/fido/appid_exclude_probe_task.cc b/device/fido/appid_exclude_probe_task.cc
index d10ceb1..cb83c02d 100644
--- a/device/fido/appid_exclude_probe_task.cc
+++ b/device/fido/appid_exclude_probe_task.cc
@@ -35,7 +35,7 @@
   if (exclude_list_batches_.size() == 1 &&
       exclude_list_batches_.front().empty()) {
     // None of the credential IDs are candidates for this device.
-    std::move(callback_).Run(CtapDeviceResponseCode::kSuccess, absl::nullopt);
+    std::move(callback_).Run(CtapDeviceResponseCode::kSuccess, std::nullopt);
     return;
   }
 
@@ -72,7 +72,7 @@
 
 void AppIdExcludeProbeTask::HandleResponseToSilentSignRequest(
     CtapDeviceResponseCode response_code,
-    absl::optional<AuthenticatorGetAssertionResponse> response_data) {
+    std::optional<AuthenticatorGetAssertionResponse> response_data) {
   silent_sign_operation_.reset();
 
   if (canceled_) {
@@ -83,14 +83,14 @@
     // The authenticator recognized a credential from previous exclude list
     // batch.
     std::move(callback_).Run(
-        CtapDeviceResponseCode::kCtap2ErrCredentialExcluded, absl::nullopt);
+        CtapDeviceResponseCode::kCtap2ErrCredentialExcluded, std::nullopt);
     return;
   }
 
   if (!FidoDevice::IsStatusForUnrecognisedCredentialID(response_code)) {
     // The authenticator returned an unexpected error.
     std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
-                             absl::nullopt);
+                             std::nullopt);
     return;
   }
 
@@ -100,7 +100,7 @@
 
   if (current_exclude_list_batch_ == exclude_list_batches_.size()) {
     // All done.
-    std::move(callback_).Run(CtapDeviceResponseCode::kSuccess, absl::nullopt);
+    std::move(callback_).Run(CtapDeviceResponseCode::kSuccess, std::nullopt);
     return;
   }
 
diff --git a/device/fido/appid_exclude_probe_task.h b/device/fido/appid_exclude_probe_task.h
index a3f6995f..cf14bd4 100644
--- a/device/fido/appid_exclude_probe_task.h
+++ b/device/fido/appid_exclude_probe_task.h
@@ -5,6 +5,7 @@
 #ifndef DEVICE_FIDO_APPID_EXCLUDE_PROBE_TASK_H_
 #define DEVICE_FIDO_APPID_EXCLUDE_PROBE_TASK_H_
 
+#include <optional>
 #include <vector>
 
 #include "base/functional/callback.h"
@@ -15,7 +16,6 @@
 #include "device/fido/device_operation.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_task.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -26,7 +26,7 @@
 class AppIdExcludeProbeTask : public FidoTask {
  public:
   using Callback =
-      base::OnceCallback<void(CtapDeviceResponseCode, absl::optional<bool>)>;
+      base::OnceCallback<void(CtapDeviceResponseCode, std::optional<bool>)>;
 
   AppIdExcludeProbeTask(FidoDevice* device,
                         CtapMakeCredentialRequest request,
@@ -42,7 +42,7 @@
   void NextSilentSignOperation();
   void HandleResponseToSilentSignRequest(
       CtapDeviceResponseCode response_code,
-      absl::optional<AuthenticatorGetAssertionResponse> response_data);
+      std::optional<AuthenticatorGetAssertionResponse> response_data);
 
   const CtapMakeCredentialRequest request_;
   const MakeCredentialOptions options_;
diff --git a/device/fido/attestation_object.cc b/device/fido/attestation_object.cc
index af080229..7ee7e4a 100644
--- a/device/fido/attestation_object.cc
+++ b/device/fido/attestation_object.cc
@@ -22,22 +22,22 @@
 AttestationObject::ResponseFields::ResponseFields(ResponseFields&&) = default;
 
 // static
-absl::optional<AttestationObject> AttestationObject::Parse(
+std::optional<AttestationObject> AttestationObject::Parse(
     const cbor::Value& value) {
   if (!value.is_map()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   const cbor::Value::MapValue& map = value.GetMap();
 
   const auto& format_it = map.find(cbor::Value(kFormatKey));
   if (format_it == map.end() || !format_it->second.is_string()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   const std::string& fmt = format_it->second.GetString();
 
   const auto& att_stmt_it = map.find(cbor::Value(kAttestationStatementKey));
   if (att_stmt_it == map.end() || !att_stmt_it->second.is_map()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   std::unique_ptr<AttestationStatement> attestation_statement =
       std::make_unique<OpaqueAttestationStatement>(
@@ -45,39 +45,39 @@
 
   const auto& auth_data_it = map.find(cbor::Value(kAuthDataKey));
   if (auth_data_it == map.end() || !auth_data_it->second.is_bytestring()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
-  absl::optional<AuthenticatorData> authenticator_data =
+  std::optional<AuthenticatorData> authenticator_data =
       AuthenticatorData::DecodeAuthenticatorData(
           auth_data_it->second.GetBytestring());
   if (!authenticator_data) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   return AttestationObject(std::move(*authenticator_data),
                            std::move(attestation_statement));
 }
 
 // static
-absl::optional<AttestationObject::ResponseFields>
+std::optional<AttestationObject::ResponseFields>
 AttestationObject::ParseForResponseFields(
     std::vector<uint8_t> attestation_object_bytes,
     bool attestation_acceptable) {
-  absl::optional<cbor::Value> attestation_object_map =
+  std::optional<cbor::Value> attestation_object_map =
       cbor::Reader::Read(attestation_object_bytes);
   if (!attestation_object_map || !attestation_object_map->is_map()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
-  absl::optional<device::AttestationObject> attestation_object =
+  std::optional<device::AttestationObject> attestation_object =
       device::AttestationObject::Parse(*attestation_object_map);
   if (!attestation_object) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
-  const absl::optional<device::AttestedCredentialData>& att_cred_data(
+  const std::optional<device::AttestedCredentialData>& att_cred_data(
       attestation_object->authenticator_data().attested_data());
   if (!att_cred_data) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   const device::PublicKey* pub_key = att_cred_data->public_key();
diff --git a/device/fido/attestation_object.h b/device/fido/attestation_object.h
index e5bd718f..896f0955d 100644
--- a/device/fido/attestation_object.h
+++ b/device/fido/attestation_object.h
@@ -9,13 +9,13 @@
 
 #include <array>
 #include <memory>
+#include <optional>
 #include <string_view>
 #include <vector>
 
 #include "base/component_export.h"
 #include "device/fido/authenticator_data.h"
 #include "device/fido/fido_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -37,17 +37,17 @@
 
     std::vector<uint8_t> attestation_object_bytes;
     std::vector<uint8_t> authenticator_data;
-    absl::optional<std::vector<uint8_t>> public_key_der;
+    std::optional<std::vector<uint8_t>> public_key_der;
     int32_t public_key_algo;
   };
 
-  static absl::optional<AttestationObject> Parse(const cbor::Value& value);
+  static std::optional<AttestationObject> Parse(const cbor::Value& value);
 
   // ParseForResponseFields parses a serialized attestation object and extracts
   // the fields needed to build a browser's response to a create() call. If
   // `attestation_acceptable` is false any attestation will have been removed
   // in the return value.
-  static absl::optional<ResponseFields> ParseForResponseFields(
+  static std::optional<ResponseFields> ParseForResponseFields(
       std::vector<uint8_t> attestation_object_bytes,
       bool attestation_acceptable);
 
diff --git a/device/fido/attestation_statement.cc b/device/fido/attestation_statement.cc
index 8406fb9..fd3a4c3 100644
--- a/device/fido/attestation_statement.cc
+++ b/device/fido/attestation_statement.cc
@@ -34,9 +34,9 @@
   return false;
 }
 
-absl::optional<base::span<const uint8_t>>
+std::optional<base::span<const uint8_t>>
 NoneAttestationStatement::GetLeafCertificate() const {
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 cbor::Value NoneAttestationStatement::AsCBOR() const {
diff --git a/device/fido/attestation_statement.h b/device/fido/attestation_statement.h
index e10cd3e..ff359f7 100644
--- a/device/fido/attestation_statement.h
+++ b/device/fido/attestation_statement.h
@@ -5,12 +5,12 @@
 #ifndef DEVICE_FIDO_ATTESTATION_STATEMENT_H_
 #define DEVICE_FIDO_ATTESTATION_STATEMENT_H_
 
+#include <optional>
 #include <string>
 
 #include "base/component_export.h"
 #include "base/containers/span.h"
 #include "components/cbor/values.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -49,7 +49,7 @@
   virtual bool IsAttestationCertificateInappropriatelyIdentifying() const = 0;
 
   // Return the DER bytes of the leaf X.509 certificate, if any.
-  virtual absl::optional<base::span<const uint8_t>> GetLeafCertificate()
+  virtual std::optional<base::span<const uint8_t>> GetLeafCertificate()
       const = 0;
 
   const std::string& format_name() const { return format_; }
@@ -76,7 +76,7 @@
   bool IsNoneAttestation() const override;
   bool IsSelfAttestation() const override;
   bool IsAttestationCertificateInappropriatelyIdentifying() const override;
-  absl::optional<base::span<const uint8_t>> GetLeafCertificate() const override;
+  std::optional<base::span<const uint8_t>> GetLeafCertificate() const override;
 };
 
 COMPONENT_EXPORT(DEVICE_FIDO)
diff --git a/device/fido/attestation_statement_formats.cc b/device/fido/attestation_statement_formats.cc
index f88713e3..50e9246 100644
--- a/device/fido/attestation_statement_formats.cc
+++ b/device/fido/attestation_statement_formats.cc
@@ -157,10 +157,10 @@
   return false;
 }
 
-absl::optional<base::span<const uint8_t>>
+std::optional<base::span<const uint8_t>>
 FidoAttestationStatement::GetLeafCertificate() const {
   if (x509_certificates_.empty()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   return x509_certificates_[0];
 }
@@ -217,10 +217,10 @@
   return false;
 }
 
-absl::optional<base::span<const uint8_t>>
+std::optional<base::span<const uint8_t>>
 PackedAttestationStatement::GetLeafCertificate() const {
   if (x509_certificates_.empty()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   return x509_certificates_[0];
 }
diff --git a/device/fido/attestation_statement_formats.h b/device/fido/attestation_statement_formats.h
index 29c4d5b..3c95bad2 100644
--- a/device/fido/attestation_statement_formats.h
+++ b/device/fido/attestation_statement_formats.h
@@ -36,7 +36,7 @@
   bool IsNoneAttestation() const override;
   bool IsSelfAttestation() const override;
   bool IsAttestationCertificateInappropriatelyIdentifying() const override;
-  absl::optional<base::span<const uint8_t>> GetLeafCertificate() const override;
+  std::optional<base::span<const uint8_t>> GetLeafCertificate() const override;
 
  private:
   const std::vector<uint8_t> signature_;
@@ -61,7 +61,7 @@
   bool IsNoneAttestation() const override;
   bool IsSelfAttestation() const override;
   bool IsAttestationCertificateInappropriatelyIdentifying() const override;
-  absl::optional<base::span<const uint8_t>> GetLeafCertificate() const override;
+  std::optional<base::span<const uint8_t>> GetLeafCertificate() const override;
 
  private:
   const CoseAlgorithmIdentifier algorithm_;
diff --git a/device/fido/attested_credential_data.cc b/device/fido/attested_credential_data.cc
index ab592b7..8e00ce7 100644
--- a/device/fido/attested_credential_data.cc
+++ b/device/fido/attested_credential_data.cc
@@ -27,18 +27,18 @@
 namespace device {
 
 // static
-absl::optional<std::pair<AttestedCredentialData, base::span<const uint8_t>>>
+std::optional<std::pair<AttestedCredentialData, base::span<const uint8_t>>>
 AttestedCredentialData::ConsumeFromCtapResponse(
     base::span<const uint8_t> buffer) {
   if (buffer.size() < kAaguidLength) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   auto aaguid = buffer.first<kAaguidLength>();
   buffer = buffer.subspan(kAaguidLength);
 
   if (buffer.size() < kCredentialIdLengthLength) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   auto credential_id_length_span = buffer.first<kCredentialIdLengthLength>();
@@ -48,18 +48,18 @@
   buffer = buffer.subspan(kCredentialIdLengthLength);
 
   if (buffer.size() < credential_id_length) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   auto credential_id = buffer.first(credential_id_length);
   buffer = buffer.subspan(credential_id_length);
 
   size_t public_key_byte_len;
-  absl::optional<cbor::Value> public_key_cbor =
+  std::optional<cbor::Value> public_key_cbor =
       cbor::Reader::Read(buffer, &public_key_byte_len);
   if (!public_key_cbor || !public_key_cbor->is_map()) {
     FIDO_LOG(ERROR) << "CBOR error in COSE public key";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   const base::span<const uint8_t> public_key_cbor_bytes(
@@ -100,7 +100,7 @@
 
   if (!cbor_extract::Extract<COSEKey>(&cose_key, kSteps, public_key_map)) {
     FIDO_LOG(ERROR) << "Failed to parse COSE key";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   // In WebIDL, a |long| is an |int32_t|[1].
@@ -110,7 +110,7 @@
   if (algorithm64 > std::numeric_limits<int32_t>::max() ||
       algorithm64 < std::numeric_limits<int32_t>::min()) {
     FIDO_LOG(ERROR) << "COSE algorithm in public key is out of range";
-    return absl::nullopt;
+    return std::nullopt;
   }
   const int32_t algorithm = static_cast<int32_t>(algorithm64);
   const int64_t key_type = *cose_key.kty;
@@ -122,7 +122,7 @@
     auto curve = public_key_map.find(
         cbor::Value(static_cast<int64_t>(CoseKeyKey::kEllipticCurve)));
     if (curve == public_key_map.end() || !curve->second.is_integer()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     const int64_t curve_id = curve->second.GetInteger();
 
@@ -132,7 +132,7 @@
           algorithm, public_key_cbor_bytes, public_key_map);
       if (!p256_key) {
         FIDO_LOG(ERROR) << "Invalid P-256 public key";
-        return absl::nullopt;
+        return std::nullopt;
       }
       public_key = std::move(p256_key);
     } else if (key_type == static_cast<int64_t>(CoseKeyTypes::kOKP) &&
@@ -141,7 +141,7 @@
           algorithm, public_key_cbor_bytes, public_key_map);
       if (!ed25519_key) {
         FIDO_LOG(ERROR) << "Invalid Ed25519 public key";
-        return absl::nullopt;
+        return std::nullopt;
       }
       public_key = std::move(ed25519_key);
     }
@@ -150,14 +150,14 @@
         algorithm, public_key_cbor_bytes, public_key_map);
     if (!rsa_key) {
       FIDO_LOG(ERROR) << "Invalid RSA public key";
-      return absl::nullopt;
+      return std::nullopt;
     }
     public_key = std::move(rsa_key);
   }
 
   if (!public_key) {
     public_key = std::make_unique<PublicKey>(algorithm, public_key_cbor_bytes,
-                                             absl::nullopt);
+                                             std::nullopt);
   }
 
   return std::make_pair(
@@ -168,7 +168,7 @@
 }
 
 // static
-absl::optional<AttestedCredentialData>
+std::optional<AttestedCredentialData>
 AttestedCredentialData::CreateFromU2fRegisterResponse(
     base::span<const uint8_t> u2f_data,
     std::unique_ptr<PublicKey> public_key) {
@@ -179,7 +179,7 @@
       fido_parsing_utils::Extract(u2f_data, kU2fKeyHandleLengthOffset, 1);
 
   if (extracted_length.empty()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   // For U2F register request, device AAGUID is set to zeros.
@@ -195,7 +195,7 @@
       base::strict_cast<size_t>(credential_id_length[1]));
 
   if (credential_id.empty()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return AttestedCredentialData(aaguid, credential_id_length,
diff --git a/device/fido/attested_credential_data.h b/device/fido/attested_credential_data.h
index 32ccf277..e4a09a1e 100644
--- a/device/fido/attested_credential_data.h
+++ b/device/fido/attested_credential_data.h
@@ -7,13 +7,14 @@
 
 #include <stddef.h>
 #include <stdint.h>
+
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "base/component_export.h"
 #include "base/containers/span.h"
 #include "device/fido/fido_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -25,11 +26,11 @@
   // Parses an |AttestedCredentialData| from a prefix of |*buffer|. Returns
   // nullopt on error, or else the parse return and a (possibly empty) suffix of
   // |buffer| that was not parsed.
-  static absl::optional<
+  static std::optional<
       std::pair<AttestedCredentialData, base::span<const uint8_t>>>
   ConsumeFromCtapResponse(base::span<const uint8_t> buffer);
 
-  static absl::optional<AttestedCredentialData> CreateFromU2fRegisterResponse(
+  static std::optional<AttestedCredentialData> CreateFromU2fRegisterResponse(
       base::span<const uint8_t> u2f_data,
       std::unique_ptr<PublicKey> public_key);
 
diff --git a/device/fido/auth_token_requester.cc b/device/fido/auth_token_requester.cc
index 039e99c..fe8c32b 100644
--- a/device/fido/auth_token_requester.cc
+++ b/device/fido/auth_token_requester.cc
@@ -83,7 +83,7 @@
   switch (client_pin_availability) {
     case ClientPinAvailability::kNotSupported:
       delegate_->HavePINUVAuthTokenResultForAuthenticator(
-          authenticator_, Result::kPreTouchUnsatisfiableRequest, absl::nullopt);
+          authenticator_, Result::kPreTouchUnsatisfiableRequest, std::nullopt);
       return;
     case ClientPinAvailability::kSupportedAndPinSet:
       if (options_.skip_pin_touch) {
@@ -112,11 +112,11 @@
 
 void AuthTokenRequester::OnGetUVRetries(
     CtapDeviceResponseCode status,
-    absl::optional<pin::RetriesResponse> response) {
+    std::optional<pin::RetriesResponse> response) {
   if (status != CtapDeviceResponseCode::kSuccess) {
     delegate_->HavePINUVAuthTokenResultForAuthenticator(
         authenticator_, Result::kPreTouchAuthenticatorResponseInvalid,
-        absl::nullopt);
+        std::nullopt);
     return;
   }
 
@@ -153,7 +153,7 @@
 
 void AuthTokenRequester::OnGetUVToken(
     CtapDeviceResponseCode status,
-    absl::optional<pin::TokenResponse> response) {
+    std::optional<pin::TokenResponse> response) {
   if (!base::Contains(
           std::set<CtapDeviceResponseCode>{
               CtapDeviceResponseCode::kCtap2ErrUvInvalid,
@@ -166,7 +166,7 @@
                     << " from " << authenticator_->GetDisplayName();
     delegate_->HavePINUVAuthTokenResultForAuthenticator(
         authenticator_, Result::kPreTouchAuthenticatorResponseInvalid,
-        absl::nullopt);
+        std::nullopt);
     return;
   }
 
@@ -179,7 +179,7 @@
     // a display.
     delegate_->HavePINUVAuthTokenResultForAuthenticator(
         authenticator_, Result::kPostTouchAuthenticatorOperationDenied,
-        absl::nullopt);
+        std::nullopt);
     return;
   }
 
@@ -203,7 +203,7 @@
     // remaining retries just before that to handle that case.
     delegate_->HavePINUVAuthTokenResultForAuthenticator(
         authenticator_, Result::kPostTouchAuthenticatorInternalUVLock,
-        absl::nullopt);
+        std::nullopt);
     return;
   }
 
@@ -222,17 +222,17 @@
 
 void AuthTokenRequester::OnGetPINRetries(
     CtapDeviceResponseCode status,
-    absl::optional<pin::RetriesResponse> response) {
+    std::optional<pin::RetriesResponse> response) {
   if (status != CtapDeviceResponseCode::kSuccess) {
     delegate_->HavePINUVAuthTokenResultForAuthenticator(
         authenticator_, Result::kPostTouchAuthenticatorResponseInvalid,
-        absl::nullopt);
+        std::nullopt);
     return;
   }
   if (response->retries == 0) {
     delegate_->HavePINUVAuthTokenResultForAuthenticator(
         authenticator_, Result::kPostTouchAuthenticatorPINHardLock,
-        absl::nullopt);
+        std::nullopt);
     return;
   }
   pin_retries_ = response->retries;
@@ -275,7 +275,7 @@
 void AuthTokenRequester::OnGetPINToken(
     std::string pin,
     CtapDeviceResponseCode status,
-    absl::optional<pin::TokenResponse> response) {
+    std::optional<pin::TokenResponse> response) {
   if (status == CtapDeviceResponseCode::kCtap2ErrPinInvalid) {
     pin_invalid_ = true;
     ObtainTokenFromPIN();
@@ -306,7 +306,7 @@
         break;
     }
     delegate_->HavePINUVAuthTokenResultForAuthenticator(authenticator_, ret,
-                                                        absl::nullopt);
+                                                        std::nullopt);
     return;
   }
 
@@ -352,11 +352,11 @@
 
 void AuthTokenRequester::OnSetPIN(std::string pin,
                                   CtapDeviceResponseCode status,
-                                  absl::optional<pin::EmptyResponse> response) {
+                                  std::optional<pin::EmptyResponse> response) {
   if (status != CtapDeviceResponseCode::kSuccess) {
     delegate_->HavePINUVAuthTokenResultForAuthenticator(
         authenticator_, Result::kPostTouchAuthenticatorResponseInvalid,
-        absl::nullopt);
+        std::nullopt);
     return;
   }
 
@@ -382,7 +382,7 @@
     Result result) {
   if (NotifyAuthenticatorSelected()) {
     delegate_->HavePINUVAuthTokenResultForAuthenticator(authenticator_, result,
-                                                        absl::nullopt);
+                                                        std::nullopt);
   }
 }
 
diff --git a/device/fido/auth_token_requester.h b/device/fido/auth_token_requester.h
index b5121f8..9d2e314 100644
--- a/device/fido/auth_token_requester.h
+++ b/device/fido/auth_token_requester.h
@@ -5,6 +5,7 @@
 #ifndef DEVICE_FIDO_AUTH_TOKEN_REQUESTER_H_
 #define DEVICE_FIDO_AUTH_TOKEN_REQUESTER_H_
 
+#include <optional>
 #include <set>
 #include <string>
 #include <vector>
@@ -15,7 +16,6 @@
 #include "base/memory/weak_ptr.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/pin.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -50,7 +50,7 @@
     std::set<pin::Permissions> token_permissions;
 
     // rp_id is the permissions RP ID for the token to be requested.
-    absl::optional<std::string> rp_id;
+    std::optional<std::string> rp_id;
 
     // skip_pin_touch indicates whether not to request a touch before attempting
     // to obtain a token using a PIN.
@@ -106,12 +106,12 @@
     virtual void PromptForInternalUVRetry(int attempts) = 0;
 
     // HavePINUVAuthTokenResultForAuthenticator notifies the delegate of the
-    // outcome of ObtainPINUVAuthToken(). |response| is `absl::nullopt`, unless
+    // outcome of ObtainPINUVAuthToken(). |response| is `std::nullopt`, unless
     // |result| is |Result::kSuccess|.
     virtual void HavePINUVAuthTokenResultForAuthenticator(
         FidoAuthenticator* authenticator,
         Result result,
-        absl::optional<pin::TokenResponse> response) = 0;
+        std::optional<pin::TokenResponse> response) = 0;
   };
 
   // Instantiates a new AuthTokenRequester. |delegate| and |authenticator| must
@@ -134,23 +134,23 @@
  private:
   void ObtainTokenFromInternalUV();
   void OnGetUVRetries(CtapDeviceResponseCode status,
-                      absl::optional<pin::RetriesResponse> response);
+                      std::optional<pin::RetriesResponse> response);
   void OnGetUVToken(CtapDeviceResponseCode status,
-                    absl::optional<pin::TokenResponse> response);
+                    std::optional<pin::TokenResponse> response);
 
   void ObtainTokenFromPIN();
   void OnGetPINRetries(CtapDeviceResponseCode status,
-                       absl::optional<pin::RetriesResponse> response);
+                       std::optional<pin::RetriesResponse> response);
   void HavePIN(std::u16string pin);
   void OnGetPINToken(std::string pin,
                      CtapDeviceResponseCode status,
-                     absl::optional<pin::TokenResponse> response);
+                     std::optional<pin::TokenResponse> response);
 
   void ObtainTokenFromNewPIN();
   void HaveNewPIN(std::u16string pin);
   void OnSetPIN(std::string pin,
                 CtapDeviceResponseCode status,
-                absl::optional<pin::EmptyResponse> response);
+                std::optional<pin::EmptyResponse> response);
 
   bool NotifyAuthenticatorSelected();
   void NotifyAuthenticatorSelectedAndFailWithResult(Result result);
@@ -160,9 +160,9 @@
 
   Options options_;
 
-  absl::optional<bool> authenticator_selected_result_;
+  std::optional<bool> authenticator_selected_result_;
   bool is_internal_uv_retry_ = false;
-  absl::optional<std::string> current_pin_;
+  std::optional<std::string> current_pin_;
   bool internal_uv_locked_ = false;
   bool pin_invalid_ = false;
   int pin_retries_ = 0;
diff --git a/device/fido/auth_token_requester_unittest.cc b/device/fido/auth_token_requester_unittest.cc
index 248930a..a15a9d2 100644
--- a/device/fido/auth_token_requester_unittest.cc
+++ b/device/fido/auth_token_requester_unittest.cc
@@ -5,11 +5,9 @@
 #include "device/fido/auth_token_requester.h"
 
 #include <list>
+#include <optional>
 #include <string>
 
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
 #include "base/containers/contains.h"
 #include "base/containers/span.h"
 #include "base/logging.h"
@@ -21,7 +19,8 @@
 #include "device/fido/fido_device_authenticator.h"
 #include "device/fido/pin.h"
 #include "device/fido/virtual_ctap2_device.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 namespace device {
 namespace {
@@ -59,8 +58,8 @@
       : expectations_(std::move(expectations)) {}
 
   void WaitForResult() { wait_for_result_loop_.Run(); }
-  absl::optional<AuthTokenRequester::Result>& result() { return result_; }
-  absl::optional<pin::TokenResponse>& response() { return response_; }
+  std::optional<AuthTokenRequester::Result>& result() { return result_; }
+  std::optional<pin::TokenResponse>& response() { return response_; }
   bool internal_uv_was_retried() { return internal_uv_num_retries_ > 0u; }
   size_t internal_uv_num_retries() { return internal_uv_num_retries_; }
   std::list<TestExpectation> expectations() { return expectations_; }
@@ -100,7 +99,7 @@
   void HavePINUVAuthTokenResultForAuthenticator(
       FidoAuthenticator* authenticator,
       AuthTokenRequester::Result result,
-      absl::optional<pin::TokenResponse> response) override {
+      std::optional<pin::TokenResponse> response) override {
     if (!base::Contains(
             std::vector<AuthTokenRequester::Result>{
                 AuthTokenRequester::Result::
@@ -117,8 +116,8 @@
 
   std::list<TestExpectation> expectations_;
 
-  absl::optional<AuthTokenRequester::Result> result_;
-  absl::optional<pin::TokenResponse> response_;
+  std::optional<AuthTokenRequester::Result> result_;
+  std::optional<pin::TokenResponse> response_;
 
   bool authenticator_selected_ = false;
   size_t internal_uv_num_retries_ = 0u;
diff --git a/device/fido/authenticator_data.cc b/device/fido/authenticator_data.cc
index e84c08f4..2a74a02 100644
--- a/device/fido/authenticator_data.cc
+++ b/device/fido/authenticator_data.cc
@@ -70,10 +70,10 @@
 }  // namespace
 
 // static
-absl::optional<AuthenticatorData> AuthenticatorData::DecodeAuthenticatorData(
+std::optional<AuthenticatorData> AuthenticatorData::DecodeAuthenticatorData(
     base::span<const uint8_t> auth_data) {
   if (auth_data.size() < kAttestedCredentialDataOffset) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   auto application_parameter = auth_data.first<kRpIdHashLength>();
   uint8_t flag_byte = auth_data[kRpIdHashLength];
@@ -81,17 +81,17 @@
       auth_data.subspan<kRpIdHashLength + kFlagsLength, kSignCounterLength>();
 
   auth_data = auth_data.subspan(kAttestedCredentialDataOffset);
-  absl::optional<AttestedCredentialData> attested_credential_data;
+  std::optional<AttestedCredentialData> attested_credential_data;
   if (flag_byte & static_cast<uint8_t>(Flag::kAttestation)) {
     auto maybe_result =
         AttestedCredentialData::ConsumeFromCtapResponse(auth_data);
     if (!maybe_result) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     std::tie(attested_credential_data, auth_data) = std::move(*maybe_result);
   }
 
-  absl::optional<cbor::Value> extensions;
+  std::optional<cbor::Value> extensions;
   if (flag_byte & static_cast<uint8_t>(Flag::kExtensionDataIncluded)) {
     cbor::Reader::DecoderError error;
     extensions = cbor::Reader::Read(auth_data, &error);
@@ -100,16 +100,16 @@
           << "CBOR decoding of authenticator data extensions failed ("
           << cbor::Reader::ErrorCodeToString(error) << ") from "
           << base::HexEncode(auth_data);
-      return absl::nullopt;
+      return std::nullopt;
     }
     if (!extensions->is_map()) {
       FIDO_LOG(ERROR)
           << "Incorrect CBOR structure of authenticator data extensions: "
           << cbor::DiagnosticWriter::Write(*extensions);
-      return absl::nullopt;
+      return std::nullopt;
     }
   } else if (!auth_data.empty()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return AuthenticatorData(application_parameter, flag_byte, counter,
@@ -121,8 +121,8 @@
     base::span<const uint8_t, kRpIdHashLength> rp_id_hash,
     uint8_t flags,
     base::span<const uint8_t, kSignCounterLength> counter,
-    absl::optional<AttestedCredentialData> data,
-    absl::optional<cbor::Value> extensions)
+    std::optional<AttestedCredentialData> data,
+    std::optional<cbor::Value> extensions)
     : flags_(flags),
       application_parameter_(fido_parsing_utils::Materialize(rp_id_hash)),
       counter_(fido_parsing_utils::Materialize(counter)),
@@ -135,8 +135,8 @@
     base::span<const uint8_t, kRpIdHashLength> rp_id_hash,
     std::initializer_list<Flag> flags,
     uint32_t sign_counter,
-    absl::optional<AttestedCredentialData> data,
-    absl::optional<cbor::Value> extensions)
+    std::optional<AttestedCredentialData> data,
+    std::optional<cbor::Value> extensions)
     : AuthenticatorData(rp_id_hash,
                         CombineAuthenticatorDataFlags(flags),
                         MarshalSignCounter(sign_counter),
@@ -150,8 +150,8 @@
     bool backup_eligible,
     bool backup_state,
     uint32_t sign_counter,
-    absl::optional<AttestedCredentialData> attested_credential_data,
-    absl::optional<cbor::Value> extensions)
+    std::optional<AttestedCredentialData> attested_credential_data,
+    std::optional<cbor::Value> extensions)
     : flags_(AuthenticatorDataFlags(user_present,
                                     user_verified,
                                     backup_eligible,
diff --git a/device/fido/authenticator_data.h b/device/fido/authenticator_data.h
index f89915ae..47b1fdb 100644
--- a/device/fido/authenticator_data.h
+++ b/device/fido/authenticator_data.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <array>
+#include <optional>
 #include <string>
 #include <string_view>
 #include <vector>
@@ -18,7 +19,6 @@
 #include "components/cbor/values.h"
 #include "device/fido/attested_credential_data.h"
 #include "device/fido/fido_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -34,7 +34,7 @@
     kExtensionDataIncluded = 1u << 7,
   };
 
-  static absl::optional<AuthenticatorData> DecodeAuthenticatorData(
+  static std::optional<AuthenticatorData> DecodeAuthenticatorData(
       base::span<const uint8_t> auth_data);
 
   //  The attested credential |data| must be specified iff |flags| have
@@ -43,14 +43,14 @@
   AuthenticatorData(base::span<const uint8_t, kRpIdHashLength> rp_id_hash,
                     uint8_t flags,
                     base::span<const uint8_t, kSignCounterLength> sign_counter,
-                    absl::optional<AttestedCredentialData> data,
-                    absl::optional<cbor::Value> extensions = absl::nullopt);
+                    std::optional<AttestedCredentialData> data,
+                    std::optional<cbor::Value> extensions = std::nullopt);
 
   AuthenticatorData(base::span<const uint8_t, kRpIdHashLength> rp_id_hash,
                     std::initializer_list<Flag> flags,
                     uint32_t sign_counter,
-                    absl::optional<AttestedCredentialData> data,
-                    absl::optional<cbor::Value> extensions = absl::nullopt);
+                    std::optional<AttestedCredentialData> data,
+                    std::optional<cbor::Value> extensions = std::nullopt);
 
   // Creates an AuthenticatorData with flags and signature counter encoded
   // according to the supplied arguments.
@@ -61,8 +61,8 @@
       bool backup_eligible,
       bool backup_state,
       uint32_t sign_counter,
-      absl::optional<AttestedCredentialData> attested_credential_data,
-      absl::optional<cbor::Value> extensions);
+      std::optional<AttestedCredentialData> attested_credential_data,
+      std::optional<cbor::Value> extensions);
 
   AuthenticatorData(AuthenticatorData&& other);
   AuthenticatorData& operator=(AuthenticatorData&& other);
@@ -92,13 +92,13 @@
   // authenticator data.
   std::vector<uint8_t> GetCredentialId() const;
 
-  const absl::optional<AttestedCredentialData>& attested_data() const {
+  const std::optional<AttestedCredentialData>& attested_data() const {
     return attested_data_;
   }
 
   // If a value is returned then the result of calling |is_map()| on it can be
   // assumed to be true.
-  const absl::optional<cbor::Value>& extensions() const { return extensions_; }
+  const std::optional<cbor::Value>& extensions() const { return extensions_; }
 
   const std::array<uint8_t, kRpIdHashLength>& application_parameter() const {
     return application_parameter_;
@@ -149,9 +149,9 @@
 
   // Signature counter, 32-bit unsigned big-endian integer.
   std::array<uint8_t, kSignCounterLength> counter_;
-  absl::optional<AttestedCredentialData> attested_data_;
+  std::optional<AttestedCredentialData> attested_data_;
   // If |extensions_| has a value, then it will be a CBOR map.
-  absl::optional<cbor::Value> extensions_;
+  std::optional<cbor::Value> extensions_;
 };
 
 }  // namespace device
diff --git a/device/fido/authenticator_get_assertion_response.cc b/device/fido/authenticator_get_assertion_response.cc
index a569c692..c72910ba 100644
--- a/device/fido/authenticator_get_assertion_response.cc
+++ b/device/fido/authenticator_get_assertion_response.cc
@@ -4,13 +4,13 @@
 
 #include "device/fido/authenticator_get_assertion_response.h"
 
+#include <optional>
 #include <utility>
 
 #include "components/cbor/values.h"
 #include "components/device_event_log/device_event_log.h"
 #include "device/fido/authenticator_data.h"
 #include "device/fido/fido_parsing_utils.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/boringssl/src/include/openssl/ecdsa.h"
 
 namespace device {
@@ -26,18 +26,18 @@
 }  // namespace
 
 // static
-absl::optional<AuthenticatorGetAssertionResponse>
+std::optional<AuthenticatorGetAssertionResponse>
 AuthenticatorGetAssertionResponse::CreateFromU2fSignResponse(
     base::span<const uint8_t, kRpIdHashLength> relying_party_id_hash,
     base::span<const uint8_t> u2f_data,
     base::span<const uint8_t> key_handle,
-    absl::optional<FidoTransportProtocol> transport_used) {
+    std::optional<FidoTransportProtocol> transport_used) {
   if (u2f_data.size() <= kSignatureIndex) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   if (key_handle.empty()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   auto flags = u2f_data.subspan<kFlagIndex, kFlagLength>()[0];
@@ -45,11 +45,11 @@
       (static_cast<uint8_t>(AuthenticatorData::Flag::kExtensionDataIncluded) |
        static_cast<uint8_t>(AuthenticatorData::Flag::kAttestation))) {
     // U2F responses cannot assert CTAP2 features.
-    return absl::nullopt;
+    return std::nullopt;
   }
   auto counter = u2f_data.subspan<kCounterIndex, kCounterLength>();
   AuthenticatorData authenticator_data(relying_party_id_hash, flags, counter,
-                                       absl::nullopt);
+                                       std::nullopt);
 
   auto signature =
       fido_parsing_utils::Materialize(u2f_data.subspan(kSignatureIndex));
@@ -59,7 +59,7 @@
   if (!parsed_sig) {
     FIDO_LOG(ERROR)
         << "Rejecting U2F assertion response with invalid signature";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   AuthenticatorGetAssertionResponse response(
@@ -72,7 +72,7 @@
 AuthenticatorGetAssertionResponse::AuthenticatorGetAssertionResponse(
     AuthenticatorData authenticator_data,
     std::vector<uint8_t> signature,
-    absl::optional<FidoTransportProtocol> transport_used)
+    std::optional<FidoTransportProtocol> transport_used)
     : authenticator_data(std::move(authenticator_data)),
       signature(std::move(signature)),
       transport_used(transport_used) {}
diff --git a/device/fido/authenticator_get_assertion_response.h b/device/fido/authenticator_get_assertion_response.h
index 426332fc..a00ccd4 100644
--- a/device/fido/authenticator_get_assertion_response.h
+++ b/device/fido/authenticator_get_assertion_response.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <array>
+#include <optional>
 #include <vector>
 
 #include "base/component_export.h"
@@ -17,7 +18,6 @@
 #include "device/fido/large_blob.h"
 #include "device/fido/public_key_credential_descriptor.h"
 #include "device/fido/public_key_credential_user_entity.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -31,31 +31,31 @@
   AuthenticatorGetAssertionResponse& operator=(
       const AuthenticatorGetAssertionResponse&) = delete;
 
-  static absl::optional<AuthenticatorGetAssertionResponse>
+  static std::optional<AuthenticatorGetAssertionResponse>
   CreateFromU2fSignResponse(
       base::span<const uint8_t, kRpIdHashLength> relying_party_id_hash,
       base::span<const uint8_t> u2f_data,
       base::span<const uint8_t> key_handle,
-      absl::optional<FidoTransportProtocol> transport_used);
+      std::optional<FidoTransportProtocol> transport_used);
 
   AuthenticatorGetAssertionResponse(
       AuthenticatorData authenticator_data,
       std::vector<uint8_t> signature,
-      absl::optional<FidoTransportProtocol> transport_used);
+      std::optional<FidoTransportProtocol> transport_used);
   AuthenticatorGetAssertionResponse(AuthenticatorGetAssertionResponse&& that);
   AuthenticatorGetAssertionResponse& operator=(
       AuthenticatorGetAssertionResponse&& other);
   ~AuthenticatorGetAssertionResponse();
 
   AuthenticatorData authenticator_data;
-  absl::optional<PublicKeyCredentialDescriptor> credential;
+  std::optional<PublicKeyCredentialDescriptor> credential;
   std::vector<uint8_t> signature;
-  absl::optional<PublicKeyCredentialUserEntity> user_entity;
-  absl::optional<uint8_t> num_credentials;
+  std::optional<PublicKeyCredentialUserEntity> user_entity;
+  std::optional<uint8_t> num_credentials;
 
   // hmac-secret contains the output of the hmac-secret or prf extension. The
   // values have already been decrypted.
-  absl::optional<std::vector<uint8_t>> hmac_secret;
+  std::optional<std::vector<uint8_t>> hmac_secret;
 
   // hmac_secret_not_evaluated will be true in cases where the
   // |FidoAuthenticator| was unable to process the extension, even though it
@@ -67,14 +67,14 @@
   // The large blob key associated to the credential. This value is only
   // returned if the assertion request contains the largeBlobKey extension on a
   // capable authenticator and the credential has an associated large blob key.
-  absl::optional<std::array<uint8_t, kLargeBlobKeyLength>> large_blob_key;
+  std::optional<std::array<uint8_t, kLargeBlobKeyLength>> large_blob_key;
 
   // user_selected indicates that the authenticator has a UI and has already
   // shown the user an account chooser for the empty-allowList request.
   bool user_selected = false;
 
   // The large blob associated with the credential.
-  absl::optional<std::vector<uint8_t>> large_blob;
+  std::optional<std::vector<uint8_t>> large_blob;
 
   // Whether a large blob was successfully written as part of this GetAssertion
   // request.
@@ -83,11 +83,11 @@
   // Contains the compressed largeBlob data when the extension form is used.
   // This will be decompressed during processing and used to populate
   // `large_blob`.
-  absl::optional<LargeBlob> large_blob_extension;
+  std::optional<LargeBlob> large_blob_extension;
 
   // The transport used to generate this response. This is unknown when using
   // the Windows WebAuthn API.
-  absl::optional<FidoTransportProtocol> transport_used;
+  std::optional<FidoTransportProtocol> transport_used;
 };
 
 }  // namespace device
diff --git a/device/fido/authenticator_get_info_response.h b/device/fido/authenticator_get_info_response.h
index 09b68ff..c4f5ab0 100644
--- a/device/fido/authenticator_get_info_response.h
+++ b/device/fido/authenticator_get_info_response.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -16,7 +17,6 @@
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_transport_protocol.h"
 #include "device/fido/fido_types.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -48,17 +48,17 @@
   base::flat_set<ProtocolVersion> versions;
   base::flat_set<Ctap2Version> ctap2_versions;
   std::array<uint8_t, kAaguidLength> aaguid;
-  absl::optional<uint32_t> max_msg_size;
-  absl::optional<uint32_t> max_credential_count_in_list;
-  absl::optional<uint32_t> max_credential_id_length;
-  absl::optional<base::flat_set<PINUVAuthProtocol>> pin_protocols;
-  absl::optional<std::vector<std::string>> extensions;
-  absl::optional<std::vector<int32_t>> algorithms;
-  absl::optional<uint32_t> max_serialized_large_blob_array;
-  absl::optional<uint32_t> remaining_discoverable_credentials;
-  absl::optional<bool> force_pin_change;
-  absl::optional<uint32_t> min_pin_length;
-  absl::optional<base::flat_set<FidoTransportProtocol>> transports;
+  std::optional<uint32_t> max_msg_size;
+  std::optional<uint32_t> max_credential_count_in_list;
+  std::optional<uint32_t> max_credential_id_length;
+  std::optional<base::flat_set<PINUVAuthProtocol>> pin_protocols;
+  std::optional<std::vector<std::string>> extensions;
+  std::optional<std::vector<int32_t>> algorithms;
+  std::optional<uint32_t> max_serialized_large_blob_array;
+  std::optional<uint32_t> remaining_discoverable_credentials;
+  std::optional<bool> force_pin_change;
+  std::optional<uint32_t> min_pin_length;
+  std::optional<base::flat_set<FidoTransportProtocol>> transports;
   AuthenticatorSupportedOptions options;
 };
 
diff --git a/device/fido/authenticator_make_credential_response.cc b/device/fido/authenticator_make_credential_response.cc
index 500e1df6..53b56579 100644
--- a/device/fido/authenticator_make_credential_response.cc
+++ b/device/fido/authenticator_make_credential_response.cc
@@ -20,22 +20,22 @@
 namespace device {
 
 // static
-absl::optional<AuthenticatorMakeCredentialResponse>
+std::optional<AuthenticatorMakeCredentialResponse>
 AuthenticatorMakeCredentialResponse::CreateFromU2fRegisterResponse(
-    absl::optional<FidoTransportProtocol> transport_used,
+    std::optional<FidoTransportProtocol> transport_used,
     base::span<const uint8_t, kRpIdHashLength> relying_party_id_hash,
     base::span<const uint8_t> u2f_data) {
   auto public_key = P256PublicKey::ExtractFromU2fRegistrationResponse(
       static_cast<int32_t>(CoseAlgorithmIdentifier::kEs256), u2f_data);
   if (!public_key)
-    return absl::nullopt;
+    return std::nullopt;
 
   auto attested_credential_data =
       AttestedCredentialData::CreateFromU2fRegisterResponse(
           u2f_data, std::move(public_key));
 
   if (!attested_credential_data)
-    return absl::nullopt;
+    return std::nullopt;
 
   // Extract the credential_id for packing into the response data.
   std::vector<uint8_t> credential_id =
@@ -54,7 +54,7 @@
       FidoAttestationStatement::CreateFromU2fRegisterResponse(u2f_data);
 
   if (!fido_attestation_statement)
-    return absl::nullopt;
+    return std::nullopt;
 
   AuthenticatorMakeCredentialResponse response(
       transport_used, AttestationObject(std::move(authenticator_data),
@@ -64,7 +64,7 @@
 }
 
 AuthenticatorMakeCredentialResponse::AuthenticatorMakeCredentialResponse(
-    absl::optional<FidoTransportProtocol> in_transport_used,
+    std::optional<FidoTransportProtocol> in_transport_used,
     AttestationObject in_attestation_object)
     : attestation_object(std::move(in_attestation_object)),
       transport_used(in_transport_used) {}
diff --git a/device/fido/authenticator_make_credential_response.h b/device/fido/authenticator_make_credential_response.h
index a83e1ae..05e40361 100644
--- a/device/fido/authenticator_make_credential_response.h
+++ b/device/fido/authenticator_make_credential_response.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <array>
+#include <optional>
 #include <vector>
 
 #include "base/component_export.h"
@@ -16,7 +17,6 @@
 #include "device/fido/attestation_object.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_transport_protocol.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -26,14 +26,14 @@
 // https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html#authenticatorMakeCredential
 class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorMakeCredentialResponse {
  public:
-  static absl::optional<AuthenticatorMakeCredentialResponse>
+  static std::optional<AuthenticatorMakeCredentialResponse>
   CreateFromU2fRegisterResponse(
-      absl::optional<FidoTransportProtocol> transport_used,
+      std::optional<FidoTransportProtocol> transport_used,
       base::span<const uint8_t, kRpIdHashLength> relying_party_id_hash,
       base::span<const uint8_t> u2f_data);
 
   AuthenticatorMakeCredentialResponse(
-      absl::optional<FidoTransportProtocol> transport_used,
+      std::optional<FidoTransportProtocol> transport_used,
       AttestationObject attestation_object);
   AuthenticatorMakeCredentialResponse(
       AuthenticatorMakeCredentialResponse&& that);
@@ -62,7 +62,7 @@
   // is_resident_key indicates whether the created credential is client-side
   // discoverable. It is nullopt if no discoverable credential was requested,
   // but the authenticator may have created one anyway.
-  absl::optional<bool> is_resident_key;
+  std::optional<bool> is_resident_key;
 
   // attestation_should_be_filtered is true iff a filter indicated that the
   // attestation should not be returned. This is acted upon by
@@ -71,17 +71,17 @@
 
   // transports contains the full set of transports supported by the
   // authenticator, if known.
-  absl::optional<base::flat_set<FidoTransportProtocol>> transports;
+  std::optional<base::flat_set<FidoTransportProtocol>> transports;
 
   // Contains the transport used to register the credential in this case. It is
   // nullopt for cases where we cannot determine the transport (Windows).
-  absl::optional<FidoTransportProtocol> transport_used;
+  std::optional<FidoTransportProtocol> transport_used;
 
   // Whether the credential that was created has an associated large blob key or
   // supports the largeBlob extension. This can only be true if the credential
   // is created with the largeBlob or largeBlobKey extension on a capable
   // authenticator.
-  absl::optional<LargeBlobSupportType> large_blob_type;
+  std::optional<LargeBlobSupportType> large_blob_type;
 
   // Whether a PRF is configured for this credential. This only reflects the
   // output of the `prf` extension. Any output from the `hmac-secret` extension
@@ -90,7 +90,7 @@
   bool prf_enabled = false;
 
   // hmac-secret contains the output of the prf extension.
-  absl::optional<std::vector<uint8_t>> prf_results;
+  std::optional<std::vector<uint8_t>> prf_results;
 };
 
 // Through cbor::Writer, produces a CTAP style CBOR-encoded byte array
diff --git a/device/fido/authenticator_supported_options.h b/device/fido/authenticator_supported_options.h
index b153257..13a6507c 100644
--- a/device/fido/authenticator_supported_options.h
+++ b/device/fido/authenticator_supported_options.h
@@ -5,10 +5,11 @@
 #ifndef DEVICE_FIDO_AUTHENTICATOR_SUPPORTED_OPTIONS_H_
 #define DEVICE_FIDO_AUTHENTICATOR_SUPPORTED_OPTIONS_H_
 
+#include <optional>
+
 #include "base/component_export.h"
 #include "components/cbor/values.h"
 #include "device/fido/fido_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -103,7 +104,7 @@
   bool enterprise_attestation = false;
   // Whether the authenticator supports large blobs, and, if so, the method of
   // that support.
-  absl::optional<LargeBlobSupportType> large_blob_type;
+  std::optional<LargeBlobSupportType> large_blob_type;
   // Indicates whether user verification must be used for make credential, final
   // (i.e. not pre-flight) get assertion requests, and writing large blobs. An
   // |always_uv| value of true will make uv=0 get assertion requests return
@@ -123,7 +124,7 @@
   bool supports_prf = false;
   // max_cred_blob_length is the longest credBlob value that this authenticator
   // can store. A value of `nullopt` indicates no support for credBlob.
-  absl::optional<uint16_t> max_cred_blob_length;
+  std::optional<uint16_t> max_cred_blob_length;
 };
 
 COMPONENT_EXPORT(DEVICE_FIDO)
diff --git a/device/fido/bio/enroller.cc b/device/fido/bio/enroller.cc
index d02fd0b..04ed880 100644
--- a/device/fido/bio/enroller.cc
+++ b/device/fido/bio/enroller.cc
@@ -16,7 +16,7 @@
                          pin::TokenResponse token)
     : delegate_(delegate), authenticator_(authenticator), token_(token) {
   authenticator_->BioEnrollFingerprint(
-      token_, /*template_id=*/absl::nullopt,
+      token_, /*template_id=*/std::nullopt,
       base::BindOnce(&BioEnroller::OnEnrollResponse,
                      weak_factory_.GetWeakPtr()));
 }
@@ -37,14 +37,14 @@
 }
 
 void BioEnroller::FinishSuccessfully(
-    absl::optional<std::vector<uint8_t>> template_id) {
+    std::optional<std::vector<uint8_t>> template_id) {
   state_ = State::kDone;
   delegate_->OnEnrollmentDone(std::move(template_id));
 }
 
 void BioEnroller::OnEnrollResponse(
     CtapDeviceResponseCode status,
-    absl::optional<BioEnrollmentResponse> response) {
+    std::optional<BioEnrollmentResponse> response) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
 
   if (state_ == State::kCancelled) {
@@ -103,9 +103,9 @@
 
 void BioEnroller::OnEnrollCancelled(
     CtapDeviceResponseCode status,
-    absl::optional<BioEnrollmentResponse> response) {
+    std::optional<BioEnrollmentResponse> response) {
   DCHECK_EQ(state_, State::kCancelled);
-  FinishSuccessfully(/*template_id=*/absl::nullopt);
+  FinishSuccessfully(/*template_id=*/std::nullopt);
 }
 
 }  // namespace device
diff --git a/device/fido/bio/enroller.h b/device/fido/bio/enroller.h
index c5e8b13..8dc32587 100644
--- a/device/fido/bio/enroller.h
+++ b/device/fido/bio/enroller.h
@@ -5,6 +5,7 @@
 #ifndef DEVICE_FIDO_BIO_ENROLLER_H_
 #define DEVICE_FIDO_BIO_ENROLLER_H_
 
+#include <optional>
 #include <vector>
 
 #include "base/memory/raw_ptr.h"
@@ -12,7 +13,6 @@
 #include "base/sequence_checker.h"
 #include "device/fido/bio/enrollment.h"
 #include "device/fido/pin.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -28,9 +28,9 @@
     virtual void OnSampleCollected(BioEnrollmentSampleStatus status,
                                    int samples_remaining) = 0;
     // Called when the enrollment has completed. |template_id| may be
-    // absl::nullopt if the enrollment has been cancelled.
+    // std::nullopt if the enrollment has been cancelled.
     virtual void OnEnrollmentDone(
-        absl::optional<std::vector<uint8_t>> template_id) = 0;
+        std::optional<std::vector<uint8_t>> template_id) = 0;
     virtual void OnEnrollmentError(CtapDeviceResponseCode status) = 0;
   };
 
@@ -57,18 +57,18 @@
   };
 
   void FinishWithError(CtapDeviceResponseCode status);
-  void FinishSuccessfully(absl::optional<std::vector<uint8_t>> template_id);
+  void FinishSuccessfully(std::optional<std::vector<uint8_t>> template_id);
 
   void OnEnrollResponse(CtapDeviceResponseCode status,
-                        absl::optional<BioEnrollmentResponse> response);
+                        std::optional<BioEnrollmentResponse> response);
   void OnEnrollCancelled(CtapDeviceResponseCode status,
-                         absl::optional<BioEnrollmentResponse> response);
+                         std::optional<BioEnrollmentResponse> response);
 
   State state_ = State::kInProgress;
   raw_ptr<Delegate> delegate_;
   raw_ptr<FidoAuthenticator> authenticator_;
   pin::TokenResponse token_;
-  absl::optional<std::vector<uint8_t>> template_id_;
+  std::optional<std::vector<uint8_t>> template_id_;
 
   SEQUENCE_CHECKER(my_sequence_checker_);
   base::WeakPtrFactory<BioEnroller> weak_factory_{this};
diff --git a/device/fido/bio/enrollment.cc b/device/fido/bio/enrollment.cc
index 11aefa2..4308cd1 100644
--- a/device/fido/bio/enrollment.cc
+++ b/device/fido/bio/enrollment.cc
@@ -126,8 +126,8 @@
 BioEnrollmentRequest::~BioEnrollmentRequest() = default;
 
 // static
-absl::optional<BioEnrollmentResponse> BioEnrollmentResponse::Parse(
-    const absl::optional<cbor::Value>& cbor_response) {
+std::optional<BioEnrollmentResponse> BioEnrollmentResponse::Parse(
+    const std::optional<cbor::Value>& cbor_response) {
   BioEnrollmentResponse response;
 
   if (!cbor_response || !cbor_response->is_map()) {
@@ -141,12 +141,12 @@
       cbor::Value(static_cast<int>(BioEnrollmentResponseKey::kModality)));
   if (it != response_map.end()) {
     if (!it->second.is_unsigned()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     response.modality =
         ToBioEnrollmentEnum<BioEnrollmentModality>(it->second.GetUnsigned());
     if (!response.modality) {
-      return absl::nullopt;
+      return std::nullopt;
     }
   }
 
@@ -155,13 +155,13 @@
       static_cast<int>(BioEnrollmentResponseKey::kFingerprintKind)));
   if (it != response_map.end()) {
     if (!it->second.is_unsigned()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     response.fingerprint_kind =
         ToBioEnrollmentEnum<BioEnrollmentFingerprintKind>(
             it->second.GetUnsigned());
     if (!response.fingerprint_kind) {
-      return absl::nullopt;
+      return std::nullopt;
     }
   }
 
@@ -171,7 +171,7 @@
   if (it != response_map.end()) {
     if (!it->second.is_unsigned() ||
         it->second.GetUnsigned() > std::numeric_limits<uint8_t>::max()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     response.max_samples_for_enroll = it->second.GetUnsigned();
   }
@@ -181,7 +181,7 @@
       cbor::Value(static_cast<int>(BioEnrollmentResponseKey::kTemplateId)));
   if (it != response_map.end()) {
     if (!it->second.is_bytestring()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     response.template_id = it->second.GetBytestring();
   }
@@ -191,12 +191,12 @@
       static_cast<int>(BioEnrollmentResponseKey::kLastEnrollSampleStatus)));
   if (it != response_map.end()) {
     if (!it->second.is_unsigned()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     response.last_status = ToBioEnrollmentEnum<BioEnrollmentSampleStatus>(
         it->second.GetUnsigned());
     if (!response.last_status) {
-      return absl::nullopt;
+      return std::nullopt;
     }
   }
 
@@ -206,7 +206,7 @@
   if (it != response_map.end()) {
     if (!it->second.is_unsigned() ||
         it->second.GetUnsigned() > std::numeric_limits<uint8_t>::max()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     response.remaining_samples = it->second.GetUnsigned();
   }
@@ -216,13 +216,13 @@
       cbor::Value(static_cast<int>(BioEnrollmentResponseKey::kTemplateInfos)));
   if (it != response_map.end()) {
     if (!it->second.is_array()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     std::map<std::vector<uint8_t>, std::string> template_infos;
     for (const auto& bio_template : it->second.GetArray()) {
       if (!bio_template.is_map()) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       const cbor::Value::MapValue& template_map = bio_template.GetMap();
 
@@ -231,12 +231,12 @@
           static_cast<int>(BioEnrollmentTemplateInfoParam::kTemplateId)));
       if (template_it == template_map.end() ||
           !template_it->second.is_bytestring()) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       std::vector<uint8_t> id = template_it->second.GetBytestring();
       if (template_infos.find(id) != template_infos.end()) {
         // Found an existing ID, invalid response.
-        return absl::nullopt;
+        return std::nullopt;
       }
 
       // name (optional)
@@ -245,7 +245,7 @@
           BioEnrollmentTemplateInfoParam::kTemplateFriendlyName)));
       if (template_it != template_map.end()) {
         if (!template_it->second.is_string()) {
-          return absl::nullopt;
+          return std::nullopt;
         }
         name = template_it->second.GetString();
       }
@@ -259,7 +259,7 @@
   if (it != response_map.end()) {
     if (!it->second.is_unsigned() ||
         it->second.GetUnsigned() > std::numeric_limits<uint32_t>::max()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     response.max_template_friendly_name = it->second.GetUnsigned();
   }
@@ -279,7 +279,7 @@
          template_infos == r.template_infos;
 }
 
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const BioEnrollmentRequest& request) {
   cbor::Value::MapValue map;
 
diff --git a/device/fido/bio/enrollment.h b/device/fido/bio/enrollment.h
index cebc3a97..8cb2944 100644
--- a/device/fido/bio/enrollment.h
+++ b/device/fido/bio/enrollment.h
@@ -6,12 +6,12 @@
 #define DEVICE_FIDO_BIO_ENROLLMENT_H_
 
 #include <map>
+#include <optional>
 
 #include "base/component_export.h"
 #include "components/cbor/values.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/pin.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -97,11 +97,11 @@
 };
 
 template <typename T>
-static absl::optional<T> ToBioEnrollmentEnum(uint8_t v) {
+static std::optional<T> ToBioEnrollmentEnum(uint8_t v) {
   // Check if enum-class is in range...
   if (v < static_cast<int>(T::kMin) || v > static_cast<int>(T::kMax)) {
     // ...to avoid possible undefined behavior (casting from int to enum).
-    return absl::nullopt;
+    return std::nullopt;
   }
   return static_cast<T>(v);
 }
@@ -133,12 +133,12 @@
                                         std::vector<uint8_t> id);
 
   Version version;
-  absl::optional<BioEnrollmentModality> modality;
-  absl::optional<BioEnrollmentSubCommand> subcommand;
-  absl::optional<cbor::Value::MapValue> params;
-  absl::optional<PINUVAuthProtocol> pin_protocol;
-  absl::optional<std::vector<uint8_t>> pin_auth;
-  absl::optional<bool> get_modality;
+  std::optional<BioEnrollmentModality> modality;
+  std::optional<BioEnrollmentSubCommand> subcommand;
+  std::optional<cbor::Value::MapValue> params;
+  std::optional<PINUVAuthProtocol> pin_protocol;
+  std::optional<std::vector<uint8_t>> pin_auth;
+  std::optional<bool> get_modality;
 
   BioEnrollmentRequest(BioEnrollmentRequest&&);
   BioEnrollmentRequest& operator=(BioEnrollmentRequest&&);
@@ -149,8 +149,8 @@
 };
 
 struct COMPONENT_EXPORT(DEVICE_FIDO) BioEnrollmentResponse {
-  static absl::optional<BioEnrollmentResponse> Parse(
-      const absl::optional<cbor::Value>& cbor_response);
+  static std::optional<BioEnrollmentResponse> Parse(
+      const std::optional<cbor::Value>& cbor_response);
 
   BioEnrollmentResponse();
   BioEnrollmentResponse(BioEnrollmentResponse&&);
@@ -159,18 +159,18 @@
 
   bool operator==(const BioEnrollmentResponse&) const;
 
-  absl::optional<BioEnrollmentModality> modality;
-  absl::optional<BioEnrollmentFingerprintKind> fingerprint_kind;
-  absl::optional<uint8_t> max_samples_for_enroll;
-  absl::optional<std::vector<uint8_t>> template_id;
-  absl::optional<BioEnrollmentSampleStatus> last_status;
-  absl::optional<uint8_t> remaining_samples;
-  absl::optional<std::map<std::vector<uint8_t>, std::string>> template_infos;
-  absl::optional<uint32_t> max_template_friendly_name;
+  std::optional<BioEnrollmentModality> modality;
+  std::optional<BioEnrollmentFingerprintKind> fingerprint_kind;
+  std::optional<uint8_t> max_samples_for_enroll;
+  std::optional<std::vector<uint8_t>> template_id;
+  std::optional<BioEnrollmentSampleStatus> last_status;
+  std::optional<uint8_t> remaining_samples;
+  std::optional<std::map<std::vector<uint8_t>, std::string>> template_infos;
+  std::optional<uint32_t> max_template_friendly_name;
 };
 
 COMPONENT_EXPORT(DEVICE_FIDO)
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const BioEnrollmentRequest& request);
 
 }  // namespace device
diff --git a/device/fido/bio/enrollment_handler.cc b/device/fido/bio/enrollment_handler.cc
index c87c6566..c4e37a1 100644
--- a/device/fido/bio/enrollment_handler.cc
+++ b/device/fido/bio/enrollment_handler.cc
@@ -120,7 +120,7 @@
 }
 
 void BioEnrollmentHandler::OnEnrollmentDone(
-    absl::optional<std::vector<uint8_t>> template_id) {
+    std::optional<std::vector<uint8_t>> template_id) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   bio_enroller_.reset();
@@ -183,7 +183,7 @@
 
 void BioEnrollmentHandler::OnRetriesResponse(
     CtapDeviceResponseCode status,
-    absl::optional<pin::RetriesResponse> response) {
+    std::optional<pin::RetriesResponse> response) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(state_, State::kGettingRetries);
   if (!response || status != CtapDeviceResponseCode::kSuccess) {
@@ -209,14 +209,14 @@
   state_ = State::kGettingPINToken;
   authenticator_->GetPINToken(
       std::move(pin), {pin::Permissions::kBioEnrollment},
-      /*rp_id=*/absl::nullopt,
+      /*rp_id=*/std::nullopt,
       base::BindOnce(&BioEnrollmentHandler::OnHavePINToken,
                      weak_factory_.GetWeakPtr()));
 }
 
 void BioEnrollmentHandler::OnHavePINToken(
     CtapDeviceResponseCode status,
-    absl::optional<pin::TokenResponse> response) {
+    std::optional<pin::TokenResponse> response) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(state_, State::kGettingPINToken);
 
@@ -248,7 +248,7 @@
 
 void BioEnrollmentHandler::OnGetSensorInfo(
     CtapDeviceResponseCode status,
-    absl::optional<BioEnrollmentResponse> response) {
+    std::optional<BioEnrollmentResponse> response) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(state_, State::kGettingSensorInfo);
   if (status != CtapDeviceResponseCode::kSuccess) {
@@ -267,14 +267,14 @@
 void BioEnrollmentHandler::OnEnumerateTemplates(
     EnumerationCallback callback,
     CtapDeviceResponseCode status,
-    absl::optional<BioEnrollmentResponse> response) {
+    std::optional<BioEnrollmentResponse> response) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(state_, State::kEnumerating);
 
   state_ = State::kReady;
 
   if (status != CtapDeviceResponseCode::kSuccess) {
-    std::move(callback).Run(status, absl::nullopt);
+    std::move(callback).Run(status, std::nullopt);
     return;
   }
 
@@ -289,7 +289,7 @@
 void BioEnrollmentHandler::OnRenameTemplate(
     StatusCallback callback,
     CtapDeviceResponseCode status,
-    absl::optional<BioEnrollmentResponse> response) {
+    std::optional<BioEnrollmentResponse> response) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(state_, State::kRenaming);
   state_ = State::kReady;
@@ -299,7 +299,7 @@
 void BioEnrollmentHandler::OnDeleteTemplate(
     StatusCallback callback,
     CtapDeviceResponseCode status,
-    absl::optional<BioEnrollmentResponse> response) {
+    std::optional<BioEnrollmentResponse> response) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(state_, State::kDeleting);
   state_ = State::kReady;
diff --git a/device/fido/bio/enrollment_handler.h b/device/fido/bio/enrollment_handler.h
index 80c011e..af01981e 100644
--- a/device/fido/bio/enrollment_handler.h
+++ b/device/fido/bio/enrollment_handler.h
@@ -46,7 +46,7 @@
     SensorInfo& operator=(const SensorInfo&) = delete;
     SensorInfo& operator=(SensorInfo&&);
 
-    absl::optional<uint8_t> max_samples_for_enroll;
+    std::optional<uint8_t> max_samples_for_enroll;
     uint32_t max_template_friendly_name;
   };
 
@@ -73,7 +73,7 @@
   // EnumerationCallback is invoked upon the completion of EnumerateTemplates.
   using EnumerationCallback = base::OnceCallback<void(
       CtapDeviceResponseCode,
-      absl::optional<std::map<TemplateId, std::string>>)>;
+      std::optional<std::map<TemplateId, std::string>>)>;
 
   // SampleCallback is invoked repeatedly during an ongoing EnrollTemplate()
   // operation to indicate the status of a single sample collection (i.e. user
@@ -111,7 +111,7 @@
 
   // Requests a map of current enrollments from the authenticator. On success,
   // the callback is invoked with a map from template IDs to human-readable
-  // names. On failure, the callback is invoked with absl::nullopt.
+  // names. On failure, the callback is invoked with std::nullopt.
   void EnumerateTemplates(EnumerationCallback);
 
   // Renames the enrollment identified by |template_id| to |name|.
@@ -146,26 +146,26 @@
   void OnSampleCollected(BioEnrollmentSampleStatus status,
                          int samples_remaining) override;
   void OnEnrollmentDone(
-      absl::optional<std::vector<uint8_t>> template_id) override;
+      std::optional<std::vector<uint8_t>> template_id) override;
   void OnEnrollmentError(CtapDeviceResponseCode status) override;
 
   void OnTouch(FidoAuthenticator* authenticator);
   void OnRetriesResponse(CtapDeviceResponseCode,
-                         absl::optional<pin::RetriesResponse>);
+                         std::optional<pin::RetriesResponse>);
   void OnHavePIN(std::string pin);
   void OnHavePINToken(CtapDeviceResponseCode,
-                      absl::optional<pin::TokenResponse>);
+                      std::optional<pin::TokenResponse>);
   void OnGetSensorInfo(CtapDeviceResponseCode,
-                       absl::optional<BioEnrollmentResponse>);
+                       std::optional<BioEnrollmentResponse>);
   void OnEnumerateTemplates(EnumerationCallback,
                             CtapDeviceResponseCode,
-                            absl::optional<BioEnrollmentResponse>);
+                            std::optional<BioEnrollmentResponse>);
   void OnRenameTemplate(StatusCallback,
                         CtapDeviceResponseCode,
-                        absl::optional<BioEnrollmentResponse>);
+                        std::optional<BioEnrollmentResponse>);
   void OnDeleteTemplate(StatusCallback,
                         CtapDeviceResponseCode,
-                        absl::optional<BioEnrollmentResponse>);
+                        std::optional<BioEnrollmentResponse>);
 
   void RunErrorCallback(Error error);
 
@@ -180,7 +180,7 @@
   GetPINCallback get_pin_callback_;
   EnrollmentCallback enrollment_callback_;
   SampleCallback sample_callback_;
-  absl::optional<pin::TokenResponse> pin_token_response_;
+  std::optional<pin::TokenResponse> pin_token_response_;
   base::WeakPtrFactory<BioEnrollmentHandler> weak_factory_{this};
 };
 
diff --git a/device/fido/bio/enrollment_handler_unittest.cc b/device/fido/bio/enrollment_handler_unittest.cc
index f27d019e..72722581 100644
--- a/device/fido/bio/enrollment_handler_unittest.cc
+++ b/device/fido/bio/enrollment_handler_unittest.cc
@@ -187,7 +187,7 @@
   // Enumerate to check enrollments.
   test::StatusAndValueCallbackReceiver<
       CtapDeviceResponseCode,
-      absl::optional<std::map<std::vector<uint8_t>, std::string>>>
+      std::optional<std::map<std::vector<uint8_t>, std::string>>>
       cb;
   handler->EnumerateTemplates(cb.callback());
   cb.WaitForCallback();
@@ -236,12 +236,12 @@
 
   test::StatusAndValueCallbackReceiver<
       CtapDeviceResponseCode,
-      absl::optional<std::map<std::vector<uint8_t>, std::string>>>
+      std::optional<std::map<std::vector<uint8_t>, std::string>>>
       cb;
   handler->EnumerateTemplates(cb.callback());
   cb.WaitForCallback();
   EXPECT_EQ(cb.status(), CtapDeviceResponseCode::kCtap2ErrInvalidOption);
-  EXPECT_EQ(cb.value(), absl::nullopt);
+  EXPECT_EQ(cb.value(), std::nullopt);
 }
 
 // Tests enumerating with one enrollment.
@@ -263,7 +263,7 @@
   // Enumerate
   test::StatusAndValueCallbackReceiver<
       CtapDeviceResponseCode,
-      absl::optional<std::map<std::vector<uint8_t>, std::string>>>
+      std::optional<std::map<std::vector<uint8_t>, std::string>>>
       cb1;
   handler->EnumerateTemplates(cb1.callback());
   cb1.WaitForCallback();
@@ -303,7 +303,7 @@
   // Enumerate to validate renaming.
   test::StatusAndValueCallbackReceiver<
       CtapDeviceResponseCode,
-      absl::optional<std::map<std::vector<uint8_t>, std::string>>>
+      std::optional<std::map<std::vector<uint8_t>, std::string>>>
       cb3;
   handler->EnumerateTemplates(cb3.callback());
   cb3.WaitForCallback();
diff --git a/device/fido/cable/cable_discovery_data.cc b/device/fido/cable/cable_discovery_data.cc
index c4971a0..9ba66962 100644
--- a/device/fido/cable/cable_discovery_data.cc
+++ b/device/fido/cable/cable_discovery_data.cc
@@ -110,13 +110,13 @@
 Pairing::~Pairing() = default;
 
 // static
-absl::optional<std::unique_ptr<Pairing>> Pairing::Parse(
+std::optional<std::unique_ptr<Pairing>> Pairing::Parse(
     const cbor::Value& cbor,
     tunnelserver::KnownDomainID domain,
     base::span<const uint8_t, kQRSeedSize> local_identity_seed,
     base::span<const uint8_t, 32> handshake_hash) {
   if (!cbor.is_map()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   const cbor::Value::MapValue& map = cbor.GetMap();
@@ -136,7 +136,7 @@
           }) ||
       its[3]->second.GetBytestring().size() !=
           std::tuple_size<decltype(pairing->peer_public_key_x962)>::value) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   pairing->tunnel_server_domain = domain;
@@ -150,7 +150,7 @@
   if (!VerifyPairingSignature(local_identity_seed,
                               pairing->peer_public_key_x962, handshake_hash,
                               its[4]->second.GetBytestring())) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   const auto play_services_tag_it = map.find(cbor::Value(999));
diff --git a/device/fido/cable/cable_discovery_data.h b/device/fido/cable/cable_discovery_data.h
index 7d224c2..349b467c 100644
--- a/device/fido/cable/cable_discovery_data.h
+++ b/device/fido/cable/cable_discovery_data.h
@@ -9,6 +9,7 @@
 
 #include <array>
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -17,7 +18,6 @@
 #include "base/time/time.h"
 #include "device/fido/cable/v2_constants.h"
 #include "device/fido/fido_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 #include "third_party/icu/source/common/unicode/uversion.h"
 
@@ -91,7 +91,7 @@
     CableEidArray authenticator_eid;
     CableSessionPreKeyArray session_pre_key;
   };
-  absl::optional<V1Data> v1;
+  std::optional<V1Data> v1;
 
   // For caBLEv2, the payload is the server-link data provided in the extension
   // as the "sessionPreKey".
@@ -105,7 +105,7 @@
     std::vector<uint8_t> server_link_data;
     std::vector<uint8_t> experiments;
   };
-  absl::optional<V2Data> v2;
+  std::optional<V2Data> v2;
 };
 
 namespace cablev2 {
@@ -138,7 +138,7 @@
   // Parse builds a `Pairing` from an authenticator message. The signature
   // within the structure is validated by using `local_identity_seed` and
   // `handshake_hash`.
-  static absl::optional<std::unique_ptr<Pairing>> Parse(
+  static std::optional<std::unique_ptr<Pairing>> Parse(
       const cbor::Value& cbor,
       tunnelserver::KnownDomainID domain,
       base::span<const uint8_t, kQRSeedSize> local_identity_seed,
diff --git a/device/fido/cable/fido_ble_connection.cc b/device/fido/cable/fido_ble_connection.cc
index 0bf1ff3..2b369158 100644
--- a/device/fido/cable/fido_ble_connection.cc
+++ b/device/fido/cable/fido_ble_connection.cc
@@ -108,7 +108,7 @@
 
 void OnReadServiceRevisionBitfield(
     ServiceRevisionsCallback callback,
-    absl::optional<device::BluetoothGattService::GattErrorCode> error_code,
+    std::optional<device::BluetoothGattService::GattErrorCode> error_code,
     const std::vector<uint8_t>& value) {
   if (error_code.has_value()) {
     FIDO_LOG(ERROR) << "Error while reading Service Revision Bitfield: "
@@ -196,14 +196,14 @@
   const auto* fido_service = GetFidoService();
   if (!fido_service) {
     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), absl::nullopt));
+        FROM_HERE, base::BindOnce(std::move(callback), std::nullopt));
     return;
   }
 
   if (!control_point_length_id_) {
     FIDO_LOG(ERROR) << "Failed to get Control Point Length.";
     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), absl::nullopt));
+        FROM_HERE, base::BindOnce(std::move(callback), std::nullopt));
     return;
   }
 
@@ -212,7 +212,7 @@
   if (!control_point_length) {
     FIDO_LOG(ERROR) << "No Control Point Length characteristic present.";
     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), absl::nullopt));
+        FROM_HERE, base::BindOnce(std::move(callback), std::nullopt));
     return;
   }
 
@@ -267,7 +267,7 @@
 
 void FidoBleConnection::OnCreateGattConnection(
     std::unique_ptr<BluetoothGattConnection> connection,
-    absl::optional<BluetoothDevice::ConnectErrorCode> error_code) {
+    std::optional<BluetoothDevice::ConnectErrorCode> error_code) {
   FIDO_LOG(DEBUG) << "GATT connection created";
   DCHECK(pending_connection_callback_);
   if (error_code.has_value()) {
@@ -514,18 +514,18 @@
 // static
 void FidoBleConnection::OnReadControlPointLength(
     ControlPointLengthCallback callback,
-    absl::optional<device::BluetoothGattService::GattErrorCode> error_code,
+    std::optional<device::BluetoothGattService::GattErrorCode> error_code,
     const std::vector<uint8_t>& value) {
   if (error_code.has_value()) {
     FIDO_LOG(ERROR) << "Error reading Control Point Length: "
                     << ToString(error_code.value());
-    std::move(callback).Run(absl::nullopt);
+    std::move(callback).Run(std::nullopt);
     return;
   }
   if (value.size() != 2) {
     FIDO_LOG(ERROR) << "Wrong Control Point Length: " << value.size()
                     << " bytes";
-    std::move(callback).Run(absl::nullopt);
+    std::move(callback).Run(std::nullopt);
     return;
   }
 
diff --git a/device/fido/cable/fido_ble_connection.h b/device/fido/cable/fido_ble_connection.h
index 6d63042..54f8dcbd 100644
--- a/device/fido/cable/fido_ble_connection.h
+++ b/device/fido/cable/fido_ble_connection.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <optional>
 #include <set>
 #include <string>
 #include <vector>
@@ -19,7 +20,6 @@
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/bluetooth_gatt_service.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -50,7 +50,7 @@
   using WriteCallback = base::OnceCallback<void(bool)>;
   using ReadCallback = base::RepeatingCallback<void(std::vector<uint8_t>)>;
   using ControlPointLengthCallback =
-      base::OnceCallback<void(absl::optional<uint16_t>)>;
+      base::OnceCallback<void(std::optional<uint16_t>)>;
 
   FidoBleConnection(BluetoothAdapter* adapter,
                     std::string device_address,
@@ -93,7 +93,7 @@
 
   void OnCreateGattConnection(
       std::unique_ptr<BluetoothGattConnection> connection,
-      absl::optional<BluetoothDevice::ConnectErrorCode> error_code);
+      std::optional<BluetoothDevice::ConnectErrorCode> error_code);
 
   void ConnectToFidoService();
   void OnReadServiceRevisions(std::vector<ServiceRevision> service_revisions);
@@ -109,7 +109,7 @@
 
   static void OnReadControlPointLength(
       ControlPointLengthCallback callback,
-      absl::optional<device::BluetoothGattService::GattErrorCode> error_code,
+      std::optional<device::BluetoothGattService::GattErrorCode> error_code,
       const std::vector<uint8_t>& value);
 
   std::unique_ptr<BluetoothGattConnection> connection_;
@@ -119,11 +119,11 @@
   bool waiting_for_gatt_discovery_ = false;
   const BluetoothUUID service_uuid_;
 
-  absl::optional<std::string> control_point_length_id_;
-  absl::optional<std::string> control_point_id_;
-  absl::optional<std::string> status_id_;
-  absl::optional<std::string> service_revision_id_;
-  absl::optional<std::string> service_revision_bitfield_id_;
+  std::optional<std::string> control_point_length_id_;
+  std::optional<std::string> control_point_id_;
+  std::optional<std::string> status_id_;
+  std::optional<std::string> service_revision_id_;
+  std::optional<std::string> service_revision_bitfield_id_;
 
   base::WeakPtrFactory<FidoBleConnection> weak_factory_{this};
 };
diff --git a/device/fido/cable/fido_ble_connection_unittest.cc b/device/fido/cable/fido_ble_connection_unittest.cc
index 1e0a628..4b787fc8 100644
--- a/device/fido/cable/fido_ble_connection_unittest.cc
+++ b/device/fido/cable/fido_ble_connection_unittest.cc
@@ -98,13 +98,13 @@
 
  private:
   std::vector<uint8_t> value_;
-  absl::optional<base::RunLoop> run_loop_{absl::in_place};
+  std::optional<base::RunLoop> run_loop_{std::in_place};
 };
 
 using TestConnectionCallbackReceiver = test::ValueCallbackReceiver<bool>;
 
 using TestReadControlPointLengthCallback =
-    test::ValueCallbackReceiver<absl::optional<uint16_t>>;
+    test::ValueCallbackReceiver<std::optional<uint16_t>>;
 
 using TestReadServiceRevisionsCallback =
     test::ValueCallbackReceiver<std::set<FidoBleConnection::ServiceRevision>>;
@@ -152,7 +152,7 @@
               new NiceMockBluetoothGattConnection(adapter_, device_address);
           std::move(callback).Run(
               std::move(base::WrapUnique(connection_.get())),
-              /*error_code=*/absl::nullopt);
+              /*error_code=*/std::nullopt);
         }));
 
     ON_CALL(*fido_device_, IsGattServicesDiscoveryComplete)
@@ -163,7 +163,7 @@
             [=](BluetoothRemoteGattCharacteristic::ValueCallback& callback) {
               base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
                   FROM_HERE, base::BindOnce(std::move(callback),
-                                            /*error_code=*/absl::nullopt,
+                                            /*error_code=*/std::nullopt,
                                             std::vector<uint8_t>(
                                                 {kDefaultServiceRevision})));
             }));
@@ -239,7 +239,7 @@
         .WillOnce(Invoke(
             [success, value](
                 BluetoothRemoteGattCharacteristic::ValueCallback& callback) {
-              absl::optional<BluetoothGattService::GattErrorCode> error_code;
+              std::optional<BluetoothGattService::GattErrorCode> error_code;
               if (!success)
                 error_code = BluetoothGattService::GattErrorCode::kFailed;
               base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
@@ -254,7 +254,7 @@
         .WillOnce(Invoke(
             [success, value](
                 BluetoothRemoteGattCharacteristic::ValueCallback& callback) {
-              absl::optional<BluetoothGattService::GattErrorCode> error_code;
+              std::optional<BluetoothGattService::GattErrorCode> error_code;
               if (!success)
                 error_code = BluetoothGattService::GattErrorCode::kFailed;
               base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
@@ -271,8 +271,8 @@
             [success, value](
                 BluetoothRemoteGattCharacteristic::ValueCallback& callback) {
               auto error_code =
-                  success ? absl::nullopt
-                          : absl::make_optional(
+                  success ? std::nullopt
+                          : std::make_optional(
                                 BluetoothGattService::GattErrorCode::kFailed);
               base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
                   FROM_HERE,
@@ -681,17 +681,17 @@
     SetNextReadControlPointLengthReponse(false, {});
     connection.ReadControlPointLength(length_callback.callback());
     length_callback.WaitForCallback();
-    EXPECT_EQ(absl::nullopt, length_callback.value());
+    EXPECT_EQ(std::nullopt, length_callback.value());
   }
 
   // The Control Point Length should consist of exactly two bytes, hence we
-  // EXPECT_EQ(absl::nullopt) for payloads of size 0, 1 and 3.
+  // EXPECT_EQ(std::nullopt) for payloads of size 0, 1 and 3.
   {
     TestReadControlPointLengthCallback length_callback;
     SetNextReadControlPointLengthReponse(true, {});
     connection.ReadControlPointLength(length_callback.callback());
     length_callback.WaitForCallback();
-    EXPECT_EQ(absl::nullopt, length_callback.value());
+    EXPECT_EQ(std::nullopt, length_callback.value());
   }
 
   {
@@ -699,7 +699,7 @@
     SetNextReadControlPointLengthReponse(true, {0xAB});
     connection.ReadControlPointLength(length_callback.callback());
     length_callback.WaitForCallback();
-    EXPECT_EQ(absl::nullopt, length_callback.value());
+    EXPECT_EQ(std::nullopt, length_callback.value());
   }
 
   {
@@ -715,7 +715,7 @@
     SetNextReadControlPointLengthReponse(true, {0xAB, 0xCD, 0xEF});
     connection.ReadControlPointLength(length_callback.callback());
     length_callback.WaitForCallback();
-    EXPECT_EQ(absl::nullopt, length_callback.value());
+    EXPECT_EQ(std::nullopt, length_callback.value());
   }
 }
 
@@ -766,7 +766,7 @@
   TestReadControlPointLengthCallback length_callback;
   connection.ReadControlPointLength(length_callback.callback());
   length_callback.WaitForCallback();
-  EXPECT_EQ(absl::nullopt, length_callback.value());
+  EXPECT_EQ(std::nullopt, length_callback.value());
 
   // Writes should always fail on a disconnected device.
   TestWriteCallback write_callback;
diff --git a/device/fido/cable/fido_ble_transaction.cc b/device/fido/cable/fido_ble_transaction.cc
index 74a44a1..1ec82e7 100644
--- a/device/fido/cable/fido_ble_transaction.cc
+++ b/device/fido/cable/fido_ble_transaction.cc
@@ -30,7 +30,7 @@
     FIDO_LOG(DEBUG) << "Control Point Length is too short: "
                     << control_point_length_;
     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), absl::nullopt));
+        FROM_HERE, base::BindOnce(std::move(callback), std::nullopt));
     return;
   }
 
@@ -73,7 +73,7 @@
   has_pending_request_fragment_write_ = false;
   StopTimeout();
   if (!success) {
-    OnError(absl::nullopt);
+    OnError(std::nullopt);
     return;
   }
 
@@ -108,7 +108,7 @@
     FidoBleFrameInitializationFragment fragment;
     if (!FidoBleFrameInitializationFragment::Parse(data, &fragment)) {
       FIDO_LOG(ERROR) << "Malformed Frame Initialization Fragment";
-      OnError(absl::nullopt);
+      OnError(std::nullopt);
       return;
     }
 
@@ -118,7 +118,7 @@
     if (!FidoBleFrameContinuationFragment::Parse(data, &fragment) ||
         !response_frame_assembler_->AddFragment(fragment)) {
       FIDO_LOG(ERROR) << "Malformed Frame Continuation Fragment";
-      OnError(absl::nullopt);
+      OnError(std::nullopt);
       return;
     }
   }
@@ -171,7 +171,7 @@
   if (response_frame.command() == FidoBleDeviceCommand::kKeepAlive) {
     if (!response_frame.IsValid()) {
       FIDO_LOG(ERROR) << "Got invalid KeepAlive Command.";
-      OnError(absl::nullopt);
+      OnError(std::nullopt);
       return;
     }
 
@@ -185,7 +185,7 @@
   if (response_frame.command() == FidoBleDeviceCommand::kError) {
     if (!response_frame.IsValid()) {
       FIDO_LOG(ERROR) << "Got invald Error Command.";
-      OnError(absl::nullopt);
+      OnError(std::nullopt);
       return;
     }
 
@@ -197,20 +197,20 @@
 
   FIDO_LOG(ERROR) << "Got unexpected Command: "
                   << static_cast<int>(response_frame.command());
-  OnError(absl::nullopt);
+  OnError(std::nullopt);
 }
 
 void FidoBleTransaction::StartTimeout() {
   timer_.Start(FROM_HERE, kDeviceTimeout,
                base::BindOnce(&FidoBleTransaction::OnError,
-                              base::Unretained(this), absl::nullopt));
+                              base::Unretained(this), std::nullopt));
 }
 
 void FidoBleTransaction::StopTimeout() {
   timer_.Stop();
 }
 
-void FidoBleTransaction::OnError(absl::optional<FidoBleFrame> response_frame) {
+void FidoBleTransaction::OnError(std::optional<FidoBleFrame> response_frame) {
   request_frame_.reset();
   request_cont_fragments_ = base::queue<FidoBleFrameContinuationFragment>();
   response_frame_assembler_.reset();
diff --git a/device/fido/cable/fido_ble_transaction.h b/device/fido/cable/fido_ble_transaction.h
index 3c9e20e..50bd0ef 100644
--- a/device/fido/cable/fido_ble_transaction.h
+++ b/device/fido/cable/fido_ble_transaction.h
@@ -6,6 +6,7 @@
 #define DEVICE_FIDO_CABLE_FIDO_BLE_TRANSACTION_H_
 
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "base/component_export.h"
@@ -14,7 +15,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
 #include "device/fido/cable/fido_ble_frames.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -25,7 +25,7 @@
 // class that should make use of this class.
 class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleTransaction {
  public:
-  using FrameCallback = base::OnceCallback<void(absl::optional<FidoBleFrame>)>;
+  using FrameCallback = base::OnceCallback<void(std::optional<FidoBleFrame>)>;
 
   FidoBleTransaction(FidoBleConnection* connection,
                      uint16_t control_point_length);
@@ -48,16 +48,16 @@
 
   void StartTimeout();
   void StopTimeout();
-  void OnError(absl::optional<FidoBleFrame> response_frame);
+  void OnError(std::optional<FidoBleFrame> response_frame);
 
   raw_ptr<FidoBleConnection> connection_;
   uint16_t control_point_length_;
 
-  absl::optional<FidoBleFrame> request_frame_;
+  std::optional<FidoBleFrame> request_frame_;
   FrameCallback callback_;
 
   base::queue<FidoBleFrameContinuationFragment> request_cont_fragments_;
-  absl::optional<FidoBleFrameAssembler> response_frame_assembler_;
+  std::optional<FidoBleFrameAssembler> response_frame_assembler_;
 
   std::vector<uint8_t> buffer_;
   base::OneShotTimer timer_;
diff --git a/device/fido/cable/fido_ble_transaction_unittest.cc b/device/fido/cable/fido_ble_transaction_unittest.cc
index 3cd5d14..3e881a8 100644
--- a/device/fido/cable/fido_ble_transaction_unittest.cc
+++ b/device/fido/cable/fido_ble_transaction_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <stdint.h>
 
+#include <optional>
 #include <utility>
 #include <vector>
 
@@ -20,7 +21,6 @@
 #include "device/fido/test_callback_receiver.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -29,7 +29,7 @@
 constexpr uint16_t kDefaultControlPointLength = 20;
 
 using FrameCallbackReceiver =
-    test::ValueCallbackReceiver<absl::optional<FidoBleFrame>>;
+    test::ValueCallbackReceiver<std::optional<FidoBleFrame>>;
 
 std::vector<std::vector<uint8_t>> ToByteFragments(const FidoBleFrame& frame) {
   std::vector<std::vector<uint8_t>> byte_fragments;
@@ -84,7 +84,7 @@
   FrameCallbackReceiver receiver;
   transaction().WriteRequestFrame(FidoBleFrame(), receiver.callback());
   receiver.WaitForCallback();
-  EXPECT_EQ(absl::nullopt, receiver.value());
+  EXPECT_EQ(std::nullopt, receiver.value());
 }
 
 // Tests a case where the control point write succeeds.
@@ -172,7 +172,7 @@
   transaction().WriteRequestFrame(frame, receiver.callback());
 
   receiver.WaitForCallback();
-  EXPECT_EQ(absl::nullopt, receiver.value());
+  EXPECT_EQ(std::nullopt, receiver.value());
 }
 
 // Tests that valid KeepaliveCodes are ignored, and only a valid
@@ -227,7 +227,7 @@
     transaction().OnResponseFragment(std::move(byte_fragment));
 
   receiver.WaitForCallback();
-  EXPECT_EQ(absl::nullopt, receiver.value());
+  EXPECT_EQ(std::nullopt, receiver.value());
 }
 
 // Tests a scenario where the response frame contains a valid error command.
@@ -270,7 +270,7 @@
     transaction().OnResponseFragment(std::move(byte_fragment));
 
   receiver.WaitForCallback();
-  EXPECT_EQ(absl::nullopt, receiver.value());
+  EXPECT_EQ(std::nullopt, receiver.value());
 }
 
 // Tests a scenario where the command of the response frame does not match the
@@ -292,7 +292,7 @@
     transaction().OnResponseFragment(std::move(byte_fragment));
 
   receiver.WaitForCallback();
-  EXPECT_EQ(absl::nullopt, receiver.value());
+  EXPECT_EQ(std::nullopt, receiver.value());
 }
 
 // Tests a scenario where the response initialization fragment is invalid.
@@ -313,7 +313,7 @@
   transaction().OnResponseFragment(std::move(byte_fragments.front()));
 
   receiver.WaitForCallback();
-  EXPECT_EQ(absl::nullopt, receiver.value());
+  EXPECT_EQ(std::nullopt, receiver.value());
 }
 
 // Tests a scenario where a response continuation fragment is invalid.
@@ -336,7 +336,7 @@
   transaction().OnResponseFragment(byte_fragments.front());
 
   receiver.WaitForCallback();
-  EXPECT_EQ(absl::nullopt, receiver.value());
+  EXPECT_EQ(std::nullopt, receiver.value());
 }
 
 // Tests a scenario where the order of response continuation fragments is
@@ -360,7 +360,7 @@
   transaction().OnResponseFragment(byte_fragments[1]);
 
   receiver.WaitForCallback();
-  EXPECT_EQ(absl::nullopt, receiver.value());
+  EXPECT_EQ(std::nullopt, receiver.value());
 }
 
 }  // namespace device
diff --git a/device/fido/cable/fido_cable_device.cc b/device/fido/cable/fido_cable_device.cc
index c2bde98..2fba070 100644
--- a/device/fido/cable/fido_cable_device.cc
+++ b/device/fido/cable/fido_cable_device.cc
@@ -27,12 +27,12 @@
 // counter larger than |kMaxCounter| FidoCableDevice should error out.
 constexpr uint32_t kMaxCounter = (1 << 24) - 1;
 
-absl::optional<std::vector<uint8_t>> ConstructV1Nonce(
+std::optional<std::vector<uint8_t>> ConstructV1Nonce(
     base::span<const uint8_t> nonce,
     bool is_sender_client,
     uint32_t counter) {
   if (counter > kMaxCounter)
-    return absl::nullopt;
+    return std::nullopt;
 
   auto constructed_nonce = fido_parsing_utils::Materialize(nonce);
   constructed_nonce.push_back(is_sender_client ? 0x00 : 0x01);
@@ -124,7 +124,7 @@
     DeviceCallback callback) {
   if (!encryption_data_ || !EncryptOutgoingMessage(&command)) {
     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), absl::nullopt));
+        FROM_HERE, base::BindOnce(std::move(callback), std::nullopt));
     state_ = State::kDeviceError;
     FIDO_LOG(ERROR) << "Failed to encrypt outgoing caBLE message.";
     return 0;
@@ -136,7 +136,7 @@
 }
 
 void FidoCableDevice::OnResponseFrame(FrameCallback callback,
-                                      absl::optional<FidoBleFrame> frame) {
+                                      std::optional<FidoBleFrame> frame) {
   // The request is done, time to reset |transaction_|.
   ResetTransaction();
   state_ = frame ? State::kReady : State::kDeviceError;
@@ -144,7 +144,7 @@
   if (frame && frame->command() != FidoBleDeviceCommand::kControl) {
     if (!encryption_data_ || !DecryptIncomingMessage(&frame.value())) {
       state_ = State::kDeviceError;
-      frame = absl::nullopt;
+      frame = std::nullopt;
     }
   }
 
@@ -185,7 +185,7 @@
         // Respond to any pending frames.
         FrameCallback cb = std::move(pending_frames_.front().callback);
         pending_frames_.pop_front();
-        std::move(cb).Run(absl::nullopt);
+        std::move(cb).Run(std::nullopt);
       }
       break;
   }
@@ -268,8 +268,7 @@
     transaction_->OnResponseFragment(std::move(data));
 }
 
-void FidoCableDevice::OnReadControlPointLength(
-    absl::optional<uint16_t> length) {
+void FidoCableDevice::OnReadControlPointLength(std::optional<uint16_t> length) {
   if (state_ == State::kDeviceError) {
     return;
   }
@@ -308,18 +307,17 @@
   Transition();
 }
 
-void FidoCableDevice::OnBleResponseReceived(
-    DeviceCallback callback,
-    absl::optional<FidoBleFrame> frame) {
+void FidoCableDevice::OnBleResponseReceived(DeviceCallback callback,
+                                            std::optional<FidoBleFrame> frame) {
   if (!frame || !frame->IsValid()) {
     state_ = State::kDeviceError;
-    std::move(callback).Run(absl::nullopt);
+    std::move(callback).Run(std::nullopt);
     return;
   }
 
   if (frame->command() == FidoBleDeviceCommand::kError) {
     ProcessBleDeviceError(frame->data());
-    std::move(callback).Run(absl::nullopt);
+    std::move(callback).Run(std::nullopt);
     return;
   }
 
@@ -378,7 +376,7 @@
 
   const uint8_t additional_data[1] = {
       base::strict_cast<uint8_t>(incoming_frame->command())};
-  absl::optional<std::vector<uint8_t>> plaintext =
+  std::optional<std::vector<uint8_t>> plaintext =
       aes_key.Open(incoming_frame->data(), *nonce, additional_data);
   if (!plaintext) {
     FIDO_LOG(ERROR) << "Failed to decrypt caBLE message.";
diff --git a/device/fido/cable/fido_cable_device.h b/device/fido/cable/fido_cable_device.h
index 10d7210..19255a2c 100644
--- a/device/fido/cable/fido_cable_device.h
+++ b/device/fido/cable/fido_cable_device.h
@@ -7,6 +7,7 @@
 
 #include <array>
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -18,7 +19,6 @@
 #include "device/fido/cable/fido_ble_connection.h"
 #include "device/fido/cable/fido_ble_transaction.h"
 #include "device/fido/fido_device.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -92,7 +92,7 @@
   };
 
   void OnResponseFrame(FrameCallback callback,
-                       absl::optional<FidoBleFrame> frame);
+                       std::optional<FidoBleFrame> frame);
   void Transition();
   CancelToken AddToPendingFrames(FidoBleDeviceCommand cmd,
                                  std::vector<uint8_t> request,
@@ -102,7 +102,7 @@
   void OnConnected(bool success);
   void OnStatusMessage(std::vector<uint8_t> data);
 
-  void OnReadControlPointLength(absl::optional<uint16_t> length);
+  void OnReadControlPointLength(std::optional<uint16_t> length);
 
   void SendRequestFrame(FidoBleFrame frame, FrameCallback callback);
 
@@ -111,7 +111,7 @@
   void OnTimeout();
 
   void OnBleResponseReceived(DeviceCallback callback,
-                             absl::optional<FidoBleFrame> frame);
+                             std::optional<FidoBleFrame> frame);
   void ProcessBleDeviceError(base::span<const uint8_t> data);
 
   bool EncryptOutgoingMessage(std::vector<uint8_t>* message_to_encrypt);
@@ -127,10 +127,10 @@
   std::list<PendingFrame> pending_frames_;
   // current_token_ contains the cancelation token of the currently running
   // request, or else is empty if no request is currently pending.
-  absl::optional<CancelToken> current_token_;
-  absl::optional<FidoBleTransaction> transaction_;
+  std::optional<CancelToken> current_token_;
+  std::optional<FidoBleTransaction> transaction_;
 
-  absl::optional<EncryptionData> encryption_data_;
+  std::optional<EncryptionData> encryption_data_;
   base::WeakPtrFactory<FidoCableDevice> weak_factory_{this};
 };
 
diff --git a/device/fido/cable/fido_cable_device_unittest.cc b/device/fido/cable/fido_cable_device_unittest.cc
index 2b02e003..7fdc306 100644
--- a/device/fido/cable/fido_cable_device_unittest.cc
+++ b/device/fido/cable/fido_cable_device_unittest.cc
@@ -7,6 +7,7 @@
 #include <array>
 #include <limits>
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 
@@ -23,7 +24,6 @@
 #include "device/fido/test_callback_receiver.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -33,7 +33,7 @@
 using ::testing::Invoke;
 using ::testing::Test;
 using TestDeviceCallbackReceiver =
-    test::ValueCallbackReceiver<absl::optional<std::vector<uint8_t>>>;
+    test::ValueCallbackReceiver<std::optional<std::vector<uint8_t>>>;
 using NiceMockBluetoothAdapter = ::testing::NiceMock<MockBluetoothAdapter>;
 
 // Sufficiently large test control point length as we are not interested
diff --git a/device/fido/cable/fido_cable_discovery.cc b/device/fido/cable/fido_cable_discovery.cc
index e2f412f..e9e7263 100644
--- a/device/fido/cable/fido_cable_discovery.cc
+++ b/device/fido/cable/fido_cable_discovery.cc
@@ -346,7 +346,7 @@
 
 void FidoCableDiscovery::OnSessionStarted(
     device::BluetoothLowEnergyScanSession* scan_session,
-    absl::optional<device::BluetoothLowEnergyScanSession::ErrorCode>
+    std::optional<device::BluetoothLowEnergyScanSession::ErrorCode>
         error_code) {
   if (error_code) {
     FIDO_LOG(ERROR) << "Failed to start caBLE LE scan session, error_code = "
@@ -503,8 +503,7 @@
     return;
   }
 
-  absl::optional<V1DiscoveryDataAndEID> v1_match =
-      GetCableDiscoveryData(device);
+  std::optional<V1DiscoveryDataAndEID> v1_match = GetCableDiscoveryData(device);
   if (!v1_match) {
     return;
   }
@@ -554,7 +553,7 @@
 void FidoCableDiscovery::ValidateAuthenticatorHandshakeMessage(
     CableDiscoveryData::Version cable_version,
     FidoCableHandshakeHandler* handshake_handler,
-    absl::optional<std::vector<uint8_t>> handshake_response) {
+    std::optional<std::vector<uint8_t>> handshake_response) {
   const bool ok = handshake_response.has_value() &&
                   handshake_handler->ValidateAuthenticatorHandshakeMessage(
                       *handshake_response);
@@ -582,14 +581,14 @@
   }
 }
 
-absl::optional<FidoCableDiscovery::V1DiscoveryDataAndEID>
+std::optional<FidoCableDiscovery::V1DiscoveryDataAndEID>
 FidoCableDiscovery::GetCableDiscoveryData(const BluetoothDevice* device) {
   const std::vector<uint8_t>* service_data =
       device->GetServiceDataForUUID(GoogleCableUUID());
   if (!service_data) {
     service_data = device->GetServiceDataForUUID(FIDOCableUUID());
   }
-  absl::optional<CableEidArray> maybe_eid_from_service_data =
+  std::optional<CableEidArray> maybe_eid_from_service_data =
       MaybeGetEidFromServiceData(device);
   std::vector<CableEidArray> uuids = GetUUIDs(device);
 
@@ -601,7 +600,7 @@
     if (maybe_eid_from_service_data == data->service_data &&
         uuids == data->uuids) {
       // Duplicate data. Ignore.
-      return absl::nullopt;
+      return std::nullopt;
     }
   }
 
@@ -613,7 +612,7 @@
     FIDO_LOG(DEBUG) << "New caBLE device " << address << ":";
   }
 
-  absl::optional<FidoCableDiscovery::V1DiscoveryDataAndEID> result;
+  std::optional<FidoCableDiscovery::V1DiscoveryDataAndEID> result;
   if (maybe_eid_from_service_data.has_value()) {
     result =
         GetCableDiscoveryDataFromAuthenticatorEid(*maybe_eid_from_service_data);
@@ -652,24 +651,24 @@
 }
 
 // static
-absl::optional<CableEidArray> FidoCableDiscovery::MaybeGetEidFromServiceData(
+std::optional<CableEidArray> FidoCableDiscovery::MaybeGetEidFromServiceData(
     const BluetoothDevice* device) {
   const std::vector<uint8_t>* service_data =
       device->GetServiceDataForUUID(GoogleCableUUID());
   if (!service_data) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   // Received service data from authenticator must have a flag that signals that
   // the service data includes Cable EID.
   if (service_data->empty() || !(service_data->at(0) >> 5 & 1u))
-    return absl::nullopt;
+    return std::nullopt;
 
   CableEidArray received_authenticator_eid;
   bool extract_success = fido_parsing_utils::ExtractArray(
       *service_data, 2, &received_authenticator_eid);
   if (!extract_success)
-    return absl::nullopt;
+    return std::nullopt;
   return received_authenticator_eid;
 }
 
@@ -692,7 +691,7 @@
   return ret;
 }
 
-absl::optional<FidoCableDiscovery::V1DiscoveryDataAndEID>
+std::optional<FidoCableDiscovery::V1DiscoveryDataAndEID>
 FidoCableDiscovery::GetCableDiscoveryDataFromAuthenticatorEid(
     CableEidArray authenticator_eid) {
   for (const auto& candidate : discovery_data_) {
@@ -702,7 +701,7 @@
     }
   }
 
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 void FidoCableDiscovery::StartInternal() {
@@ -713,7 +712,7 @@
 // static
 std::string FidoCableDiscovery::ResultDebugString(
     const CableEidArray& eid,
-    const absl::optional<FidoCableDiscovery::V1DiscoveryDataAndEID>& result) {
+    const std::optional<FidoCableDiscovery::V1DiscoveryDataAndEID>& result) {
   static const uint8_t kAppleContinuity[16] = {
       0xd0, 0x61, 0x1e, 0x78, 0xbb, 0xb4, 0x45, 0x91,
       0xa5, 0xf8, 0x48, 0x79, 0x10, 0xae, 0x43, 0x66,
diff --git a/device/fido/cable/fido_cable_discovery.h b/device/fido/cable/fido_cable_discovery.h
index f3b29ea..f84de8c 100644
--- a/device/fido/cable/fido_cable_discovery.h
+++ b/device/fido/cable/fido_cable_discovery.h
@@ -79,7 +79,7 @@
     ObservedDeviceData();
     ~ObservedDeviceData();
 
-    absl::optional<CableEidArray> service_data;
+    std::optional<CableEidArray> service_data;
     std::vector<CableEidArray> uuids;
   };
 
@@ -91,8 +91,8 @@
   // description of |result|, if present.
   static std::string ResultDebugString(
       const CableEidArray& eid,
-      const absl::optional<V1DiscoveryDataAndEID>& result);
-  static absl::optional<CableEidArray> MaybeGetEidFromServiceData(
+      const std::optional<V1DiscoveryDataAndEID>& result);
+  static std::optional<CableEidArray> MaybeGetEidFromServiceData(
       const BluetoothDevice* device);
   static std::vector<CableEidArray> GetUUIDs(const BluetoothDevice* device);
 
@@ -123,11 +123,11 @@
   void ValidateAuthenticatorHandshakeMessage(
       CableDiscoveryData::Version cable_version,
       FidoCableHandshakeHandler* handshake_handler,
-      absl::optional<std::vector<uint8_t>> handshake_response);
+      std::optional<std::vector<uint8_t>> handshake_response);
 
-  absl::optional<V1DiscoveryDataAndEID> GetCableDiscoveryData(
+  std::optional<V1DiscoveryDataAndEID> GetCableDiscoveryData(
       const BluetoothDevice* device);
-  absl::optional<V1DiscoveryDataAndEID>
+  std::optional<V1DiscoveryDataAndEID>
   GetCableDiscoveryDataFromAuthenticatorEid(CableEidArray authenticator_eid);
 
   // FidoDeviceDiscovery:
@@ -151,7 +151,7 @@
                     device::BluetoothDevice* device) override;
   void OnSessionStarted(
       device::BluetoothLowEnergyScanSession* scan_session,
-      absl::optional<device::BluetoothLowEnergyScanSession::ErrorCode>
+      std::optional<device::BluetoothLowEnergyScanSession::ErrorCode>
           error_code) override;
   void OnSessionInvalidated(
       device::BluetoothLowEnergyScanSession* scan_session) override;
diff --git a/device/fido/cable/fido_cable_discovery_unittest.cc b/device/fido/cable/fido_cable_discovery_unittest.cc
index 6a1f832c..a0313cdb 100644
--- a/device/fido/cable/fido_cable_discovery_unittest.cc
+++ b/device/fido/cable/fido_cable_discovery_unittest.cc
@@ -221,8 +221,8 @@
     service_data_map.emplace(kGoogleCableUUID128, std::move(service_data));
 
     mock_device->UpdateAdvertisementData(
-        1 /* rssi */, absl::nullopt /* flags */, BluetoothDevice::UUIDList(),
-        absl::nullopt /* tx_power */, std::move(service_data_map),
+        1 /* rssi */, std::nullopt /* flags */, BluetoothDevice::UUIDList(),
+        std::nullopt /* tx_power */, std::move(service_data_map),
         BluetoothDevice::ManufacturerDataMap());
 
     auto* mock_device_ptr = mock_device.get();
@@ -316,7 +316,7 @@
                             delegate) {
               EXPECT_TRUE(filter);
               delegate->OnSessionStarted(/*scan_session=*/nullptr,
-                                         /*error_code=*/absl::nullopt);
+                                         /*error_code=*/std::nullopt);
               auto* device = CreateNewTestBluetoothDevice(eid);
               delegate->OnDeviceFound(/*scan_session=*/nullptr, device);
               return nullptr;
diff --git a/device/fido/cable/fido_cable_handshake_handler.cc b/device/fido/cable/fido_cable_handshake_handler.cc
index 016bbb3..b1fbc5f5b 100644
--- a/device/fido/cable/fido_cable_handshake_handler.cc
+++ b/device/fido/cable/fido_cable_handshake_handler.cc
@@ -48,7 +48,7 @@
 
 constexpr size_t kCableHandshakeMacMessageSize = 16;
 
-absl::optional<std::array<uint8_t, kClientHelloMessageSize>>
+std::optional<std::array<uint8_t, kClientHelloMessageSize>>
 ConstructHandshakeMessage(std::string_view handshake_key,
                           base::span<const uint8_t, 16> client_random_nonce) {
   cbor::Value::MapValue map;
@@ -59,12 +59,12 @@
 
   crypto::HMAC hmac(crypto::HMAC::SHA256);
   if (!hmac.Init(handshake_key))
-    return absl::nullopt;
+    return std::nullopt;
 
   std::array<uint8_t, kCableHandshakeMacMessageSize> client_hello_mac;
   if (!hmac.Sign(fido_parsing_utils::ConvertToStringView(*client_hello),
                  client_hello_mac.data(), client_hello_mac.size())) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   DCHECK_EQ(kClientHelloMessageSize,
@@ -104,7 +104,7 @@
       ConstructHandshakeMessage(handshake_key_, client_session_random_);
   if (!handshake_message) {
     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), absl::nullopt));
+        FROM_HERE, base::BindOnce(std::move(callback), std::nullopt));
     return;
   }
 
diff --git a/device/fido/cable/fido_cable_handshake_handler_unittest.cc b/device/fido/cable/fido_cable_handshake_handler_unittest.cc
index b0ed9e2..f87f21f8 100644
--- a/device/fido/cable/fido_cable_handshake_handler_unittest.cc
+++ b/device/fido/cable/fido_cable_handshake_handler_unittest.cc
@@ -7,6 +7,7 @@
 #include <array>
 #include <limits>
 #include <memory>
+#include <optional>
 #include <string>
 #include <string_view>
 #include <utility>
@@ -30,7 +31,6 @@
 #include "device/fido/test_callback_receiver.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -40,7 +40,7 @@
 using ::testing::Invoke;
 using ::testing::Test;
 using TestDeviceCallbackReceiver =
-    test::ValueCallbackReceiver<absl::optional<std::vector<uint8_t>>>;
+    test::ValueCallbackReceiver<std::optional<std::vector<uint8_t>>>;
 using NiceMockBluetoothAdapter = ::testing::NiceMock<MockBluetoothAdapter>;
 
 // Sufficiently large test control point length as we are not interested
diff --git a/device/fido/cable/fido_tunnel_device.cc b/device/fido/cable/fido_tunnel_device.cc
index c153f16..9a63df3 100644
--- a/device/fido/cable/fido_tunnel_device.cc
+++ b/device/fido/cable/fido_tunnel_device.cc
@@ -97,13 +97,13 @@
 
 FidoTunnelDevice::FidoTunnelDevice(
     network::mojom::NetworkContext* network_context,
-    absl::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
+    std::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
         pairing_callback,
-    absl::optional<base::RepeatingCallback<void(Event)>> event_callback,
+    std::optional<base::RepeatingCallback<void(Event)>> event_callback,
     base::span<const uint8_t> secret,
     base::span<const uint8_t, kQRSeedSize> local_identity_seed,
     const CableEidArray& decrypted_eid)
-    : info_(absl::in_place_type<QRInfo>),
+    : info_(std::in_place_type<QRInfo>),
       id_(RandomId()),
       event_callback_(std::move(event_callback)) {
   const eid::Components components = eid::ToComponents(decrypted_eid);
@@ -140,7 +140,7 @@
       /*url_loader_network_observer=*/mojo::NullRemote(),
       /*auth_handler=*/mojo::NullRemote(),
       /*header_client=*/mojo::NullRemote(),
-      /*throttling_profile_id=*/absl::nullopt);
+      /*throttling_profile_id=*/std::nullopt);
 }
 
 FidoTunnelDevice::FidoTunnelDevice(
@@ -148,8 +148,8 @@
     network::mojom::NetworkContext* network_context,
     std::unique_ptr<Pairing> pairing,
     base::OnceClosure pairing_is_invalid,
-    absl::optional<base::RepeatingCallback<void(Event)>> event_callback)
-    : info_(absl::in_place_type<PairedInfo>),
+    std::optional<base::RepeatingCallback<void(Event)>> event_callback)
+    : info_(std::in_place_type<PairedInfo>),
       id_(RandomId()),
       event_callback_(std::move(event_callback)) {
   uint8_t client_nonce[kClientNonceSize];
@@ -159,7 +159,7 @@
   client_payload.emplace(1, pairing->id);
   client_payload.emplace(2, base::span<const uint8_t>(client_nonce));
   client_payload.emplace(3, RequestTypeToString(request_type));
-  const absl::optional<std::vector<uint8_t>> client_payload_bytes =
+  const std::optional<std::vector<uint8_t>> client_payload_bytes =
       cbor::Writer::Write(cbor::Value(std::move(client_payload)));
   CHECK(client_payload_bytes.has_value());
   const std::string client_payload_hex = base::HexEncode(*client_payload_bytes);
@@ -195,7 +195,7 @@
       /*url_loader_network_observer=*/mojo::NullRemote(),
       /*auth_handler=*/mojo::NullRemote(),
       /*header_client=*/mojo::NullRemote(),
-      /*throttling_profile_id=*/absl::nullopt);
+      /*throttling_profile_id=*/std::nullopt);
 }
 
 FidoTunnelDevice::~FidoTunnelDevice() {
@@ -209,7 +209,7 @@
     const std::array<uint8_t, kAdvertSize>& advert) {
   PairedInfo& info = absl::get<PairedInfo>(info_);
 
-  absl::optional<CableEidArray> plaintext =
+  std::optional<CableEidArray> plaintext =
       eid::Decrypt(advert, info.eid_encryption_key);
   if (!plaintext) {
     return false;
@@ -223,7 +223,7 @@
     // We were waiting for this BLE advert in order to start the handshake.
     DCHECK(!handshake_);
     handshake_.emplace(*info.psk, info.peer_identity,
-                       /*local_identity=*/absl::nullopt);
+                       /*local_identity=*/std::nullopt);
     websocket_client_->Write(handshake_->BuildInitialMessage());
     state_ = state_ == State::kWaitingForEID ? State::kHandshakeSent
                                              : State::kWaitingForConnectSignal;
@@ -239,7 +239,7 @@
 
   if (state_ == State::kError) {
     base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), absl::nullopt));
+        FROM_HERE, base::BindOnce(std::move(callback), std::nullopt));
   } else if (state_ != State::kReady) {
     DCHECK(!pending_callback_);
     pending_message_ = std::move(command);
@@ -274,7 +274,7 @@
 
 void FidoTunnelDevice::OnTunnelReady(
     WebSocketAdapter::Result result,
-    absl::optional<std::array<uint8_t, kRoutingIdSize>> routing_id,
+    std::optional<std::array<uint8_t, kRoutingIdSize>> routing_id,
     WebSocketAdapter::ConnectSignalSupport connect_signal_support) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(State::kConnecting, state_);
@@ -286,7 +286,7 @@
 
       if (auto* info = absl::get_if<QRInfo>(&info_)) {
         // A QR handshake can start as soon as the tunnel is connected.
-        handshake_.emplace(info->psk, /*peer_identity=*/absl::nullopt,
+        handshake_.emplace(info->psk, /*peer_identity=*/std::nullopt,
                            info->local_identity_seed);
       } else {
         // A paired handshake may be able to start if we have already seen
@@ -294,7 +294,7 @@
         PairedInfo& paired_info = absl::get<PairedInfo>(info_);
         if (paired_info.psk) {
           handshake_.emplace(*paired_info.psk, paired_info.peer_identity,
-                             /*local_identity=*/absl::nullopt);
+                             /*local_identity=*/std::nullopt);
         }
       }
 
@@ -335,7 +335,7 @@
 }
 
 void FidoTunnelDevice::OnTunnelData(
-    absl::optional<base::span<const uint8_t>> data) {
+    std::optional<base::span<const uint8_t>> data) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (!data) {
@@ -397,7 +397,7 @@
       }
 
       int protocol_revision = 1;
-      absl::optional<cbor::Value> payload = cbor::Reader::Read(decrypted);
+      std::optional<cbor::Value> payload = cbor::Reader::Read(decrypted);
       if (!payload) {
         // This message was padded in revision zero, which will cause a parse
         // error from `cbor::Reader::Read`.
@@ -440,7 +440,7 @@
           return;
         }
         if (auto* info = absl::get_if<QRInfo>(&info_)) {
-          absl::optional<std::unique_ptr<Pairing>> maybe_pairing =
+          std::optional<std::unique_ptr<Pairing>> maybe_pairing =
               Pairing::Parse(linking_it->second, info->tunnel_server_domain,
                              info->local_identity_seed, *handshake_hash_);
           if (!maybe_pairing) {
@@ -501,7 +501,7 @@
   } else {
     websocket_client_.reset();
     if (pending_callback_) {
-      std::move(pending_callback_).Run(absl::nullopt);
+      std::move(pending_callback_).Run(std::nullopt);
     }
   }
 }
@@ -587,7 +587,7 @@
 
   if (state_ == State::kRemoteShutdown || !crypter_->Encrypt(&message)) {
     base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), absl::nullopt));
+        FROM_HERE, base::BindOnce(std::move(callback), std::nullopt));
     return;
   }
 
@@ -631,7 +631,7 @@
 }
 
 void FidoTunnelDevice::EstablishedConnection::OnTunnelData(
-    absl::optional<base::span<const uint8_t>> data) {
+    std::optional<base::span<const uint8_t>> data) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(state_ == State::kRunning || state_ == State::kLocallyShutdown);
 
@@ -714,7 +714,7 @@
 
 bool FidoTunnelDevice::EstablishedConnection::ProcessUpdate(
     base::span<const uint8_t> plaintext) {
-  absl::optional<cbor::Value> payload = cbor::Reader::Read(plaintext);
+  std::optional<cbor::Value> payload = cbor::Reader::Read(plaintext);
   if (!payload || !payload->is_map()) {
     return false;
   }
@@ -731,7 +731,7 @@
       return true;
     }
 
-    absl::optional<std::unique_ptr<Pairing>> maybe_pairing =
+    std::optional<std::unique_ptr<Pairing>> maybe_pairing =
         Pairing::Parse(linking_it->second, *tunnel_server_domain_,
                        *local_identity_seed_, handshake_hash_);
     if (!maybe_pairing) {
@@ -752,7 +752,7 @@
     case State::kRunning:
       state_ = State::kRemoteShutdown;
       if (callback_) {
-        std::move(callback_).Run(absl::nullopt);
+        std::move(callback_).Run(std::nullopt);
       }
       break;
 
diff --git a/device/fido/cable/fido_tunnel_device.h b/device/fido/cable/fido_tunnel_device.h
index 1d0a7cb..d77da28 100644
--- a/device/fido/cable/fido_tunnel_device.h
+++ b/device/fido/cable/fido_tunnel_device.h
@@ -34,9 +34,9 @@
   // This constructor is used for QR-initiated connections.
   FidoTunnelDevice(
       network::mojom::NetworkContext* network_context,
-      absl::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
+      std::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
           pairing_callback,
-      absl::optional<base::RepeatingCallback<void(Event)>> event_callback,
+      std::optional<base::RepeatingCallback<void(Event)>> event_callback,
       base::span<const uint8_t> secret,
       base::span<const uint8_t, kQRSeedSize> local_identity_seed,
       const CableEidArray& decrypted_eid);
@@ -50,7 +50,7 @@
       network::mojom::NetworkContext* network_context,
       std::unique_ptr<Pairing> pairing,
       base::OnceClosure pairing_is_invalid,
-      absl::optional<base::RepeatingCallback<void(Event)>> event_callback);
+      std::optional<base::RepeatingCallback<void(Event)>> event_callback);
 
   FidoTunnelDevice(const FidoTunnelDevice&) = delete;
   FidoTunnelDevice& operator=(const FidoTunnelDevice&) = delete;
@@ -140,7 +140,7 @@
 
     CableEidArray decrypted_eid;
     std::array<uint8_t, 32> psk;
-    absl::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
+    std::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
         pairing_callback;
     std::array<uint8_t, kQRSeedSize> local_identity_seed;
     tunnelserver::KnownDomainID tunnel_server_domain;
@@ -155,9 +155,9 @@
     std::array<uint8_t, kEIDKeySize> eid_encryption_key;
     std::array<uint8_t, kP256X962Length> peer_identity;
     std::vector<uint8_t> secret;
-    absl::optional<CableEidArray> decrypted_eid;
-    absl::optional<std::array<uint8_t, 32>> psk;
-    absl::optional<std::vector<uint8_t>> handshake_message;
+    std::optional<CableEidArray> decrypted_eid;
+    std::optional<std::array<uint8_t, 32>> psk;
+    std::optional<std::vector<uint8_t>> handshake_message;
     base::OnceClosure pairing_is_invalid;
   };
 
@@ -188,7 +188,7 @@
     friend class base::RefCounted<EstablishedConnection>;
     ~EstablishedConnection();
 
-    void OnTunnelData(absl::optional<base::span<const uint8_t>> data);
+    void OnTunnelData(std::optional<base::span<const uint8_t>> data);
     void OnRemoteClose();
     void OnTimeout();
     bool ProcessUpdate(base::span<const uint8_t> plaintext);
@@ -202,10 +202,10 @@
     const HandshakeHash handshake_hash_;
 
     // These three fields are either all present or all nullopt.
-    absl::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
+    std::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
         pairing_callback_;
-    absl::optional<std::array<uint8_t, kQRSeedSize>> local_identity_seed_;
-    absl::optional<tunnelserver::KnownDomainID> tunnel_server_domain_;
+    std::optional<std::array<uint8_t, kQRSeedSize>> local_identity_seed_;
+    std::optional<tunnelserver::KnownDomainID> tunnel_server_domain_;
 
     base::OneShotTimer timer_;
     DeviceCallback callback_;
@@ -214,9 +214,9 @@
 
   void OnTunnelReady(
       WebSocketAdapter::Result result,
-      absl::optional<std::array<uint8_t, kRoutingIdSize>> routing_id,
+      std::optional<std::array<uint8_t, kRoutingIdSize>> routing_id,
       WebSocketAdapter::ConnectSignalSupport connect_signal_support);
-  void OnTunnelData(absl::optional<base::span<const uint8_t>> data);
+  void OnTunnelData(std::optional<base::span<const uint8_t>> data);
   void OnError();
   void DeviceTransactReady(std::vector<uint8_t> command,
                            DeviceCallback callback);
@@ -225,11 +225,11 @@
   State state_ = State::kConnecting;
   absl::variant<QRInfo, PairedInfo> info_;
   const std::array<uint8_t, 8> id_;
-  const absl::optional<base::RepeatingCallback<void(Event)>> event_callback_;
+  const std::optional<base::RepeatingCallback<void(Event)>> event_callback_;
   std::vector<uint8_t> pending_message_;
   DeviceCallback pending_callback_;
-  absl::optional<HandshakeInitiator> handshake_;
-  absl::optional<HandshakeHash> handshake_hash_;
+  std::optional<HandshakeInitiator> handshake_;
+  std::optional<HandshakeHash> handshake_hash_;
   std::vector<uint8_t> getinfo_response_bytes_;
 
   // These fields are |nullptr| when in state |kReady|.
diff --git a/device/fido/cable/noise.cc b/device/fido/cable/noise.cc
index bb7d7e59..18f4b764 100644
--- a/device/fido/cable/noise.cc
+++ b/device/fido/cable/noise.cc
@@ -119,7 +119,7 @@
   return ciphertext;
 }
 
-absl::optional<std::vector<uint8_t>> Noise::DecryptAndHash(
+std::optional<std::vector<uint8_t>> Noise::DecryptAndHash(
     base::span<const uint8_t> ciphertext) {
   uint8_t nonce[12] = {0};
   const uint32_t counter = base::ByteSwap(symmetric_nonce_);
diff --git a/device/fido/cable/noise.h b/device/fido/cable/noise.h
index ab595af..4b34038 100644
--- a/device/fido/cable/noise.h
+++ b/device/fido/cable/noise.h
@@ -8,11 +8,11 @@
 #include <stdint.h>
 
 #include <array>
+#include <optional>
 #include <tuple>
 
 #include "base/component_export.h"
 #include "base/containers/span.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/boringssl/src/include/openssl/base.h"
 
 namespace device {
@@ -41,7 +41,7 @@
   void MixKey(base::span<const uint8_t> ikm);
   void MixKeyAndHash(base::span<const uint8_t> ikm);
   std::vector<uint8_t> EncryptAndHash(base::span<const uint8_t> plaintext);
-  absl::optional<std::vector<uint8_t>> DecryptAndHash(
+  std::optional<std::vector<uint8_t>> DecryptAndHash(
       base::span<const uint8_t> ciphertext);
   std::array<uint8_t, 32> handshake_hash() const;
 
diff --git a/device/fido/cable/v2_authenticator.cc b/device/fido/cable/v2_authenticator.cc
index 3bc226a..c147882 100644
--- a/device/fido/cable/v2_authenticator.cc
+++ b/device/fido/cable/v2_authenticator.cc
@@ -256,7 +256,7 @@
 }
 
 using GeneratePairingDataCallback =
-    base::OnceCallback<absl::optional<cbor::Value>(
+    base::OnceCallback<std::optional<cbor::Value>(
         base::span<const uint8_t, device::kP256X962Length> peer_public_key_x962,
         device::cablev2::HandshakeHash)>;
 
@@ -378,13 +378,13 @@
         /*url_loader_network_observer=*/mojo::NullRemote(),
         /*auth_handler=*/mojo::NullRemote(),
         /*header_client=*/mojo::NullRemote(),
-        /*throttling_profile_id=*/absl::nullopt);
+        /*throttling_profile_id=*/std::nullopt);
     FIDO_LOG(DEBUG) << "Creating WebSocket to " << target_.spec();
   }
 
   void OnTunnelReady(
       WebSocketAdapter::Result result,
-      absl::optional<std::array<uint8_t, device::cablev2::kRoutingIdSize>>
+      std::optional<std::array<uint8_t, device::cablev2::kRoutingIdSize>>
           routing_id,
       WebSocketAdapter::ConnectSignalSupport) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -430,7 +430,7 @@
     update_callback_.Run(Platform::Status::TUNNEL_SERVER_CONNECT);
   }
 
-  void OnTunnelData(absl::optional<base::span<const uint8_t>> msg) {
+  void OnTunnelData(std::optional<base::span<const uint8_t>> msg) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
     if (!msg) {
@@ -458,7 +458,7 @@
         cbor::Value::MapValue post_handshake_msg;
         post_handshake_msg.emplace(1, BuildGetInfoResponse());
 
-        absl::optional<std::vector<uint8_t>> post_handshake_msg_bytes;
+        std::optional<std::vector<uint8_t>> post_handshake_msg_bytes;
         post_handshake_msg_bytes =
             cbor::Writer::Write(cbor::Value(std::move(post_handshake_msg)));
         if (!post_handshake_msg_bytes) {
@@ -475,7 +475,7 @@
         if (state_ == State::kConnected) {
           // Linking information can be sent at any time. We always send it
           // immediately after the post-handshake message.
-          absl::optional<cbor::Value> pairing_data(
+          std::optional<cbor::Value> pairing_data(
               std::move(generate_pairing_data_)
                   .Run(*peer_identity_, result->second));
 
@@ -496,7 +496,7 @@
           if (pairing_data) {
             cbor::Value::MapValue update_msg_for_measurement;
             update_msg_for_measurement.emplace(1, pairing_data->Clone());
-            absl::optional<std::vector<uint8_t>> cbor_bytes =
+            std::optional<std::vector<uint8_t>> cbor_bytes =
                 cbor::Writer::Write(
                     cbor::Value(std::move(update_msg_for_measurement)));
 
@@ -513,7 +513,7 @@
             update_msg.emplace(1, std::move(*pairing_data));
           }
 
-          absl::optional<std::vector<uint8_t>> update_msg_bytes =
+          std::optional<std::vector<uint8_t>> update_msg_bytes =
               cbor::Writer::Write(cbor::Value(std::move(update_msg)));
           if (!update_msg_bytes) {
             FIDO_LOG(ERROR) << "failed to encode update message";
@@ -601,7 +601,7 @@
   std::unique_ptr<WebSocketAdapter> websocket_client_;
   std::unique_ptr<Crypter> crypter_;
   const raw_ptr<network::mojom::NetworkContext> network_context_;
-  const absl::optional<std::array<uint8_t, kP256X962Length>> peer_identity_;
+  const std::optional<std::array<uint8_t, kP256X962Length>> peer_identity_;
   std::array<uint8_t, kPSKSize> psk_;
   GeneratePairingDataCallback generate_pairing_data_;
   const std::vector<uint8_t> secret_;
@@ -643,7 +643,7 @@
       platform_->OnStatus(*status);
       return;
     } else if (absl::get_if<Transport::Disconnected>(&update)) {
-      absl::optional<Platform::Error> maybe_error;
+      std::optional<Platform::Error> maybe_error;
       if (!transaction_received_) {
         maybe_error = Platform::Error::UNEXPECTED_EOF;
       } else if (!transaction_done_) {
@@ -681,7 +681,7 @@
     const auto command = message_bytes[0];
     const auto cbor_bytes = message_bytes.subspan(1);
 
-    absl::optional<cbor::Value> payload;
+    std::optional<cbor::Value> payload;
     if (!cbor_bytes.empty()) {
       payload = cbor::Reader::Read(cbor_bytes);
       if (!payload) {
@@ -704,7 +704,7 @@
           return Platform::Error::INVALID_CTAP;
         }
 
-        absl::optional<std::vector<uint8_t>> response = BuildGetInfoResponse();
+        std::optional<std::vector<uint8_t>> response = BuildGetInfoResponse();
         if (!response) {
           return Platform::Error::INTERNAL_ERROR;
         }
@@ -907,7 +907,7 @@
     std::vector<uint8_t> response = {base::checked_cast<uint8_t>(ctap_status)};
     if (ctap_status == static_cast<uint8_t>(CtapDeviceResponseCode::kSuccess)) {
       // TODO: pass response parameters from the Java side.
-      absl::optional<cbor::Value> cbor_attestation_object =
+      std::optional<cbor::Value> cbor_attestation_object =
           cbor::Reader::Read(attestation_object_bytes);
       if (!cbor_attestation_object || !cbor_attestation_object->is_map()) {
         FIDO_LOG(ERROR) << "invalid CBOR attestation object";
@@ -938,7 +938,7 @@
         response_map.emplace(6, std::move(unsigned_extension_outputs));
       }
 
-      absl::optional<std::vector<uint8_t>> response_payload =
+      std::optional<std::vector<uint8_t>> response_payload =
           cbor::Writer::Write(cbor::Value(std::move(response_map)));
       if (!response_payload) {
         return;
@@ -1029,7 +1029,7 @@
         response_map.emplace(8, std::move(unsigned_extension_outputs));
       }
 
-      absl::optional<std::vector<uint8_t>> response_payload =
+      std::optional<std::vector<uint8_t>> response_payload =
           cbor::Writer::Write(cbor::Value(std::move(response_map)));
       if (!response_payload) {
         return;
@@ -1090,7 +1090,7 @@
 
 static std::array<uint8_t, 32> DerivePairedSecret(
     base::span<const uint8_t, kRootSecretSize> root_secret,
-    const absl::optional<base::span<const uint8_t>>& contact_id,
+    const std::optional<base::span<const uint8_t>>& contact_id,
     base::span<const uint8_t, kPairingIDSize> pairing_id) {
   base::span<const uint8_t, kRootSecretSize> secret = root_secret;
 
@@ -1120,7 +1120,7 @@
   static GeneratePairingDataCallback GetClosure(
       base::span<const uint8_t, kRootSecretSize> root_secret,
       const std::string& name,
-      absl::optional<std::vector<uint8_t>> contact_id) {
+      std::optional<std::vector<uint8_t>> contact_id) {
     auto* generator =
         new PairingDataGenerator(root_secret, name, std::move(contact_id));
     return base::BindOnce(&PairingDataGenerator::Generate,
@@ -1130,16 +1130,16 @@
  private:
   PairingDataGenerator(base::span<const uint8_t, kRootSecretSize> root_secret,
                        const std::string& name,
-                       absl::optional<std::vector<uint8_t>> contact_id)
+                       std::optional<std::vector<uint8_t>> contact_id)
       : root_secret_(fido_parsing_utils::Materialize(root_secret)),
         name_(name),
         contact_id_(std::move(contact_id)) {}
 
-  absl::optional<cbor::Value> Generate(
+  std::optional<cbor::Value> Generate(
       base::span<const uint8_t, device::kP256X962Length> peer_public_key_x962,
       device::cablev2::HandshakeHash handshake_hash) {
     if (!contact_id_) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     std::array<uint8_t, kPairingIDSize> pairing_id;
@@ -1173,7 +1173,7 @@
 
   const std::array<uint8_t, kRootSecretSize> root_secret_;
   const std::string name_;
-  absl::optional<std::vector<uint8_t>> contact_id_;
+  std::optional<std::vector<uint8_t>> contact_id_;
 };
 
 }  // namespace
@@ -1197,7 +1197,7 @@
     const std::string& authenticator_name,
     base::span<const uint8_t, 16> qr_secret,
     base::span<const uint8_t, kP256X962Length> peer_identity,
-    absl::optional<std::vector<uint8_t>> contact_id) {
+    std::optional<std::vector<uint8_t>> contact_id) {
   auto generate_pairing_data = PairingDataGenerator::GetClosure(
       root_secret, authenticator_name, std::move(contact_id));
 
@@ -1217,7 +1217,7 @@
     base::span<const uint8_t, kTunnelIdSize> tunnel_id,
     base::span<const uint8_t, kPairingIDSize> pairing_id,
     base::span<const uint8_t, kClientNonceSize> client_nonce,
-    absl::optional<base::span<const uint8_t>> contact_id) {
+    std::optional<base::span<const uint8_t>> contact_id) {
   const std::array<uint8_t, 32> paired_secret =
       DerivePairedSecret(root_secret, contact_id, pairing_id);
 
diff --git a/device/fido/cable/v2_authenticator.h b/device/fido/cable/v2_authenticator.h
index 6a2e8fa0..2df97cc 100644
--- a/device/fido/cable/v2_authenticator.h
+++ b/device/fido/cable/v2_authenticator.h
@@ -5,17 +5,17 @@
 #ifndef DEVICE_FIDO_CABLE_V2_AUTHENTICATOR_H_
 #define DEVICE_FIDO_CABLE_V2_AUTHENTICATOR_H_
 
+#include <stdint.h>
+
+#include <optional>
 #include <string>
 #include <vector>
 
-#include <stdint.h>
-
 #include "base/containers/span.h"
 #include "base/functional/callback.h"
 #include "device/fido/cable/v2_constants.h"
 #include "device/fido/fido_constants.h"
 #include "services/network/public/mojom/network_context.mojom-forward.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 #include "third_party/blink/public/mojom/webauthn/authenticator.mojom-forward.h"
 
@@ -92,7 +92,7 @@
   // OnCompleted is called when the transaction has completed. Note that calling
   // this may result in the |Transaction| that owns this |Platform| being
   // deleted.
-  virtual void OnCompleted(absl::optional<Error>) = 0;
+  virtual void OnCompleted(std::optional<Error>) = 0;
 
   virtual std::unique_ptr<BLEAdvert> SendBLEAdvert(
       base::span<const uint8_t, kAdvertSize> payload) = 0;
@@ -144,7 +144,7 @@
     // TODO: name this constant.
     base::span<const uint8_t, 16> qr_secret,
     base::span<const uint8_t, kP256X962Length> peer_identity,
-    absl::optional<std::vector<uint8_t>> contact_id);
+    std::optional<std::vector<uint8_t>> contact_id);
 
 // TransactFromFCM starts a network-based transaction based on the decoded
 // contents of a cloud message.
@@ -156,7 +156,7 @@
     base::span<const uint8_t, kTunnelIdSize> tunnel_id,
     base::span<const uint8_t, kPairingIDSize> pairing_id,
     base::span<const uint8_t, kClientNonceSize> client_nonce,
-    absl::optional<base::span<const uint8_t>> contact_id);
+    std::optional<base::span<const uint8_t>> contact_id);
 
 }  // namespace authenticator
 }  // namespace cablev2
diff --git a/device/fido/cable/v2_discovery.cc b/device/fido/cable/v2_discovery.cc
index cbe25d3..34a5f11 100644
--- a/device/fido/cable/v2_discovery.cc
+++ b/device/fido/cable/v2_discovery.cc
@@ -50,16 +50,16 @@
 Discovery::Discovery(
     FidoRequestType request_type,
     network::mojom::NetworkContext* network_context,
-    absl::optional<base::span<const uint8_t, kQRKeySize>> qr_generator_key,
+    std::optional<base::span<const uint8_t, kQRKeySize>> qr_generator_key,
     std::unique_ptr<AdvertEventStream> advert_stream,
     std::unique_ptr<EventStream<std::unique_ptr<Pairing>>>
         contact_device_stream,
     const std::vector<CableDiscoveryData>& extension_contents,
-    absl::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
+    std::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
         pairing_callback,
-    absl::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
+    std::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
         invalidated_pairing_callback,
-    absl::optional<base::RepeatingCallback<void(Event)>> event_callback)
+    std::optional<base::RepeatingCallback<void(Event)>> event_callback)
     : FidoDeviceDiscovery(FidoTransportProtocol::kHybrid),
       request_type_(request_type),
       network_context_(network_context),
@@ -150,7 +150,7 @@
 
   if (qr_keys_) {
     // Check whether the EID matches a QR code.
-    absl::optional<CableEidArray> plaintext =
+    std::optional<CableEidArray> plaintext =
         eid::Decrypt(advert_array, qr_keys_->eid_key);
     if (plaintext) {
       FIDO_LOG(DEBUG) << "  (" << base::HexEncode(advert)
@@ -169,7 +169,7 @@
 
   // Check whether the EID matches the extension.
   for (const auto& extension : extension_keys_) {
-    absl::optional<CableEidArray> plaintext =
+    std::optional<CableEidArray> plaintext =
         eid::Decrypt(advert_array, extension.eid_key);
     if (plaintext) {
       FIDO_LOG(DEBUG) << "  (" << base::HexEncode(advert)
@@ -205,11 +205,11 @@
 }
 
 // static
-absl::optional<Discovery::UnpairedKeys> Discovery::KeysFromQRGeneratorKey(
-    const absl::optional<base::span<const uint8_t, kQRKeySize>>
+std::optional<Discovery::UnpairedKeys> Discovery::KeysFromQRGeneratorKey(
+    const std::optional<base::span<const uint8_t, kQRKeySize>>
         qr_generator_key) {
   if (!qr_generator_key) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   UnpairedKeys ret;
@@ -239,7 +239,7 @@
       continue;
     }
 
-    absl::optional<Discovery::UnpairedKeys> keys = KeysFromQRGeneratorKey(
+    std::optional<Discovery::UnpairedKeys> keys = KeysFromQRGeneratorKey(
         base::make_span<kQRKeySize>(data.v2->server_link_data));
     if (keys.has_value()) {
       ret.emplace_back(std::move(keys.value()));
diff --git a/device/fido/cable/v2_discovery.h b/device/fido/cable/v2_discovery.h
index c5aa217..67a4c3e 100644
--- a/device/fido/cable/v2_discovery.h
+++ b/device/fido/cable/v2_discovery.h
@@ -7,6 +7,7 @@
 
 #include <array>
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "base/component_export.h"
@@ -20,7 +21,6 @@
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_device_discovery.h"
 #include "services/network/public/mojom/network_context.mojom-forward.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device::cablev2 {
 
@@ -37,7 +37,7 @@
   Discovery(
       FidoRequestType request_type,
       network::mojom::NetworkContext* network_context,
-      absl::optional<base::span<const uint8_t, kQRKeySize>> qr_generator_key,
+      std::optional<base::span<const uint8_t, kQRKeySize>> qr_generator_key,
       std::unique_ptr<AdvertEventStream> advert_stream,
       // contact_device_stream contains a series of pairings indicating that the
       // given device should be contacted. The pairings may be duplicated. It
@@ -47,14 +47,14 @@
       const std::vector<CableDiscoveryData>& extension_contents,
       // pairing_callback will be called when a QR-initiated connection
       // receives pairing information from the peer.
-      absl::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
+      std::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
           pairing_callback,
       // invalidated_pairing_callback will be called when a pairing is reported
       // to be invalid by the tunnel server.
-      absl::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
+      std::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
           invalidated_pairing_callback,
       // event_callback receives updates on cablev2 events.
-      absl::optional<base::RepeatingCallback<void(Event)>> event_callback);
+      std::optional<base::RepeatingCallback<void(Event)>> event_callback);
   ~Discovery() override;
   Discovery(const Discovery&) = delete;
   Discovery& operator=(const Discovery&) = delete;
@@ -74,22 +74,22 @@
   void OnBLEAdvertSeen(base::span<const uint8_t, kAdvertSize> advert);
   void OnContactDevice(std::unique_ptr<Pairing> pairing);
   void PairingIsInvalid(std::unique_ptr<Pairing> pairing);
-  static absl::optional<UnpairedKeys> KeysFromQRGeneratorKey(
-      absl::optional<base::span<const uint8_t, kQRKeySize>> qr_generator_key);
+  static std::optional<UnpairedKeys> KeysFromQRGeneratorKey(
+      std::optional<base::span<const uint8_t, kQRKeySize>> qr_generator_key);
   static std::vector<UnpairedKeys> KeysFromExtension(
       const std::vector<CableDiscoveryData>& extension_contents);
 
   const FidoRequestType request_type_;
   const raw_ptr<network::mojom::NetworkContext> network_context_;
-  const absl::optional<UnpairedKeys> qr_keys_;
+  const std::optional<UnpairedKeys> qr_keys_;
   const std::vector<UnpairedKeys> extension_keys_;
   std::unique_ptr<AdvertEventStream> advert_stream_;
   std::unique_ptr<EventStream<std::unique_ptr<Pairing>>> contact_device_stream_;
-  const absl::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
+  const std::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
       pairing_callback_;
-  const absl::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
+  const std::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
       invalidated_pairing_callback_;
-  const absl::optional<base::RepeatingCallback<void(Event)>> event_callback_;
+  const std::optional<base::RepeatingCallback<void(Event)>> event_callback_;
   std::vector<std::unique_ptr<FidoTunnelDevice>> tunnels_pending_advert_;
   base::flat_set<std::array<uint8_t, kAdvertSize>> observed_adverts_;
   bool started_ = false;
diff --git a/device/fido/cable/v2_handshake.cc b/device/fido/cable/v2_handshake.cc
index 3c660c2..2ba3307 100644
--- a/device/fido/cable/v2_handshake.cc
+++ b/device/fido/cable/v2_handshake.cc
@@ -100,11 +100,11 @@
 // to values 0..256.
 static const char* kAssignedDomains[] = {"cable.ua5v.com", "cable.auth.com"};
 
-absl::optional<KnownDomainID> ToKnownDomainID(uint16_t domain) {
+std::optional<KnownDomainID> ToKnownDomainID(uint16_t domain) {
   if (domain >= 256 || domain < std::size(kAssignedDomains)) {
     return KnownDomainID(domain);
   }
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 std::string DecodeDomain(KnownDomainID domain_id) {
@@ -215,7 +215,7 @@
   return ret;
 }
 
-absl::optional<CableEidArray> Decrypt(
+std::optional<CableEidArray> Decrypt(
     const std::array<uint8_t, kAdvertSize>& advert,
     base::span<const uint8_t, kEIDKeySize> key) {
   // See |Encrypt| about the format.
@@ -229,7 +229,7 @@
   CHECK_EQ(calculated_hmac_len, sizeof(calculated_hmac));
 
   if (CRYPTO_memcmp(calculated_hmac, advert.data() + AES_BLOCK_SIZE, 4) != 0) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   AES_KEY aes_key;
@@ -243,7 +243,7 @@
   // code, thus authenticators should not be unilaterally setting any of these
   // bits.
   if (!ReservedBitsAreZero(plaintext)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   uint16_t tunnel_server_domain;
@@ -252,7 +252,7 @@
          &plaintext[EXTENT(plaintext) - sizeof(tunnel_server_domain)],
          sizeof(tunnel_server_domain));
   if (!tunnelserver::ToKnownDomainID(tunnel_server_domain)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return plaintext;
@@ -305,11 +305,11 @@
 
 // DecompressPublicKey converts a compressed public key (from a scanned QR
 // code) into a standard, uncompressed one.
-static absl::optional<std::array<uint8_t, device::kP256X962Length>>
+static std::optional<std::array<uint8_t, device::kP256X962Length>>
 DecompressPublicKey(base::span<const uint8_t> compressed_public_key) {
   if (compressed_public_key.size() !=
       device::cablev2::kCompressedPublicKeySize) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   bssl::UniquePtr<EC_GROUP> p256(
@@ -317,7 +317,7 @@
   bssl::UniquePtr<EC_POINT> point(EC_POINT_new(p256.get()));
   if (!EC_POINT_oct2point(p256.get(), point.get(), compressed_public_key.data(),
                           compressed_public_key.size(), /*ctx=*/nullptr)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   std::array<uint8_t, device::kP256X962Length> ret;
   CHECK_EQ(
@@ -341,22 +341,22 @@
 }
 
 // static
-absl::optional<Components> Parse(const std::string& qr_url) {
+std::optional<Components> Parse(const std::string& qr_url) {
   if (qr_url.size() < sizeof(kPrefix) - 1 ||
       base::CompareCaseInsensitiveASCII(
           kPrefix, qr_url.substr(0, sizeof(kPrefix) - 1)) != 0) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
-  absl::optional<std::vector<uint8_t>> qr_bytes =
+  std::optional<std::vector<uint8_t>> qr_bytes =
       DigitsToBytes(qr_url.substr(sizeof(kPrefix) - 1));
   if (!qr_bytes) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
-  absl::optional<cbor::Value> qr_contents = cbor::Reader::Read(*qr_bytes);
+  std::optional<cbor::Value> qr_contents = cbor::Reader::Read(*qr_bytes);
   if (!qr_contents || !qr_contents->is_map()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   const cbor::Value::MapValue& qr_contents_map(qr_contents->GetMap());
 
@@ -365,7 +365,7 @@
     const cbor::Value::MapValue::const_iterator it =
         qr_contents_map.find(cbor::Value(static_cast<int>(i)));
     if (it == qr_contents_map.end() || !it->second.is_bytestring()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     values[i] = it->second.GetBytestring();
   }
@@ -375,22 +375,22 @@
 
   Components ret;
   if (qr_secret.size() != ret.secret.size()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   base::ranges::copy(qr_secret, ret.secret.begin());
 
-  absl::optional<std::array<uint8_t, device::kP256X962Length>> peer_identity =
+  std::optional<std::array<uint8_t, device::kP256X962Length>> peer_identity =
       DecompressPublicKey(compressed_public_key);
   if (!peer_identity) {
     FIDO_LOG(ERROR) << "Invalid compressed public key in QR data";
-    return absl::nullopt;
+    return std::nullopt;
   }
   ret.peer_identity = *peer_identity;
 
   auto it = qr_contents_map.find(cbor::Value(2));
   if (it != qr_contents_map.end()) {
     if (!it->second.is_integer()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     ret.num_known_domains = it->second.GetInteger();
   }
@@ -398,7 +398,7 @@
   it = qr_contents_map.find(cbor::Value(4));
   if (it != qr_contents_map.end()) {
     if (!it->second.is_bool()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     ret.supports_linking = it->second.GetBool();
   }
@@ -406,7 +406,7 @@
   it = qr_contents_map.find(cbor::Value(5));
   if (it != qr_contents_map.end()) {
     if (!it->second.is_string()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     ret.request_type = RequestTypeFromString(it->second.GetString());
   }
@@ -433,7 +433,7 @@
 
   qr_contents.emplace(5, RequestTypeToString(request_type));
 
-  const absl::optional<std::vector<uint8_t>> qr_data =
+  const std::optional<std::vector<uint8_t>> qr_data =
       cbor::Writer::Write(cbor::Value(std::move(qr_contents)));
   return std::string(kPrefix) + BytesToDigits(*qr_data);
 }
@@ -484,7 +484,7 @@
   return ret;
 }
 
-absl::optional<std::vector<uint8_t>> DigitsToBytes(std::string_view in) {
+std::optional<std::vector<uint8_t>> DigitsToBytes(std::string_view in) {
   std::vector<uint8_t> ret;
   ret.reserve(((in.size() + kChunkDigits - 1) / kChunkDigits) * kChunkSize);
 
@@ -492,7 +492,7 @@
     uint64_t v;
     if (!base::StringToUint64(in.substr(0, kChunkDigits), &v) ||
         v >> (kChunkSize * 8) != 0) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     const uint8_t* const v_bytes = reinterpret_cast<uint8_t*>(&v);
     ret.insert(ret.end(), v_bytes, v_bytes + kChunkSize);
@@ -522,12 +522,12 @@
         remaining_bytes = 6;
         break;
       default:
-        return absl::nullopt;
+        return std::nullopt;
     }
 
     uint64_t v;
     if (!base::StringToUint64(in, &v) || v >> (remaining_bytes * 8) != 0) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     const uint8_t* const v_bytes = reinterpret_cast<uint8_t*>(&v);
@@ -612,7 +612,7 @@
       EC_KEY_derive_from_secret(p256.get(), seed.data(), seed.size()));
 }
 
-absl::optional<std::vector<uint8_t>> EncodePaddedCBORMap(
+std::optional<std::vector<uint8_t>> EncodePaddedCBORMap(
     cbor::Value::MapValue map) {
   // The number of padding bytes is a uint16_t, so the granularity cannot be
   // larger than that.
@@ -623,10 +623,10 @@
   static_assert((kPostHandshakeMsgPaddingGranularity &
                  (kPostHandshakeMsgPaddingGranularity - 1)) == 0);
 
-  absl::optional<std::vector<uint8_t>> cbor_bytes =
+  std::optional<std::vector<uint8_t>> cbor_bytes =
       cbor::Writer::Write(cbor::Value(std::move(map)));
   if (!cbor_bytes) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   base::CheckedNumeric<size_t> padded_size_checked = cbor_bytes->size();
@@ -635,7 +635,7 @@
       (padded_size_checked + kPostHandshakeMsgPaddingGranularity - 1) &
       ~(kPostHandshakeMsgPaddingGranularity - 1);
   if (!padded_size_checked.IsValid()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   const size_t padded_size = padded_size_checked.ValueOrDie();
@@ -661,21 +661,21 @@
 //
 // TODO(agl): remove support for this padding format. (Chromium started sending
 // the new format with M99.)
-absl::optional<cbor::Value> DecodePaddedCBORMap8(
+std::optional<cbor::Value> DecodePaddedCBORMap8(
     const base::span<const uint8_t> input) {
   if (input.empty()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   const size_t padding_length = input.back();
   if (padding_length + 1 > input.size()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   auto unpadded_input = input.first(input.size() - padding_length - 1);
 
-  absl::optional<cbor::Value> payload = cbor::Reader::Read(unpadded_input);
+  std::optional<cbor::Value> payload = cbor::Reader::Read(unpadded_input);
   if (!payload || !payload->is_map()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return payload;
@@ -683,10 +683,10 @@
 
 // DecodePaddedCBORMap16 performs the actions of |DecodePaddedCBORMap| using the
 // new padding format. See comment in |DecodePaddedCBORMap|.
-absl::optional<cbor::Value> DecodePaddedCBORMap16(
+std::optional<cbor::Value> DecodePaddedCBORMap16(
     base::span<const uint8_t> input) {
   if (input.size() < sizeof(uint16_t)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   uint16_t padding_length16;
@@ -694,13 +694,13 @@
          sizeof(padding_length16));
   const size_t padding_length = padding_length16;
   if (padding_length + sizeof(uint16_t) > input.size()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   input = input.first(input.size() - padding_length - sizeof(uint16_t));
 
-  absl::optional<cbor::Value> payload = cbor::Reader::Read(input);
+  std::optional<cbor::Value> payload = cbor::Reader::Read(input);
   if (!payload || !payload->is_map()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return payload;
@@ -708,12 +708,12 @@
 
 }  // namespace
 
-absl::optional<cbor::Value> DecodePaddedCBORMap(
+std::optional<cbor::Value> DecodePaddedCBORMap(
     base::span<const uint8_t> input) {
   // Two padding formats are currently in use. They are unambiguous so we try
   // each, new first. Eventually the old format can be removed once enough time
   // has passed since M99.
-  absl::optional<cbor::Value> result = DecodePaddedCBORMap16(input);
+  std::optional<cbor::Value> result = DecodePaddedCBORMap16(input);
   if (!result) {
     result = DecodePaddedCBORMap8(input);
   }
@@ -789,7 +789,7 @@
   DCHECK_EQ(nonce.size(), aes_key.NonceLength());
 
   base::span<const uint8_t> additional_data;
-  absl::optional<std::vector<uint8_t>> plaintext =
+  std::optional<std::vector<uint8_t>> plaintext =
       aes_key.Open(ciphertext, nonce, additional_data);
 
   if (!plaintext) {
@@ -818,9 +818,9 @@
 }
 
 HandshakeInitiator::HandshakeInitiator(
-    absl::optional<base::span<const uint8_t, 32>> psk,
-    absl::optional<base::span<const uint8_t, kP256X962Length>> peer_identity,
-    absl::optional<base::span<const uint8_t, kQRSeedSize>> identity_seed)
+    std::optional<base::span<const uint8_t, 32>> psk,
+    std::optional<base::span<const uint8_t, kP256X962Length>> peer_identity,
+    std::optional<base::span<const uint8_t, kQRSeedSize>> identity_seed)
     : local_identity_(identity_seed ? ECKeyFromSeed(*identity_seed) : nullptr) {
   DCHECK(peer_identity.has_value() ^ static_cast<bool>(local_identity_));
   if (peer_identity) {
@@ -903,7 +903,7 @@
   if (response.size() < kP256X962Length) {
     FIDO_LOG(DEBUG) << "Handshake response truncated (" << response.size()
                     << " bytes)";
-    return absl::nullopt;
+    return std::nullopt;
   }
   auto peer_point_bytes = response.first(kP256X962Length);
   auto ciphertext = response.subspan(kP256X962Length);
@@ -918,7 +918,7 @@
                        ephemeral_key_.get(),
                        /*kdf=*/nullptr) != sizeof(shared_key_ee)) {
     FIDO_LOG(DEBUG) << "Peer's P-256 point not on curve.";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   noise_.MixHash(peer_point_bytes);
@@ -931,7 +931,7 @@
                          local_identity_.get(),
                          /*kdf=*/nullptr) != sizeof(shared_key_se)) {
       FIDO_LOG(DEBUG) << "ECDH_compute_key failed";
-      return absl::nullopt;
+      return std::nullopt;
     }
     noise_.MixKey(shared_key_se);
   }
@@ -939,7 +939,7 @@
   auto plaintext = noise_.DecryptAndHash(ciphertext);
   if (!plaintext || !plaintext->empty()) {
     FIDO_LOG(DEBUG) << "Invalid caBLE handshake message";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   auto [write_key, read_key] = noise_.traffic_keys();
@@ -948,16 +948,16 @@
 }
 
 HandshakeResult RespondToHandshake(
-    absl::optional<base::span<const uint8_t, 32>> psk,
+    std::optional<base::span<const uint8_t, 32>> psk,
     bssl::UniquePtr<EC_KEY> identity,
-    absl::optional<base::span<const uint8_t, kP256X962Length>> peer_identity,
+    std::optional<base::span<const uint8_t, kP256X962Length>> peer_identity,
     base::span<const uint8_t> in,
     std::vector<uint8_t>* out_response) {
   DCHECK(peer_identity.has_value() ^ static_cast<bool>(identity));
 
   if (in.size() < kP256X962Length) {
     FIDO_LOG(DEBUG) << "Handshake truncated (" << in.size() << " bytes)";
-    return absl::nullopt;
+    return std::nullopt;
   }
   auto peer_point_bytes = in.first(kP256X962Length);
   auto ciphertext = in.subspan(kP256X962Length);
@@ -995,7 +995,7 @@
                           peer_point_bytes.size(),
                           /*ctx=*/nullptr)) {
     FIDO_LOG(DEBUG) << "Peer's P-256 point not on curve.";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   if (identity) {
@@ -1003,7 +1003,7 @@
     if (ECDH_compute_key(es_key, sizeof(es_key), peer_point.get(),
                          identity.get(),
                          /*kdf=*/nullptr) != sizeof(es_key)) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     noise.MixKey(es_key);
   }
@@ -1011,7 +1011,7 @@
   auto plaintext = noise.DecryptAndHash(ciphertext);
   if (!plaintext || !plaintext->empty()) {
     FIDO_LOG(DEBUG) << "Failed to decrypt handshake ciphertext.";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   uint8_t ephemeral_key_public_bytes[kP256X962Length];
@@ -1028,7 +1028,7 @@
   if (ECDH_compute_key(shared_key_ee, sizeof(shared_key_ee), peer_point.get(),
                        ephemeral_key.get(),
                        /*kdf=*/nullptr) != sizeof(shared_key_ee)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   noise.MixKey(shared_key_ee);
 
@@ -1041,7 +1041,7 @@
     if (ECDH_compute_key(shared_key_se, sizeof(shared_key_se),
                          peer_identity_point.get(), ephemeral_key.get(),
                          /*kdf=*/nullptr) != sizeof(shared_key_se)) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     noise.MixKey(shared_key_se);
   }
diff --git a/device/fido/cable/v2_handshake.h b/device/fido/cable/v2_handshake.h
index f44d226..2122e8d 100644
--- a/device/fido/cable/v2_handshake.h
+++ b/device/fido/cable/v2_handshake.h
@@ -9,6 +9,7 @@
 
 #include <array>
 #include <memory>
+#include <optional>
 #include <string_view>
 
 #include "base/component_export.h"
@@ -18,7 +19,6 @@
 #include "device/fido/cable/noise.h"
 #include "device/fido/cable/v2_constants.h"
 #include "device/fido/fido_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/boringssl/src/include/openssl/base.h"
 
 class GURL;
@@ -30,7 +30,7 @@
 // ToKnownDomainID creates a KnownDomainID from a raw 16-bit value, or returns
 // |nullopt| if the value maps to an assigned, but unknown, domain.
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<KnownDomainID> ToKnownDomainID(uint16_t domain);
+std::optional<KnownDomainID> ToKnownDomainID(uint16_t domain);
 
 // DecodeDomain converts a 16-bit tunnel server domain into a string in dotted
 // form.
@@ -71,7 +71,7 @@
 // to |ToComponents|) by decrypting with |key|. It ensures that the encoded
 // tunnel server domain is recognised.
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<CableEidArray> Decrypt(
+std::optional<CableEidArray> Decrypt(
     const std::array<uint8_t, kAdvertSize>& advert,
     base::span<const uint8_t, kEIDKeySize> key);
 
@@ -117,7 +117,7 @@
   // supports_linking is true if the device showing the QR code supports storing
   // and later using linking information. If this is false or absent, an
   // authenticator may wish to avoid bothering the user about linking.
-  absl::optional<bool> supports_linking;
+  std::optional<bool> supports_linking;
 
   // request_type contains the hinted type of the request. This can
   // be used to guide UI ahead of receiving the actual request. This defaults to
@@ -126,7 +126,7 @@
 };
 
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<Components> Parse(const std::string& qr_url);
+std::optional<Components> Parse(const std::string& qr_url);
 
 // Encode returns the contents of a QR code that represents |qr_key|.
 COMPONENT_EXPORT(DEVICE_FIDO)
@@ -139,7 +139,7 @@
 
 // DigitsToBytes reverses the actions of |BytesToDigits|.
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<std::vector<uint8_t>> DigitsToBytes(std::string_view in);
+std::optional<std::vector<uint8_t>> DigitsToBytes(std::string_view in);
 
 }  // namespace qr
 
@@ -219,14 +219,13 @@
 // should be hidden. The function can fail if the CBOR encoding fails or,
 // somehow, the size overflows.
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<std::vector<uint8_t>> EncodePaddedCBORMap(
+std::optional<std::vector<uint8_t>> EncodePaddedCBORMap(
     cbor::Value::MapValue map);
 
 // DecodePaddedCBORMap unpads and decodes a CBOR map as produced by
 // |EncodePaddedCBORMap|.
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<cbor::Value> DecodePaddedCBORMap(
-    base::span<const uint8_t> input);
+std::optional<cbor::Value> DecodePaddedCBORMap(base::span<const uint8_t> input);
 
 // Crypter handles the post-handshake encryption of CTAP2 messages.
 class COMPONENT_EXPORT(DEVICE_FIDO) Crypter {
@@ -273,7 +272,7 @@
 // |Crypter| that can encrypt and decrypt future messages on the connection, and
 // the handshake hash that can be used to tie signatures to the connection.
 using HandshakeResult =
-    absl::optional<std::pair<std::unique_ptr<Crypter>, HandshakeHash>>;
+    std::optional<std::pair<std::unique_ptr<Crypter>, HandshakeHash>>;
 
 // HandshakeInitiator starts a caBLE v2 handshake and processes the single
 // response message from the other party. The handshake is always initiated from
@@ -283,15 +282,15 @@
   HandshakeInitiator(
       // psk is derived from the connection nonce and either QR-code secrets
       // pairing secrets. nullopt for enclave handshakes.
-      absl::optional<base::span<const uint8_t, 32>> psk,
+      std::optional<base::span<const uint8_t, 32>> psk,
       // peer_identity, if not nullopt, specifies that this is a paired
       // handshake and then contains a P-256 public key for the peer. Otherwise
       // this is a QR handshake.
-      absl::optional<base::span<const uint8_t, kP256X962Length>> peer_identity,
+      std::optional<base::span<const uint8_t, kP256X962Length>> peer_identity,
       // identity_seed, if not nullopt, specifies that this is a QR handshake
       // and contains the seed for QR key for this client. identity_seed must be
       // provided iff |peer_identity| is not.
-      absl::optional<base::span<const uint8_t, kQRSeedSize>> identity_seed);
+      std::optional<base::span<const uint8_t, kQRSeedSize>> identity_seed);
 
   ~HandshakeInitiator();
 
@@ -306,9 +305,9 @@
 
  private:
   Noise noise_;
-  absl::optional<std::array<uint8_t, 32>> psk_;
+  std::optional<std::array<uint8_t, 32>> psk_;
 
-  absl::optional<std::array<uint8_t, kP256X962Length>> peer_identity_;
+  std::optional<std::array<uint8_t, kP256X962Length>> peer_identity_;
   bssl::UniquePtr<EC_KEY> local_identity_;
   bssl::UniquePtr<EC_KEY> ephemeral_key_;
 };
@@ -319,13 +318,13 @@
 HandshakeResult RespondToHandshake(
     // psk is derived from the connection nonce and either QR-code secrets or
     // pairing secrets.
-    absl::optional<base::span<const uint8_t, 32>> psk,
+    std::optional<base::span<const uint8_t, 32>> psk,
     // identity, if not nullptr, specifies that this is a paired handshake and
     // contains the phone's private key.
     bssl::UniquePtr<EC_KEY> identity,
     // peer_identity, which must be non-nullopt iff |identity| is nullptr,
     // contains the peer's public key as taken from the QR code.
-    absl::optional<base::span<const uint8_t, kP256X962Length>> peer_identity,
+    std::optional<base::span<const uint8_t, kP256X962Length>> peer_identity,
     // in contains the initial handshake message from the peer.
     base::span<const uint8_t> in,
     // out_response is set to the response handshake message, if successful.
diff --git a/device/fido/cable/v2_handshake_fuzzer.cc b/device/fido/cable/v2_handshake_fuzzer.cc
index b4091a4..1fc907e 100644
--- a/device/fido/cable/v2_handshake_fuzzer.cc
+++ b/device/fido/cable/v2_handshake_fuzzer.cc
@@ -47,8 +47,8 @@
   const bool have_local_key = input[0] & 2;
   input = input.subspan(1);
 
-  absl::optional<base::span<const uint8_t, 65>> peer_identity;
-  absl::optional<base::span<const uint8_t, 32>> local_seed;
+  std::optional<base::span<const uint8_t, 65>> peer_identity;
+  std::optional<base::span<const uint8_t, 32>> local_seed;
   bssl::UniquePtr<EC_KEY> local_key;
   if (have_local_key) {
     local_seed = kTestLocalSeed;
diff --git a/device/fido/cable/v2_handshake_unittest.cc b/device/fido/cable/v2_handshake_unittest.cc
index 6234f332..88a8ffb3 100644
--- a/device/fido/cable/v2_handshake_unittest.cc
+++ b/device/fido/cable/v2_handshake_unittest.cc
@@ -68,7 +68,7 @@
   crypto::RandBytes(key);
   std::array<uint8_t, kAdvertSize> advert = eid::Encrypt(eid, key);
 
-  const absl::optional<CableEidArray> eid2 = eid::Decrypt(advert, key);
+  const std::optional<CableEidArray> eid2 = eid::Decrypt(advert, key);
   ASSERT_TRUE(eid2.has_value());
   EXPECT_TRUE(memcmp(eid.data(), eid2->data(), eid.size()) == 0);
 
@@ -86,7 +86,7 @@
   std::array<uint8_t, kQRKeySize> qr_key;
   crypto::RandBytes(qr_key);
   std::string url = qr::Encode(qr_key, FidoRequestType::kMakeCredential);
-  const absl::optional<qr::Components> decoded = qr::Parse(url);
+  const std::optional<qr::Components> decoded = qr::Parse(url);
   ASSERT_TRUE(decoded.has_value()) << url;
   static_assert(EXTENT(qr_key) >= EXTENT(decoded->secret), "");
   EXPECT_EQ(memcmp(decoded->secret.data(),
@@ -119,7 +119,7 @@
     std::function<void(cbor::Value::MapValue* m)> build;
     bool is_valid;
     int64_t num_known_domains;
-    absl::optional<bool> supports_linking;
+    std::optional<bool> supports_linking;
     FidoRequestType request_type;
   } kTests[] = {
       {
@@ -130,7 +130,7 @@
           },
           /* is_valid= */ true,
           /* num_known_domains= */ 0,
-          /* supports_linking= */ absl::nullopt,
+          /* supports_linking= */ std::nullopt,
           /* request_type= */ FidoRequestType::kGetAssertion,
       },
       {
@@ -161,7 +161,7 @@
           },
           /* is_valid= */ true,
           /* num_known_domains= */ 4567,
-          /* supports_linking= */ absl::nullopt,
+          /* supports_linking= */ std::nullopt,
           /* request_type= */ FidoRequestType::kGetAssertion,
       },
       {
@@ -215,7 +215,7 @@
           },
           /* is_valid= */ true,
           /* num_known_domains= */ 0,
-          /* supports_linking= */ absl::nullopt,
+          /* supports_linking= */ std::nullopt,
           /* request_type= */ FidoRequestType::kGetAssertion,
       },
       {
@@ -227,7 +227,7 @@
           },
           /* is_valid= */ true,
           /* num_known_domains= */ 0,
-          /* supports_linking= */ absl::nullopt,
+          /* supports_linking= */ std::nullopt,
           /* request_type= */ FidoRequestType::kMakeCredential,
       },
       {
@@ -239,7 +239,7 @@
           },
           /* is_valid= */ true,
           /* num_known_domains= */ 0,
-          /* supports_linking= */ absl::nullopt,
+          /* supports_linking= */ std::nullopt,
           /* request_type= */ FidoRequestType::kGetAssertion,
       },
       {
@@ -260,7 +260,7 @@
           },
           /* is_valid= */ true,
           /* num_known_domains= */ 0,
-          /* supports_linking= */ absl::nullopt,
+          /* supports_linking= */ std::nullopt,
           /* request_type= */ FidoRequestType::kGetAssertion,
       },
   };
@@ -272,10 +272,10 @@
 
     cbor::Value::MapValue map;
     test.build(&map);
-    const absl::optional<std::vector<uint8_t>> qr_data =
+    const std::optional<std::vector<uint8_t>> qr_data =
         cbor::Writer::Write(cbor::Value(std::move(map)));
     const std::string qr = std::string("FIDO:/") + qr::BytesToDigits(*qr_data);
-    const absl::optional<qr::Components> decoded = qr::Parse(qr);
+    const std::optional<qr::Components> decoded = qr::Parse(qr);
 
     EXPECT_EQ(decoded.has_value(), test.is_valid);
     if (!decoded.has_value() || !test.is_valid) {
@@ -300,12 +300,12 @@
 
 TEST(CableV2Encoding, PaddedCBOR) {
   cbor::Value::MapValue map1;
-  absl::optional<std::vector<uint8_t>> encoded =
+  std::optional<std::vector<uint8_t>> encoded =
       EncodePaddedCBORMap(std::move(map1));
   ASSERT_TRUE(encoded);
   EXPECT_EQ(kPostHandshakeMsgPaddingGranularity, encoded->size());
 
-  absl::optional<cbor::Value> decoded = DecodePaddedCBORMap(*encoded);
+  std::optional<cbor::Value> decoded = DecodePaddedCBORMap(*encoded);
   ASSERT_TRUE(decoded);
   EXPECT_EQ(0u, decoded->GetMap().size());
 
@@ -324,19 +324,19 @@
 // EncodePaddedCBORMapOld is the old padding function that used to be used.
 // We should still be compatible with it until M99 has been out in the world
 // for long enough.
-absl::optional<std::vector<uint8_t>> EncodePaddedCBORMapOld(
+std::optional<std::vector<uint8_t>> EncodePaddedCBORMapOld(
     cbor::Value::MapValue map) {
-  absl::optional<std::vector<uint8_t>> cbor_bytes =
+  std::optional<std::vector<uint8_t>> cbor_bytes =
       cbor::Writer::Write(cbor::Value(std::move(map)));
   if (!cbor_bytes) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   base::CheckedNumeric<size_t> padded_size_checked = cbor_bytes->size();
   padded_size_checked += 1;  // padding-length byte
   padded_size_checked = (padded_size_checked + 255) & ~255;
   if (!padded_size_checked.IsValid()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   const size_t padded_size = padded_size_checked.ValueOrDie();
@@ -358,11 +358,11 @@
     const std::vector<uint8_t> dummy_array(i);
     cbor::Value::MapValue map;
     map.emplace(1, dummy_array);
-    absl::optional<std::vector<uint8_t>> encoded =
+    std::optional<std::vector<uint8_t>> encoded =
         EncodePaddedCBORMapOld(std::move(map));
     ASSERT_TRUE(encoded);
 
-    absl::optional<cbor::Value> decoded = DecodePaddedCBORMap(*encoded);
+    std::optional<cbor::Value> decoded = DecodePaddedCBORMap(*encoded);
     ASSERT_TRUE(decoded);
   }
 }
@@ -385,7 +385,7 @@
   for (size_t i = 0; i < sizeof(test_data); i++) {
     std::string digits =
         qr::BytesToDigits(base::span<const uint8_t>(test_data, i));
-    absl::optional<std::vector<uint8_t>> test_data_again =
+    std::optional<std::vector<uint8_t>> test_data_again =
         qr::DigitsToBytes(digits);
     ASSERT_TRUE(test_data_again.has_value());
     ASSERT_EQ(test_data_again.value(),
@@ -405,7 +405,7 @@
   char digits[20];
   memset(digits, '0', sizeof(digits));
   for (size_t i = 0; i < sizeof(digits); i++) {
-    absl::optional<std::vector<uint8_t>> bytes =
+    std::optional<std::vector<uint8_t>> bytes =
         qr::DigitsToBytes(std::string_view(digits, i));
     if (!bytes.has_value()) {
       continue;
@@ -594,19 +594,19 @@
   for (const bool use_correct_key : {false, true}) {
     HandshakeInitiator initiator(use_correct_key ? psk_ : wrong_psk,
                                  identity_public_,
-                                 /*identity_seed=*/absl::nullopt);
+                                 /*identity_seed=*/std::nullopt);
     std::vector<uint8_t> message = initiator.BuildInitialMessage();
     std::vector<uint8_t> response;
     EC_KEY_up_ref(identity_key_.get());
-    HandshakeResult responder_result(RespondToHandshake(
-        psk_, bssl::UniquePtr<EC_KEY>(identity_key_.get()),
-        /*peer_identity=*/absl::nullopt, message, &response));
+    HandshakeResult responder_result(
+        RespondToHandshake(psk_, bssl::UniquePtr<EC_KEY>(identity_key_.get()),
+                           /*peer_identity=*/std::nullopt, message, &response));
     ASSERT_EQ(responder_result.has_value(), use_correct_key);
     if (!use_correct_key) {
       continue;
     }
 
-    absl::optional<std::pair<std::unique_ptr<Crypter>, HandshakeHash>>
+    std::optional<std::pair<std::unique_ptr<Crypter>, HandshakeHash>>
         initiator_result(initiator.ProcessResponse(response));
     ASSERT_TRUE(initiator_result.has_value());
     EXPECT_EQ(initiator_result->second, responder_result->second);
@@ -626,7 +626,7 @@
     base::span<const uint8_t, kQRSeedSize> seed =
         use_correct_key ? identity_seed_ : wrong_seed;
     HandshakeInitiator initiator(psk_,
-                                 /*peer_identity=*/absl::nullopt, seed);
+                                 /*peer_identity=*/std::nullopt, seed);
     std::vector<uint8_t> message = initiator.BuildInitialMessage();
     std::vector<uint8_t> response;
     HandshakeResult responder_result(RespondToHandshake(
@@ -638,7 +638,7 @@
       continue;
     }
 
-    absl::optional<std::pair<std::unique_ptr<Crypter>, HandshakeHash>>
+    std::optional<std::pair<std::unique_ptr<Crypter>, HandshakeHash>>
         initiator_result(initiator.ProcessResponse(response));
     ASSERT_TRUE(initiator_result.has_value());
     EXPECT_TRUE(responder_result->first->IsCounterpartyOfForTesting(
diff --git a/device/fido/cable/v2_registration.cc b/device/fido/cable/v2_registration.cc
index 8cb89391..288d553 100644
--- a/device/fido/cable/v2_registration.cc
+++ b/device/fido/cable/v2_registration.cc
@@ -89,11 +89,11 @@
                                              base::Unretained(this)));
   }
 
-  absl::optional<std::vector<uint8_t>> contact_id() const override {
+  std::optional<std::vector<uint8_t>> contact_id() const override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
     if (!registration_token_) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     return std::vector<uint8_t>(registration_token_->begin(),
                                 registration_token_->end());
@@ -112,7 +112,7 @@
       return;
     }
 
-    absl::optional<std::unique_ptr<Registration::Event>> event =
+    std::optional<std::unique_ptr<Registration::Event>> event =
         MessageToEvent(message.data, type_);
     if (!event) {
       FIDO_LOG(ERROR) << "Failed to decode FCM message. Ignoring.";
@@ -178,7 +178,7 @@
     PrepareContactID();
   }
 
-  static absl::optional<std::unique_ptr<Registration::Event>> MessageToEvent(
+  static std::optional<std::unique_ptr<Registration::Event>> MessageToEvent(
       const gcm::MessageData& data,
       Type source) {
     auto event = std::make_unique<Registration::Event>();
@@ -187,46 +187,46 @@
     gcm::MessageData::const_iterator it = data.find("caBLE.tunnelID");
     if (it == data.end() ||
         !base::HexStringToSpan(it->second, event->tunnel_id)) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     it = data.find("caBLE.routingID");
     if (it == data.end() ||
         !base::HexStringToSpan(it->second, event->routing_id)) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     std::vector<uint8_t> payload_bytes;
     it = data.find("caBLE.clientPayload");
     if (it == data.end() ||
         !base::HexStringToBytes(it->second, &payload_bytes)) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
-    absl::optional<cbor::Value> payload = cbor::Reader::Read(payload_bytes);
+    std::optional<cbor::Value> payload = cbor::Reader::Read(payload_bytes);
     if (!payload || !payload->is_map()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     const cbor::Value::MapValue& map = payload->GetMap();
     cbor::Value::MapValue::const_iterator cbor_it = map.find(cbor::Value(1));
     if (cbor_it == map.end() || !cbor_it->second.is_bytestring()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     const std::vector<uint8_t>& pairing_id = cbor_it->second.GetBytestring();
     if (pairing_id.size() != event->pairing_id.size()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     memcpy(event->pairing_id.data(), pairing_id.data(),
            event->pairing_id.size());
 
     if (!fido_parsing_utils::CopyCBORBytestring(&event->client_nonce, map, 2)) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     cbor_it = map.find(cbor::Value(3));
     if (cbor_it == map.end() || !cbor_it->second.is_string()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     const std::string& request_type_str = cbor_it->second.GetString();
     if (request_type_str == "mc") {
@@ -234,7 +234,7 @@
     } else if (request_type_str == "ga") {
       event->request_type = FidoRequestType::kGetAssertion;
     } else {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     return event;
@@ -247,7 +247,7 @@
   const raw_ptr<instance_id::InstanceIDDriver> instance_id_driver_;
   const raw_ptr<instance_id::InstanceID> instance_id_;
   bool registration_token_pending_ = false;
-  absl::optional<std::string> registration_token_;
+  std::optional<std::string> registration_token_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 };
@@ -311,7 +311,7 @@
   return e;
 }
 
-absl::optional<std::vector<uint8_t>> Registration::Event::Serialize() {
+std::optional<std::vector<uint8_t>> Registration::Event::Serialize() {
   bssl::ScopedCBB cbb;
   if (!CBB_init(cbb.get(), /*initial_capacity=*/512) ||
       !CBB_add_u8(cbb.get(), static_cast<uint8_t>(this->source)) ||
@@ -326,13 +326,13 @@
                      this->client_nonce.size()) ||
       (this->contact_id && !CBB_add_bytes(cbb.get(), this->contact_id->data(),
                                           this->contact_id->size()))) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   uint8_t* serialized_bytes;
   size_t serialized_bytes_len;
   if (!CBB_finish(cbb.get(), &serialized_bytes, &serialized_bytes_len)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   const std::vector<uint8_t> ret(serialized_bytes,
                                  serialized_bytes + serialized_bytes_len);
diff --git a/device/fido/cable/v2_registration.h b/device/fido/cable/v2_registration.h
index aafda08..fd9bf079 100644
--- a/device/fido/cable/v2_registration.h
+++ b/device/fido/cable/v2_registration.h
@@ -9,13 +9,13 @@
 
 #include <array>
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "base/containers/span.h"
 #include "base/functional/callback_forward.h"
 #include "device/fido/cable/v2_constants.h"
 #include "device/fido/fido_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace instance_id {
 class InstanceIDDriver;
@@ -51,7 +51,7 @@
 
     // Serialize returns a serialized form of the |Event|. This format is
     // not stable and is suitable only for transient storage.
-    absl::optional<std::vector<uint8_t>> Serialize();
+    std::optional<std::vector<uint8_t>> Serialize();
 
     // FromSerialized parses the bytes produced by |Serialize|. It assumes that
     // the input is well formed. It returns |nullptr| on error.
@@ -63,7 +63,7 @@
     std::array<uint8_t, kRoutingIdSize> routing_id;
     std::array<uint8_t, kPairingIDSize> pairing_id;
     std::array<uint8_t, kClientNonceSize> client_nonce;
-    absl::optional<std::vector<uint8_t>> contact_id;
+    std::optional<std::vector<uint8_t>> contact_id;
   };
 
   virtual ~Registration();
@@ -80,7 +80,7 @@
   // contact_id returns an opaque token that may be placed in pairing data for
   // desktops to later connect to. |nullopt| will be returned if the value is
   // not yet ready.
-  virtual absl::optional<std::vector<uint8_t>> contact_id() const = 0;
+  virtual std::optional<std::vector<uint8_t>> contact_id() const = 0;
 };
 
 // Register subscribes to the tunnel service and returns a |Registration|. This
diff --git a/device/fido/cable/v2_test_util.cc b/device/fido/cable/v2_test_util.cc
index 6779b0f4..dd44590 100644
--- a/device/fido/cable/v2_test_util.cc
+++ b/device/fido/cable/v2_test_util.cc
@@ -43,7 +43,7 @@
 // caBLEv2 tunnel server.
 class TestNetworkContext : public network::TestNetworkContext {
  public:
-  TestNetworkContext(absl::optional<ContactCallback> contact_callback,
+  TestNetworkContext(std::optional<ContactCallback> contact_callback,
                      bool supports_connect_signal)
       : contact_callback_(std::move(contact_callback)),
         supports_connect_signal_(supports_connect_signal) {}
@@ -66,7 +66,7 @@
       mojo::PendingRemote<network::mojom::WebSocketAuthenticationHandler>
           auth_handler,
       mojo::PendingRemote<network::mojom::TrustedHeaderClient> header_client,
-      const absl::optional<base::UnguessableToken>& throttling_profile_id)
+      const std::optional<base::UnguessableToken>& throttling_profile_id)
       override {
     CHECK(url.has_path());
 
@@ -116,7 +116,7 @@
       CHECK(base::HexStringToBytes(additional_headers[0]->value,
                                    &client_payload_bytes));
 
-      absl::optional<cbor::Value> client_payload =
+      std::optional<cbor::Value> client_payload =
           cbor::Reader::Read(client_payload_bytes);
       const cbor::Value::MapValue& map = client_payload->GetMap();
 
@@ -382,7 +382,7 @@
   };
 
   std::map<std::string, std::unique_ptr<Connection>> connections_;
-  const absl::optional<ContactCallback> contact_callback_;
+  const std::optional<ContactCallback> contact_callback_;
   const bool supports_connect_signal_;
 };
 
@@ -417,7 +417,7 @@
                   ResidentKeyRequirement::kRequired;
     request.prf = params->prf_enable;
 
-    std::pair<device::CtapRequestCommand, absl::optional<cbor::Value>>
+    std::pair<device::CtapRequestCommand, std::optional<cbor::Value>>
         request_cbor = AsCTAPRequestValuePair(request);
 
     ctap2_device_->DeviceTransact(
@@ -461,7 +461,7 @@
       }
     }
 
-    std::pair<device::CtapRequestCommand, absl::optional<cbor::Value>>
+    std::pair<device::CtapRequestCommand, std::optional<cbor::Value>>
         request_cbor = AsCTAPRequestValuePair(request);
 
     ctap2_device_->DeviceTransact(
@@ -476,7 +476,7 @@
     }
   }
 
-  void OnCompleted(absl::optional<Error> maybe_error) override {
+  void OnCompleted(std::optional<Error> maybe_error) override {
     if (observer_) {
       observer_->OnCompleted(maybe_error);
     }
@@ -498,12 +498,12 @@
   }
 
   std::vector<uint8_t> ToCTAP2Command(
-      const std::pair<device::CtapRequestCommand, absl::optional<cbor::Value>>&
+      const std::pair<device::CtapRequestCommand, std::optional<cbor::Value>>&
           parts) {
     std::vector<uint8_t> ret;
 
     if (parts.second.has_value()) {
-      absl::optional<std::vector<uint8_t>> cbor_bytes =
+      std::optional<std::vector<uint8_t>> cbor_bytes =
           cbor::Writer::Write(std::move(*parts.second));
       ret.swap(*cbor_bytes);
     }
@@ -513,7 +513,7 @@
   }
 
   void OnMakeCredentialResult(MakeCredentialCallback callback,
-                              absl::optional<std::vector<uint8_t>> result) {
+                              std::optional<std::vector<uint8_t>> result) {
     if (!result || result->empty()) {
       std::move(callback).Run(
           static_cast<uint32_t>(device::CtapDeviceResponseCode::kCtap2ErrOther),
@@ -530,7 +530,7 @@
       return;
     }
 
-    absl::optional<cbor::Value> v = cbor::Reader::Read(payload.subspan(1));
+    std::optional<cbor::Value> v = cbor::Reader::Read(payload.subspan(1));
     const cbor::Value::MapValue& in_map = v->GetMap();
 
     cbor::Value::MapValue out_map;
@@ -553,7 +553,7 @@
       }
     }
 
-    absl::optional<std::vector<uint8_t>> attestation_obj =
+    std::optional<std::vector<uint8_t>> attestation_obj =
         cbor::Writer::Write(cbor::Value(std::move(out_map)));
 
     std::move(callback).Run(
@@ -562,7 +562,7 @@
   }
 
   void OnGetAssertionResult(GetAssertionCallback callback,
-                            absl::optional<std::vector<uint8_t>> result) {
+                            std::optional<std::vector<uint8_t>> result) {
     if (!result || result->empty()) {
       std::move(callback).Run(
           static_cast<uint32_t>(device::CtapDeviceResponseCode::kCtap2ErrOther),
@@ -583,7 +583,7 @@
     response->extensions =
         blink::mojom::AuthenticationExtensionsClientOutputs::New();
 
-    absl::optional<cbor::Value> v = cbor::Reader::Read(payload.subspan(1));
+    std::optional<cbor::Value> v = cbor::Reader::Read(payload.subspan(1));
     const cbor::Value::MapValue& in_map = v->GetMap();
 
     auto cred_id_it = in_map.find(cbor::Value(1));
@@ -681,13 +681,13 @@
         /*url_loader_network_observer=*/mojo::NullRemote(),
         /*auth_handler=*/mojo::NullRemote(),
         /*header_client=*/mojo::NullRemote(),
-        /*throttling_profile_id=*/absl::nullopt);
+        /*throttling_profile_id=*/std::nullopt);
   }
 
  private:
   void OnTunnelReady(
       WebSocketAdapter::Result result,
-      absl::optional<std::array<uint8_t, device::cablev2::kRoutingIdSize>>
+      std::optional<std::array<uint8_t, device::cablev2::kRoutingIdSize>>
           routing_id,
       WebSocketAdapter::ConnectSignalSupport connect_signal_support) {
     CHECK_EQ(result, WebSocketAdapter::Result::OK);
@@ -732,9 +732,9 @@
     return cbor::Writer::Write(cbor::Value(std::move(response_map))).value();
   }
 
-  void OnTunnelData(absl::optional<base::span<const uint8_t>> msg) {
+  void OnTunnelData(std::optional<base::span<const uint8_t>> msg) {
     if (!msg) {
-      platform_->OnCompleted(absl::nullopt);
+      platform_->OnCompleted(std::nullopt);
       return;
     }
 
@@ -751,7 +751,7 @@
         cbor::Value::MapValue post_handshake_msg;
         post_handshake_msg.emplace(1, BuildGetInfoResponse());
 
-        absl::optional<std::vector<uint8_t>> post_handshake_msg_bytes =
+        std::optional<std::vector<uint8_t>> post_handshake_msg_bytes =
             cbor::Writer::Write(cbor::Value(std::move(post_handshake_msg)));
         CHECK(post_handshake_msg_bytes);
         CHECK(crypter_->Encrypt(&post_handshake_msg_bytes.value()));
@@ -833,7 +833,7 @@
     cbor::Value::MapValue update_msg;
     update_msg.emplace(1, cbor::Value(std::move(pairing)));
 
-    absl::optional<std::vector<uint8_t>> update_msg_bytes =
+    std::optional<std::vector<uint8_t>> update_msg_bytes =
         cbor::Writer::Write(cbor::Value(std::move(update_msg)));
     CHECK(update_msg_bytes);
     update_msg_bytes->insert(update_msg_bytes->begin(),
@@ -900,13 +900,13 @@
         /*url_loader_network_observer=*/mojo::NullRemote(),
         /*auth_handler=*/mojo::NullRemote(),
         /*header_client=*/mojo::NullRemote(),
-        /*throttling_profile_id=*/absl::nullopt);
+        /*throttling_profile_id=*/std::nullopt);
   }
 
  private:
   void OnTunnelReady(
       WebSocketAdapter::Result result,
-      absl::optional<std::array<uint8_t, device::cablev2::kRoutingIdSize>>
+      std::optional<std::array<uint8_t, device::cablev2::kRoutingIdSize>>
           routing_id,
       WebSocketAdapter::ConnectSignalSupport connect_signal_support) {
     CHECK_EQ(result, WebSocketAdapter::Result::OK);
@@ -932,7 +932,7 @@
     return ret;
   }
 
-  void OnTunnelData(absl::optional<base::span<const uint8_t>> msg) {
+  void OnTunnelData(std::optional<base::span<const uint8_t>> msg) {
     std::vector<uint8_t> response = {'b', 'o', 'g', 'u', 's'};
     websocket_client_->Write(response);
   }
@@ -952,7 +952,7 @@
 }  // namespace authenticator
 
 std::unique_ptr<network::mojom::NetworkContext> NewMockTunnelServer(
-    absl::optional<ContactCallback> contact_callback,
+    std::optional<ContactCallback> contact_callback,
     bool supports_connect_signal) {
   return std::make_unique<TestNetworkContext>(std::move(contact_callback),
                                               supports_connect_signal);
diff --git a/device/fido/cable/v2_test_util.h b/device/fido/cable/v2_test_util.h
index ca46a90..3b7286c 100644
--- a/device/fido/cable/v2_test_util.h
+++ b/device/fido/cable/v2_test_util.h
@@ -6,6 +6,7 @@
 #define DEVICE_FIDO_CABLE_V2_TEST_UTIL_H_
 
 #include <memory>
+#include <optional>
 
 #include "base/containers/span.h"
 #include "base/functional/callback_forward.h"
@@ -13,7 +14,6 @@
 #include "device/fido/cable/v2_constants.h"
 #include "device/fido/cable/v2_discovery.h"
 #include "services/network/public/mojom/network_context.mojom-forward.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -35,7 +35,7 @@
 // |nullopt| then all contact requests will be rejected with an HTTP 410 status
 // to indicate that the contact ID is disabled.
 std::unique_ptr<network::mojom::NetworkContext> NewMockTunnelServer(
-    absl::optional<ContactCallback> contact_callback,
+    std::optional<ContactCallback> contact_callback,
     bool supports_connect_signal = false);
 
 namespace authenticator {
@@ -48,7 +48,7 @@
   virtual void OnStatus(Platform::Status) = 0;
 
   // See `Platform::OnCompleted`.
-  virtual void OnCompleted(absl::optional<Platform::Error>) = 0;
+  virtual void OnCompleted(std::optional<Platform::Error>) = 0;
 };
 
 // NewMockPlatform returns a |Platform| that implements the makeCredential
diff --git a/device/fido/cable/websocket_adapter.cc b/device/fido/cable/websocket_adapter.cc
index 083674fc..03fd19cf6 100644
--- a/device/fido/cable/websocket_adapter.cc
+++ b/device/fido/cable/websocket_adapter.cc
@@ -84,7 +84,7 @@
   // this device should be dropped.
   if (on_tunnel_ready_) {
     std::move(on_tunnel_ready_)
-        .Run(Result::GONE, absl::nullopt, ConnectSignalSupport::NO);
+        .Run(Result::GONE, std::nullopt, ConnectSignalSupport::NO);
     // `this` may be invalid now.
   }
 }
@@ -102,7 +102,7 @@
     return;
   }
 
-  absl::optional<std::array<uint8_t, kRoutingIdSize>> routing_id;
+  std::optional<std::array<uint8_t, kRoutingIdSize>> routing_id;
   ConnectSignalSupport connect_signal_support = ConnectSignalSupport::NO;
   for (const auto& header : response->headers) {
     if (base::EqualsCaseInsensitiveASCII(header->name.c_str(),
@@ -238,7 +238,7 @@
   // failure to establish the tunnel.
   if (on_tunnel_ready_) {
     std::move(on_tunnel_ready_)
-        .Run(Result::FAILED, absl::nullopt, ConnectSignalSupport::NO);
+        .Run(Result::FAILED, std::nullopt, ConnectSignalSupport::NO);
     // `this` may be invalid now.
     return;
   }
@@ -253,7 +253,7 @@
   DCHECK(!closed_);
   closed_ = true;
   client_receiver_.reset();
-  on_tunnel_data_.Run(absl::nullopt);
+  on_tunnel_data_.Run(std::nullopt);
   // `this` may be invalid now.
 }
 
diff --git a/device/fido/cable/websocket_adapter.h b/device/fido/cable/websocket_adapter.h
index 30f5ec1..51f3354 100644
--- a/device/fido/cable/websocket_adapter.h
+++ b/device/fido/cable/websocket_adapter.h
@@ -5,6 +5,7 @@
 #ifndef DEVICE_FIDO_CABLE_WEBSOCKET_ADAPTER_H_
 #define DEVICE_FIDO_CABLE_WEBSOCKET_ADAPTER_H_
 
+#include <optional>
 #include <vector>
 
 #include "base/component_export.h"
@@ -17,7 +18,6 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/network/public/mojom/network_context.mojom.h"
 #include "services/network/public/mojom/websocket.mojom.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 namespace cablev2 {
@@ -48,10 +48,10 @@
 
   using TunnelReadyCallback = base::OnceCallback<void(
       Result,
-      absl::optional<std::array<uint8_t, kRoutingIdSize>>,
+      std::optional<std::array<uint8_t, kRoutingIdSize>>,
       ConnectSignalSupport)>;
   using TunnelDataCallback =
-      base::RepeatingCallback<void(absl::optional<base::span<const uint8_t>>)>;
+      base::RepeatingCallback<void(std::optional<base::span<const uint8_t>>)>;
   WebSocketAdapter(
       // on_tunnel_ready is called once with a boolean that indicates whether
       // the WebSocket successfully connected and an optional routing ID.
diff --git a/device/fido/credential_management.cc b/device/fido/credential_management.cc
index 5b9930f..5ee0152 100644
--- a/device/fido/credential_management.cc
+++ b/device/fido/credential_management.cc
@@ -18,7 +18,7 @@
 CredentialManagementRequest::CredentialManagementRequest(
     Version version_,
     CredentialManagementSubCommand subcommand_,
-    absl::optional<cbor::Value::MapValue> params_)
+    std::optional<cbor::Value::MapValue> params_)
     : version(version_), subcommand(subcommand_), params(std::move(params_)) {}
 CredentialManagementRequest::CredentialManagementRequest(
     CredentialManagementRequest&&) = default;
@@ -32,7 +32,7 @@
     const pin::TokenResponse& token) {
   CredentialManagementRequest request(
       version, CredentialManagementSubCommand::kGetCredsMetadata,
-      /*params=*/absl::nullopt);
+      /*params=*/std::nullopt);
   std::tie(request.pin_protocol, request.pin_auth) =
       token.PinAuth({{static_cast<uint8_t>(
           CredentialManagementSubCommand::kGetCredsMetadata)}});
@@ -45,7 +45,7 @@
     const pin::TokenResponse& token) {
   CredentialManagementRequest request(
       version, CredentialManagementSubCommand::kEnumerateRPsBegin,
-      /*params=*/absl::nullopt);
+      /*params=*/std::nullopt);
   std::tie(request.pin_protocol, request.pin_auth) =
       token.PinAuth({{static_cast<uint8_t>(
           CredentialManagementSubCommand::kEnumerateRPsBegin)}});
@@ -57,7 +57,7 @@
     Version version) {
   return CredentialManagementRequest(
       version, CredentialManagementSubCommand::kEnumerateRPsGetNextRP,
-      /*params=*/absl::nullopt);
+      /*params=*/std::nullopt);
 }
 
 // static
@@ -90,7 +90,7 @@
   return CredentialManagementRequest(
       version,
       CredentialManagementSubCommand::kEnumerateCredentialsGetNextCredential,
-      /*params=*/absl::nullopt);
+      /*params=*/std::nullopt);
 }
 
 // static
@@ -145,23 +145,23 @@
 }
 
 // static
-absl::optional<CredentialsMetadataResponse> CredentialsMetadataResponse::Parse(
-    const absl::optional<cbor::Value>& cbor_response) {
+std::optional<CredentialsMetadataResponse> CredentialsMetadataResponse::Parse(
+    const std::optional<cbor::Value>& cbor_response) {
   CredentialsMetadataResponse response;
 
   if (!cbor_response || !cbor_response->is_map()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   const cbor::Value::MapValue& response_map = cbor_response->GetMap();
 
   auto it = response_map.find(cbor::Value(static_cast<int>(
       CredentialManagementResponseKey::kExistingResidentCredentialsCount)));
   if (it == response_map.end() || !it->second.is_unsigned()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   const int64_t existing_count = it->second.GetUnsigned();
   if (existing_count > std::numeric_limits<size_t>::max()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   response.num_existing_credentials = static_cast<size_t>(existing_count);
 
@@ -169,11 +169,11 @@
       static_cast<int>(CredentialManagementResponseKey::
                            kMaxPossibleRemainingResidentCredentialsCount)));
   if (it == response_map.end() || !it->second.is_unsigned()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   const int64_t remaining_count = it->second.GetUnsigned();
   if (remaining_count > std::numeric_limits<size_t>::max()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   response.num_estimated_remaining_credentials =
       static_cast<size_t>(remaining_count);
@@ -182,16 +182,16 @@
 }
 
 // static
-absl::optional<EnumerateRPsResponse> EnumerateRPsResponse::Parse(
+std::optional<EnumerateRPsResponse> EnumerateRPsResponse::Parse(
     bool expect_rp_count,
-    const absl::optional<cbor::Value>& cbor_response) {
+    const std::optional<cbor::Value>& cbor_response) {
   if (!cbor_response) {
     // Some authenticators send an empty response if there are no RPs (though
     // the spec doesn't say that).
-    return EnumerateRPsResponse(absl::nullopt, absl::nullopt, 0);
+    return EnumerateRPsResponse(std::nullopt, std::nullopt, 0);
   }
   if (!cbor_response->is_map() || cbor_response->GetMap().empty()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   const cbor::Value::MapValue& response_map = cbor_response->GetMap();
 
@@ -199,40 +199,40 @@
   auto it = response_map.find(cbor::Value(
       static_cast<int>(CredentialManagementResponseKey::kTotalRPs)));
   if (!expect_rp_count && it != response_map.end()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   if (expect_rp_count) {
     if (it == response_map.end() || !it->second.is_unsigned() ||
         it->second.GetUnsigned() > std::numeric_limits<size_t>::max()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     rp_count = static_cast<size_t>(it->second.GetUnsigned());
     if (rp_count == 0) {
       if (response_map.size() != 1) {
-        return absl::nullopt;
+        return std::nullopt;
       }
-      return EnumerateRPsResponse(absl::nullopt, absl::nullopt, 0);
+      return EnumerateRPsResponse(std::nullopt, std::nullopt, 0);
     }
   }
 
   it = response_map.find(
       cbor::Value(static_cast<int>(CredentialManagementResponseKey::kRP)));
   if (it == response_map.end()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   auto opt_rp = PublicKeyCredentialRpEntity::CreateFromCBORValue(it->second);
   if (!opt_rp) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   it = response_map.find(cbor::Value(
       static_cast<int>(CredentialManagementResponseKey::kRPIDHash)));
   if (it == response_map.end() || !it->second.is_bytestring()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   const std::vector<uint8_t>& rp_id_hash_bytes = it->second.GetBytestring();
   if (rp_id_hash_bytes.size() != kRpIdHashLength) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   std::array<uint8_t, kRpIdHashLength> rp_id_hash;
   std::copy_n(rp_id_hash_bytes.begin(), kRpIdHashLength, rp_id_hash.begin());
@@ -259,63 +259,62 @@
     default;
 EnumerateRPsResponse::~EnumerateRPsResponse() = default;
 EnumerateRPsResponse::EnumerateRPsResponse(
-    absl::optional<PublicKeyCredentialRpEntity> rp_,
-    absl::optional<std::array<uint8_t, kRpIdHashLength>> rp_id_hash_,
+    std::optional<PublicKeyCredentialRpEntity> rp_,
+    std::optional<std::array<uint8_t, kRpIdHashLength>> rp_id_hash_,
     size_t rp_count_)
     : rp(std::move(rp_)),
       rp_id_hash(std::move(rp_id_hash_)),
       rp_count(rp_count_) {}
 
 //  static
-absl::optional<EnumerateCredentialsResponse>
-EnumerateCredentialsResponse::Parse(
+std::optional<EnumerateCredentialsResponse> EnumerateCredentialsResponse::Parse(
     bool expect_credential_count,
-    const absl::optional<cbor::Value>& cbor_response) {
+    const std::optional<cbor::Value>& cbor_response) {
   if (!cbor_response || !cbor_response->is_map()) {
     // Note that some authenticators may send an empty response if they don't
     // have a credential for a given RP ID hash (though the spec doesn't say
     // that). However, that case should not be reached from
     // CredentialManagementHandler.
-    return absl::nullopt;
+    return std::nullopt;
   }
   const cbor::Value::MapValue& response_map = cbor_response->GetMap();
 
   auto it = response_map.find(
       cbor::Value(static_cast<int>(CredentialManagementResponseKey::kUser)));
   if (it == response_map.end()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   auto opt_user =
       PublicKeyCredentialUserEntity::CreateFromCBORValue(it->second);
   if (!opt_user) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   it = response_map.find(cbor::Value(
       static_cast<int>(CredentialManagementResponseKey::kCredentialID)));
   if (it == response_map.end()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   auto opt_credential_id =
       PublicKeyCredentialDescriptor::CreateFromCBORValue(it->second);
   if (!opt_credential_id) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   // Ignore the public key's value.
   it = response_map.find(cbor::Value(
       static_cast<int>(CredentialManagementResponseKey::kPublicKey)));
   if (it == response_map.end() || !it->second.is_map()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
-  absl::optional<std::array<uint8_t, kLargeBlobKeyLength>> large_blob_key;
+  std::optional<std::array<uint8_t, kLargeBlobKeyLength>> large_blob_key;
   it = response_map.find(cbor::Value(
       static_cast<int>(CredentialManagementResponseKey::kLargeBlobKey)));
   if (it != response_map.end()) {
     if (!it->second.is_bytestring() ||
         it->second.GetBytestring().size() != kLargeBlobKeyLength) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     large_blob_key = fido_parsing_utils::Materialize(
         base::make_span<kLargeBlobKeyLength>(it->second.GetBytestring()));
@@ -326,7 +325,7 @@
     if (response_map.find(cbor::Value(static_cast<int>(
             CredentialManagementResponseKey::kTotalCredentials))) !=
         response_map.end()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
   } else {
     it = response_map.find(cbor::Value(
@@ -334,7 +333,7 @@
     if (it == response_map.end() || !it->second.is_unsigned() ||
         it->second.GetUnsigned() == 0 ||
         it->second.GetUnsigned() > std::numeric_limits<size_t>::max()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     credential_count = static_cast<size_t>(it->second.GetUnsigned());
   }
@@ -366,7 +365,7 @@
     PublicKeyCredentialUserEntity user_,
     PublicKeyCredentialDescriptor credential_id_,
     size_t credential_count_,
-    absl::optional<std::array<uint8_t, kLargeBlobKeyLength>> large_blob_key_)
+    std::optional<std::array<uint8_t, kLargeBlobKeyLength>> large_blob_key_)
     : user(std::move(user_)),
       credential_id(std::move(credential_id_)),
       credential_count(credential_count_),
@@ -383,7 +382,7 @@
 AggregatedEnumerateCredentialsResponse::
     ~AggregatedEnumerateCredentialsResponse() = default;
 
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const CredentialManagementRequest& request) {
   cbor::Value::MapValue request_map;
   request_map.emplace(
diff --git a/device/fido/credential_management.h b/device/fido/credential_management.h
index 1dd681e..ffd383f7 100644
--- a/device/fido/credential_management.h
+++ b/device/fido/credential_management.h
@@ -5,13 +5,14 @@
 #ifndef DEVICE_FIDO_CREDENTIAL_MANAGEMENT_H_
 #define DEVICE_FIDO_CREDENTIAL_MANAGEMENT_H_
 
+#include <optional>
+
 #include "base/component_export.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/pin.h"
 #include "device/fido/public_key_credential_descriptor.h"
 #include "device/fido/public_key_credential_rp_entity.h"
 #include "device/fido/public_key_credential_user_entity.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace cbor {
 class Value;
@@ -69,8 +70,8 @@
 template <class T>
 class CredentialManagementPreviewRequestAdapter {
  public:
-  static std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
-  EncodeAsCBOR(const CredentialManagementPreviewRequestAdapter<T>& request) {
+  static std::pair<CtapRequestCommand, std::optional<cbor::Value>> EncodeAsCBOR(
+      const CredentialManagementPreviewRequestAdapter<T>& request) {
     auto result = T::EncodeAsCBOR(request.wrapped_request_);
     DCHECK_EQ(result.first,
               CtapRequestCommand::kAuthenticatorCredentialManagement);
@@ -90,8 +91,8 @@
 // CTAP2 request. Instances can be obtained via one of the subcommand-specific
 // static factory methods.
 struct CredentialManagementRequest {
-  static std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
-  EncodeAsCBOR(const CredentialManagementRequest&);
+  static std::pair<CtapRequestCommand, std::optional<cbor::Value>> EncodeAsCBOR(
+      const CredentialManagementRequest&);
 
   enum Version {
     kDefault,
@@ -123,7 +124,7 @@
 
   CredentialManagementRequest(Version version,
                               CredentialManagementSubCommand subcommand,
-                              absl::optional<cbor::Value::MapValue> params);
+                              std::optional<cbor::Value::MapValue> params);
   CredentialManagementRequest(CredentialManagementRequest&&);
   CredentialManagementRequest& operator=(CredentialManagementRequest&&);
   CredentialManagementRequest(const CredentialManagementRequest&) = delete;
@@ -133,14 +134,14 @@
 
   Version version;
   CredentialManagementSubCommand subcommand;
-  absl::optional<cbor::Value::MapValue> params;
-  absl::optional<PINUVAuthProtocol> pin_protocol;
-  absl::optional<std::vector<uint8_t>> pin_auth;
+  std::optional<cbor::Value::MapValue> params;
+  std::optional<PINUVAuthProtocol> pin_protocol;
+  std::optional<std::vector<uint8_t>> pin_auth;
 };
 
 struct CredentialsMetadataResponse {
-  static absl::optional<CredentialsMetadataResponse> Parse(
-      const absl::optional<cbor::Value>& cbor_response);
+  static std::optional<CredentialsMetadataResponse> Parse(
+      const std::optional<cbor::Value>& cbor_response);
 
   size_t num_existing_credentials;
   size_t num_estimated_remaining_credentials;
@@ -150,9 +151,9 @@
 };
 
 struct EnumerateRPsResponse {
-  static absl::optional<EnumerateRPsResponse> Parse(
+  static std::optional<EnumerateRPsResponse> Parse(
       bool expect_rp_count,
-      const absl::optional<cbor::Value>& cbor_response);
+      const std::optional<cbor::Value>& cbor_response);
 
   // StringFixupPredicate indicates which fields of an EnumerateRPsResponse may
   // contain truncated UTF-8 strings. See
@@ -163,23 +164,23 @@
   EnumerateRPsResponse& operator=(EnumerateRPsResponse&&);
   ~EnumerateRPsResponse();
 
-  absl::optional<PublicKeyCredentialRpEntity> rp;
-  absl::optional<std::array<uint8_t, kRpIdHashLength>> rp_id_hash;
+  std::optional<PublicKeyCredentialRpEntity> rp;
+  std::optional<std::array<uint8_t, kRpIdHashLength>> rp_id_hash;
   size_t rp_count;
 
  private:
   EnumerateRPsResponse(
-      absl::optional<PublicKeyCredentialRpEntity> rp,
-      absl::optional<std::array<uint8_t, kRpIdHashLength>> rp_id_hash,
+      std::optional<PublicKeyCredentialRpEntity> rp,
+      std::optional<std::array<uint8_t, kRpIdHashLength>> rp_id_hash,
       size_t rp_count);
   EnumerateRPsResponse(const EnumerateRPsResponse&) = delete;
   EnumerateRPsResponse& operator=(const EnumerateRPsResponse&) = delete;
 };
 
 struct EnumerateCredentialsResponse {
-  static absl::optional<EnumerateCredentialsResponse> Parse(
+  static std::optional<EnumerateCredentialsResponse> Parse(
       bool expect_credential_count,
-      const absl::optional<cbor::Value>& cbor_response);
+      const std::optional<cbor::Value>& cbor_response);
 
   // StringFixupPredicate indicates which fields of an
   // EnumerateCredentialsResponse may contain truncated UTF-8 strings. See
@@ -196,14 +197,14 @@
   PublicKeyCredentialUserEntity user;
   PublicKeyCredentialDescriptor credential_id;
   size_t credential_count;
-  absl::optional<std::array<uint8_t, kLargeBlobKeyLength>> large_blob_key;
+  std::optional<std::array<uint8_t, kLargeBlobKeyLength>> large_blob_key;
 
  private:
   EnumerateCredentialsResponse(
       PublicKeyCredentialUserEntity user,
       PublicKeyCredentialDescriptor credential_id,
       size_t credential_count,
-      absl::optional<std::array<uint8_t, kLargeBlobKeyLength>> large_blob_key);
+      std::optional<std::array<uint8_t, kLargeBlobKeyLength>> large_blob_key);
 };
 
 struct COMPONENT_EXPORT(DEVICE_FIDO) AggregatedEnumerateCredentialsResponse {
@@ -224,7 +225,7 @@
 using DeleteCredentialResponse = pin::EmptyResponse;
 using UpdateUserInformationResponse = pin::EmptyResponse;
 
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const CredentialManagementRequest&);
 
 }  // namespace device
diff --git a/device/fido/credential_management_handler.cc b/device/fido/credential_management_handler.cc
index 9447ef88..45f8411 100644
--- a/device/fido/credential_management_handler.cc
+++ b/device/fido/credential_management_handler.cc
@@ -88,7 +88,7 @@
 
 void CredentialManagementHandler::OnRetriesResponse(
     CtapDeviceResponseCode status,
-    absl::optional<pin::RetriesResponse> response) {
+    std::optional<pin::RetriesResponse> response) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(state_, State::kGettingRetries);
   if (status != CtapDeviceResponseCode::kSuccess) {
@@ -129,14 +129,14 @@
   }
   authenticator_->GetPINToken(
       std::move(pin), std::move(permissions),
-      /*rp_id=*/absl::nullopt,
+      /*rp_id=*/std::nullopt,
       base::BindOnce(&CredentialManagementHandler::OnHavePINToken,
                      weak_factory_.GetWeakPtr()));
 }
 
 void CredentialManagementHandler::OnHavePINToken(
     CtapDeviceResponseCode status,
-    absl::optional<pin::TokenResponse> response) {
+    std::optional<pin::TokenResponse> response) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(state_, State::kGettingPINToken);
 
@@ -211,7 +211,7 @@
     std::vector<device::PublicKeyCredentialDescriptor> remaining_credential_ids,
     CredentialManagementHandler::DeleteCredentialCallback callback,
     CtapDeviceResponseCode status,
-    absl::optional<DeleteCredentialResponse> response) {
+    std::optional<DeleteCredentialResponse> response) {
   if (status != CtapDeviceResponseCode::kSuccess) {
     std::move(callback).Run(status);
     return;
@@ -274,7 +274,7 @@
 static void OnUpdateUserInformation(
     CredentialManagementHandler::UpdateUserInformationCallback callback,
     CtapDeviceResponseCode status,
-    absl::optional<UpdateUserInformationResponse> response) {
+    std::optional<UpdateUserInformationResponse> response) {
   std::move(callback).Run(status);
 }
 
@@ -298,11 +298,11 @@
 
 void CredentialManagementHandler::OnCredentialsMetadata(
     CtapDeviceResponseCode status,
-    absl::optional<CredentialsMetadataResponse> response) {
+    std::optional<CredentialsMetadataResponse> response) {
   if (status != CtapDeviceResponseCode::kSuccess) {
     state_ = State::kFinished;
     std::move(get_credentials_callback_)
-        .Run(status, absl::nullopt, absl::nullopt);
+        .Run(status, std::nullopt, std::nullopt);
     return;
   }
   authenticator_->EnumerateCredentials(
@@ -314,12 +314,12 @@
 void CredentialManagementHandler::OnEnumerateCredentials(
     CredentialsMetadataResponse metadata_response,
     CtapDeviceResponseCode status,
-    absl::optional<std::vector<AggregatedEnumerateCredentialsResponse>>
+    std::optional<std::vector<AggregatedEnumerateCredentialsResponse>>
         responses) {
   if (status != CtapDeviceResponseCode::kSuccess) {
     state_ = State::kFinished;
     std::move(get_credentials_callback_)
-        .Run(status, absl::nullopt, absl::nullopt);
+        .Run(status, std::nullopt, std::nullopt);
     return;
   }
 
diff --git a/device/fido/credential_management_handler.h b/device/fido/credential_management_handler.h
index 82f7517..e75d676 100644
--- a/device/fido/credential_management_handler.h
+++ b/device/fido/credential_management_handler.h
@@ -63,8 +63,8 @@
   using FinishedCallback = base::OnceCallback<void(CredentialManagementStatus)>;
   using GetCredentialsCallback = base::OnceCallback<void(
       CtapDeviceResponseCode,
-      absl::optional<std::vector<AggregatedEnumerateCredentialsResponse>>,
-      absl::optional<size_t>)>;
+      std::optional<std::vector<AggregatedEnumerateCredentialsResponse>>,
+      std::optional<size_t>)>;
   using GetPINCallback =
       base::RepeatingCallback<void(AuthenticatorProperties,
                                    base::OnceCallback<void(std::string)>)>;
@@ -128,30 +128,30 @@
 
   void OnTouch(FidoAuthenticator* authenticator);
   void OnRetriesResponse(CtapDeviceResponseCode status,
-                         absl::optional<pin::RetriesResponse> response);
+                         std::optional<pin::RetriesResponse> response);
   void OnHavePIN(std::string pin);
   void OnHavePINToken(CtapDeviceResponseCode status,
-                      absl::optional<pin::TokenResponse> response);
+                      std::optional<pin::TokenResponse> response);
   void OnInitFinished(CtapDeviceResponseCode status);
   void OnCredentialsMetadata(
       CtapDeviceResponseCode status,
-      absl::optional<CredentialsMetadataResponse> response);
+      std::optional<CredentialsMetadataResponse> response);
   void OnEnumerateCredentials(
       CredentialsMetadataResponse metadata_response,
       CtapDeviceResponseCode status,
-      absl::optional<std::vector<AggregatedEnumerateCredentialsResponse>>
+      std::optional<std::vector<AggregatedEnumerateCredentialsResponse>>
           responses);
   void OnDeleteCredentials(
       std::vector<PublicKeyCredentialDescriptor> remaining_credential_ids,
       CredentialManagementHandler::DeleteCredentialCallback callback,
       CtapDeviceResponseCode status,
-      absl::optional<DeleteCredentialResponse> response);
+      std::optional<DeleteCredentialResponse> response);
 
   SEQUENCE_CHECKER(sequence_checker_);
 
   State state_ = State::kWaitingForTouch;
   raw_ptr<FidoAuthenticator> authenticator_ = nullptr;
-  absl::optional<pin::TokenResponse> pin_token_;
+  std::optional<pin::TokenResponse> pin_token_;
 
   ReadyCallback ready_callback_;
   GetPINCallback get_pin_callback_;
diff --git a/device/fido/credential_management_handler_unittest.cc b/device/fido/credential_management_handler_unittest.cc
index fc84289..f7393bb5f 100644
--- a/device/fido/credential_management_handler_unittest.cc
+++ b/device/fido/credential_management_handler_unittest.cc
@@ -62,8 +62,8 @@
   test::TestCallbackReceiver<> ready_callback_;
   test::StatusAndValuesCallbackReceiver<
       CtapDeviceResponseCode,
-      absl::optional<std::vector<AggregatedEnumerateCredentialsResponse>>,
-      absl::optional<size_t>>
+      std::optional<std::vector<AggregatedEnumerateCredentialsResponse>>,
+      std::optional<size_t>>
       get_credentials_callback_;
   test::ValueCallbackReceiver<CtapDeviceResponseCode> delete_callback_;
   test::ValueCallbackReceiver<CtapDeviceResponseCode>
diff --git a/device/fido/cros/authenticator.cc b/device/fido/cros/authenticator.cc
index 198fb8c1..b35f558 100644
--- a/device/fido/cros/authenticator.cc
+++ b/device/fido/cros/authenticator.cc
@@ -72,7 +72,7 @@
   return *options;
 }
 
-absl::optional<FidoTransportProtocol>
+std::optional<FidoTransportProtocol>
 ChromeOSAuthenticator::AuthenticatorTransport() const {
   return FidoTransportProtocol::kInternal;
 }
@@ -93,7 +93,7 @@
 
 void ChromeOSAuthenticator::OnGetAlgorithmsResponse(
     base::OnceClosure callback,
-    absl::optional<u2f::GetAlgorithmsResponse> response) {
+    std::optional<u2f::GetAlgorithmsResponse> response) {
   if (response && response->status() ==
                       u2f::GetAlgorithmsResponse_GetAlgorithmsStatus_SUCCESS) {
     supported_algorithms_ = std::vector<int32_t>();
@@ -104,7 +104,7 @@
     // Keep `supported_algorithms_` as nullopt if fetching supported algorithms
     // from u2fd failed, since the caller of `GetAlgorithms` method might want
     // to provide defaults.
-    supported_algorithms_ = absl::nullopt;
+    supported_algorithms_ = std::nullopt;
   }
 
   std::move(callback).Run();
@@ -117,12 +117,12 @@
   std::move(callback).Run();
 }
 
-absl::optional<base::span<const int32_t>>
+std::optional<base::span<const int32_t>>
 ChromeOSAuthenticator::GetAlgorithms() {
   if (supported_algorithms_) {
     return base::span<const int32_t>(*supported_algorithms_);
   }
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 void ChromeOSAuthenticator::MakeCredential(
@@ -196,11 +196,11 @@
 void ChromeOSAuthenticator::OnMakeCredentialResponse(
     CtapMakeCredentialRequest request,
     MakeCredentialCallback callback,
-    absl::optional<u2f::MakeCredentialResponse> response) {
+    std::optional<u2f::MakeCredentialResponse> response) {
   if (!response) {
     FIDO_LOG(ERROR) << "MakeCredential dbus call failed";
     std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrOther,
-                            absl::nullopt);
+                            std::nullopt);
     return;
   }
 
@@ -208,27 +208,27 @@
   if (response->status() !=
       u2f::MakeCredentialResponse_MakeCredentialStatus_SUCCESS) {
     std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrOperationDenied,
-                            absl::nullopt);
+                            std::nullopt);
     return;
   }
 
-  absl::optional<AuthenticatorData> authenticator_data =
+  std::optional<AuthenticatorData> authenticator_data =
       AuthenticatorData::DecodeAuthenticatorData(
           base::as_bytes(base::make_span(response->authenticator_data())));
   if (!authenticator_data) {
     FIDO_LOG(ERROR) << "Authenticator data corrupted.";
     std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrOther,
-                            absl::nullopt);
+                            std::nullopt);
     return;
   }
 
-  absl::optional<cbor::Value> statement_map = cbor::Reader::Read(
+  std::optional<cbor::Value> statement_map = cbor::Reader::Read(
       base::as_bytes(base::make_span(response->attestation_statement())));
   if (!statement_map ||
       statement_map.value().type() != cbor::Value::Type::MAP) {
     FIDO_LOG(ERROR) << "Attestation statement is not a CBOR map.";
     std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrOther,
-                            absl::nullopt);
+                            std::nullopt);
     return;
   }
   auto statement = std::make_unique<OpaqueAttestationStatement>(
@@ -301,7 +301,7 @@
 
 void ChromeOSAuthenticator::OnHasCredentialInformationForRequest(
     GetPlatformCredentialInfoForRequestCallback callback,
-    absl::optional<u2f::HasCredentialsResponse> response) {
+    std::optional<u2f::HasCredentialsResponse> response) {
   std::move(callback).Run(
       /*credentials=*/{},
       response &&
@@ -317,7 +317,7 @@
 void ChromeOSAuthenticator::OnGetAssertionResponse(
     CtapGetAssertionRequest request,
     GetAssertionCallback callback,
-    absl::optional<u2f::GetAssertionResponse> response) {
+    std::optional<u2f::GetAssertionResponse> response) {
   if (!response) {
     FIDO_LOG(ERROR) << "GetAssertion dbus call failed";
     std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrOther, {});
@@ -335,7 +335,7 @@
 
   u2f::Assertion assertion = response->assertion(0);
 
-  absl::optional<AuthenticatorData> authenticator_data =
+  std::optional<AuthenticatorData> authenticator_data =
       AuthenticatorData::DecodeAuthenticatorData(
           base::as_bytes(base::make_span(assertion.authenticator_data())));
   if (!authenticator_data) {
@@ -376,7 +376,7 @@
       req,
       base::BindOnce(
           [](base::OnceCallback<void(bool has_credential)> callback,
-             absl::optional<u2f::HasCredentialsResponse> response) {
+             std::optional<u2f::HasCredentialsResponse> response) {
             std::move(callback).Run(
                 response &&
                 response->status() ==
@@ -399,7 +399,7 @@
 }
 
 void ChromeOSAuthenticator::OnCancelResponse(
-    absl::optional<u2f::CancelWebAuthnFlowResponse> response) {
+    std::optional<u2f::CancelWebAuthnFlowResponse> response) {
   current_request_id_.clear();
 
   if (!response) {
@@ -427,7 +427,7 @@
             u2f::IsUvpaaRequest(),
             base::BindOnce(
                 [](base::OnceCallback<void(bool is_available)> callback,
-                   absl::optional<u2f::IsUvpaaResponse> response) {
+                   std::optional<u2f::IsUvpaaResponse> response) {
                   std::move(callback).Run(response &&
                                           !response->not_available());
                 },
@@ -442,7 +442,7 @@
       u2f::IsU2fEnabledRequest(),
       base::BindOnce(
           [](base::OnceCallback<void(bool is_enabled)> callback,
-             absl::optional<u2f::IsU2fEnabledResponse> response) {
+             std::optional<u2f::IsU2fEnabledResponse> response) {
             std::move(callback).Run(response && response->enabled());
           },
           std::move(callback)));
@@ -454,7 +454,7 @@
       u2f::GetSupportedFeaturesRequest(),
       base::BindOnce(
           [](base::OnceCallback<void(bool is_enabled)> callback,
-             absl::optional<u2f::GetSupportedFeaturesResponse> response) {
+             std::optional<u2f::GetSupportedFeaturesResponse> response) {
             std::move(callback).Run(response && response->support_lacros());
           },
           std::move(callback)));
diff --git a/device/fido/cros/authenticator.h b/device/fido/cros/authenticator.h
index 4562a04..349494f 100644
--- a/device/fido/cros/authenticator.h
+++ b/device/fido/cros/authenticator.h
@@ -5,6 +5,7 @@
 #ifndef DEVICE_FIDO_CROS_AUTHENTICATOR_H_
 #define DEVICE_FIDO_CROS_AUTHENTICATOR_H_
 
+#include <optional>
 #include <string>
 
 #include "base/component_export.h"
@@ -19,7 +20,6 @@
 #include "device/fido/ctap_make_credential_request.h"
 #include "device/fido/fido_authenticator.h"
 #include "device/fido/fido_transport_protocol.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -68,7 +68,7 @@
 
   // Since this method is synchronous, it will simply return the GetAlgorithms
   // result obtained during `InitializeAuthenticator`.
-  absl::optional<base::span<const int32_t>> GetAlgorithms() override;
+  std::optional<base::span<const int32_t>> GetAlgorithms() override;
 
   void MakeCredential(CtapMakeCredentialRequest request,
                       MakeCredentialOptions request_options,
@@ -85,7 +85,7 @@
   std::string GetId() const override;
   const AuthenticatorSupportedOptions& Options() const override;
 
-  absl::optional<FidoTransportProtocol> AuthenticatorTransport() const override;
+  std::optional<FidoTransportProtocol> AuthenticatorTransport() const override;
 
   void GetTouch(base::OnceClosure callback) override {}
   base::WeakPtr<FidoAuthenticator> GetWeakPtr() override;
@@ -95,23 +95,23 @@
   // of `InitializeAuthenticator`.
   void OnGetAlgorithmsResponse(
       base::OnceClosure callback,
-      absl::optional<u2f::GetAlgorithmsResponse> response);
+      std::optional<u2f::GetAlgorithmsResponse> response);
   // Cache whether power button is enabled, and run the barrier callback
   // of `InitializeAuthenticator`.
   void OnIsPowerButtonModeEnabled(base::OnceClosure callback, bool enabled);
   void OnHasCredentialInformationForRequest(
       GetPlatformCredentialInfoForRequestCallback callback,
-      absl::optional<u2f::HasCredentialsResponse> response);
+      std::optional<u2f::HasCredentialsResponse> response);
   void OnMakeCredentialResponse(
       CtapMakeCredentialRequest request,
       MakeCredentialCallback callback,
-      absl::optional<u2f::MakeCredentialResponse> response);
+      std::optional<u2f::MakeCredentialResponse> response);
   void OnGetAssertionResponse(
       CtapGetAssertionRequest request,
       GetAssertionCallback callback,
-      absl::optional<u2f::GetAssertionResponse> response);
+      std::optional<u2f::GetAssertionResponse> response);
   void OnCancelResponse(
-      absl::optional<u2f::CancelWebAuthnFlowResponse> response);
+      std::optional<u2f::CancelWebAuthnFlowResponse> response);
 
   // Current request_id, used for cancelling the request.
   std::string current_request_id_;
@@ -119,7 +119,7 @@
   // Callback to set request_id in the window property.
   base::RepeatingCallback<std::string()> generate_request_id_callback_;
   const Config config_;
-  absl::optional<std::vector<int32_t>> supported_algorithms_;
+  std::optional<std::vector<int32_t>> supported_algorithms_;
   bool u2f_enabled_ = false;
   base::WeakPtrFactory<ChromeOSAuthenticator> weak_factory_;
 };
diff --git a/device/fido/cros/credential_store.cc b/device/fido/cros/credential_store.cc
index 20d0de7..18cadde 100644
--- a/device/fido/cros/credential_store.cc
+++ b/device/fido/cros/credential_store.cc
@@ -81,7 +81,7 @@
 
 void PlatformAuthenticatorCredentialStore::OnDeleteCredentialsFinished(
     base::OnceClosure callback,
-    absl::optional<u2f::DeleteCredentialsInTimeRangeResponse> response) {
+    std::optional<u2f::DeleteCredentialsInTimeRangeResponse> response) {
   if (!response) {
     FIDO_LOG(ERROR) << "DeleteCredentialsInTimeRange dbus call failed";
   } else if (
@@ -96,7 +96,7 @@
 
 void PlatformAuthenticatorCredentialStore::OnCountCredentialsFinished(
     base::OnceCallback<void(size_t)> callback,
-    absl::optional<u2f::CountCredentialsInTimeRangeResponse> response) {
+    std::optional<u2f::CountCredentialsInTimeRangeResponse> response) {
   if (!response) {
     FIDO_LOG(ERROR) << "CountCredentialsInTimeRange dbus call failed";
     std::move(callback).Run(0);
diff --git a/device/fido/cros/credential_store.h b/device/fido/cros/credential_store.h
index b94cff4..af2331b 100644
--- a/device/fido/cros/credential_store.h
+++ b/device/fido/cros/credential_store.h
@@ -5,11 +5,12 @@
 #ifndef DEVICE_FIDO_CROS_CREDENTIAL_STORE_H_
 #define DEVICE_FIDO_CROS_CREDENTIAL_STORE_H_
 
+#include <optional>
+
 #include "base/component_export.h"
 #include "base/functional/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "device/fido/platform_credential_store.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace u2f {
 class DeleteCredentialsInTimeRangeResponse;
@@ -56,10 +57,10 @@
 
   void OnDeleteCredentialsFinished(
       base::OnceClosure callback,
-      absl::optional<u2f::DeleteCredentialsInTimeRangeResponse> response);
+      std::optional<u2f::DeleteCredentialsInTimeRangeResponse> response);
   void OnCountCredentialsFinished(
       base::OnceCallback<void(size_t)> callback,
-      absl::optional<u2f::CountCredentialsInTimeRangeResponse> response);
+      std::optional<u2f::CountCredentialsInTimeRangeResponse> response);
 
   base::WeakPtrFactory<PlatformAuthenticatorCredentialStore> weak_factory_{
       this};
diff --git a/device/fido/cros/discovery.cc b/device/fido/cros/discovery.cc
index 83433c9..527e5f5 100644
--- a/device/fido/cros/discovery.cc
+++ b/device/fido/cros/discovery.cc
@@ -16,7 +16,7 @@
 
 FidoChromeOSDiscovery::FidoChromeOSDiscovery(
     base::RepeatingCallback<std::string()> generate_request_id_callback,
-    absl::optional<CtapGetAssertionRequest> get_assertion_request)
+    std::optional<CtapGetAssertionRequest> get_assertion_request)
     : FidoDiscoveryBase(FidoTransportProtocol::kInternal),
       generate_request_id_callback_(generate_request_id_callback),
       get_assertion_request_(std::move(get_assertion_request)),
diff --git a/device/fido/cros/discovery.h b/device/fido/cros/discovery.h
index aea6645..3f9ac127 100644
--- a/device/fido/cros/discovery.h
+++ b/device/fido/cros/discovery.h
@@ -6,6 +6,7 @@
 #define DEVICE_FIDO_CROS_DISCOVERY_H_
 
 #include <memory>
+#include <optional>
 #include <string>
 
 #include "base/component_export.h"
@@ -15,7 +16,6 @@
 #include "device/fido/cros/authenticator.h"
 #include "device/fido/ctap_get_assertion_request.h"
 #include "device/fido/fido_discovery_base.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -24,7 +24,7 @@
  public:
   FidoChromeOSDiscovery(
       base::RepeatingCallback<std::string()> generate_request_id_callback,
-      absl::optional<CtapGetAssertionRequest> get_assertion_request_);
+      std::optional<CtapGetAssertionRequest> get_assertion_request_);
   ~FidoChromeOSDiscovery() override;
 
   void set_require_power_button_mode(bool require);
@@ -46,7 +46,7 @@
   bool uv_available_ = false;
   bool lacros_supported_ = false;
   uint32_t pending_requests_ = 0;
-  absl::optional<CtapGetAssertionRequest> get_assertion_request_;
+  std::optional<CtapGetAssertionRequest> get_assertion_request_;
   std::unique_ptr<ChromeOSAuthenticator> authenticator_;
   base::WeakPtrFactory<FidoChromeOSDiscovery> weak_factory_;
 };
diff --git a/device/fido/ctap2_device_operation.h b/device/fido/ctap2_device_operation.h
index 1e32a37..e583ec4 100644
--- a/device/fido/ctap2_device_operation.h
+++ b/device/fido/ctap2_device_operation.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -25,7 +26,6 @@
 #include "device/fido/device_response_converter.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_device.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -41,13 +41,12 @@
   // object, or else is called with a value other than |kSuccess| and
   // |nullopt|.
   using DeviceResponseCallback =
-      base::OnceCallback<void(CtapDeviceResponseCode,
-                              absl::optional<Response>)>;
+      base::OnceCallback<void(CtapDeviceResponseCode, std::optional<Response>)>;
   // DeviceResponseParser converts a generic CBOR structure into an
   // operation-specific response. If the response didn't have a payload then the
   // argument will be |nullopt|. The parser should return |nullopt| on error.
-  using DeviceResponseParser = base::OnceCallback<absl::optional<Response>(
-      const absl::optional<cbor::Value>&)>;
+  using DeviceResponseParser = base::OnceCallback<std::optional<Response>(
+      const std::optional<cbor::Value>&)>;
   // CBORPathPredicate takes a vector of CBOR |Value|s that are map keys and
   // returns true if the string at that location may validly be truncated.
   // For example, the path of the string "bar" in {"x": {"y": "foo",
@@ -76,7 +75,7 @@
   ~Ctap2DeviceOperation() override = default;
 
   void Start() override {
-    std::pair<CtapRequestCommand, absl::optional<cbor::Value>> request(
+    std::pair<CtapRequestCommand, std::optional<cbor::Value>> request(
         AsCTAPRequestValuePair(this->request()));
     std::vector<uint8_t> request_bytes;
 
@@ -86,7 +85,7 @@
     if (request.second) {
       FIDO_LOG(DEBUG) << "<- " << request.first << " "
                       << cbor::DiagnosticWriter::Write(*request.second);
-      absl::optional<std::vector<uint8_t>> cbor_bytes =
+      std::optional<std::vector<uint8_t>> cbor_bytes =
           cbor::Writer::Write(*request.second);
       DCHECK(cbor_bytes);
       request_bytes = std::move(*cbor_bytes);
@@ -116,8 +115,7 @@
     }
   }
 
-  void OnResponseReceived(
-      absl::optional<std::vector<uint8_t>> device_response) {
+  void OnResponseReceived(std::optional<std::vector<uint8_t>> device_response) {
     this->token_.reset();
 
     // TODO: it would be nice to see which device each response is coming from,
@@ -126,7 +124,7 @@
     if (!device_response || device_response->empty()) {
       FIDO_LOG(ERROR) << "-> (error reading)";
       std::move(this->callback())
-          .Run(CtapDeviceResponseCode::kCtap2ErrOther, absl::nullopt);
+          .Run(CtapDeviceResponseCode::kCtap2ErrOther, std::nullopt);
       return;
     }
 
@@ -138,13 +136,13 @@
       } else {
         FIDO_LOG(DEBUG) << "-> (CTAP2 error code " << response_code << ")";
       }
-      std::move(this->callback()).Run(response_code, absl::nullopt);
+      std::move(this->callback()).Run(response_code, std::nullopt);
       return;
     }
     DCHECK(!device_response->empty());
 
-    absl::optional<cbor::Value> cbor;
-    absl::optional<Response> response;
+    std::optional<cbor::Value> cbor;
+    std::optional<Response> response;
     base::span<const uint8_t> cbor_bytes(*device_response);
     cbor_bytes = cbor_bytes.subspan(1);
 
@@ -163,7 +161,7 @@
                         << "' from raw message "
                         << base::HexEncode(device_response.value()) << ")";
         std::move(this->callback())
-            .Run(CtapDeviceResponseCode::kCtap2ErrInvalidCBOR, absl::nullopt);
+            .Run(CtapDeviceResponseCode::kCtap2ErrInvalidCBOR, std::nullopt);
         return;
       }
 
@@ -174,7 +172,7 @@
               << "-> (CBOR with unfixable UTF-8 errors from raw message "
               << base::HexEncode(device_response.value()) << ")";
           std::move(this->callback())
-              .Run(CtapDeviceResponseCode::kCtap2ErrInvalidCBOR, absl::nullopt);
+              .Run(CtapDeviceResponseCode::kCtap2ErrInvalidCBOR, std::nullopt);
           return;
         }
       }
@@ -188,7 +186,7 @@
       }
     } else {
       response =
-          std::move(std::move(device_response_parser_).Run(absl::nullopt));
+          std::move(std::move(device_response_parser_).Run(std::nullopt));
       if (response) {
         FIDO_LOG(DEBUG) << "-> (empty payload)";
       } else {
diff --git a/device/fido/ctap_authenticator_selection_request.cc b/device/fido/ctap_authenticator_selection_request.cc
index 5705306ca..7fcceb8 100644
--- a/device/fido/ctap_authenticator_selection_request.cc
+++ b/device/fido/ctap_authenticator_selection_request.cc
@@ -6,10 +6,10 @@
 
 namespace device {
 
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const CtapAuthenticatorSelectionRequest&) {
   return std::make_pair(CtapRequestCommand::kAuthenticatorSelection,
-                        absl::nullopt);
+                        std::nullopt);
 }
 
 }  // namespace device
diff --git a/device/fido/ctap_authenticator_selection_request.h b/device/fido/ctap_authenticator_selection_request.h
index e15fa64f..57dfd37 100644
--- a/device/fido/ctap_authenticator_selection_request.h
+++ b/device/fido/ctap_authenticator_selection_request.h
@@ -5,17 +5,17 @@
 #ifndef DEVICE_FIDO_CTAP_AUTHENTICATOR_SELECTION_REQUEST_H_
 #define DEVICE_FIDO_CTAP_AUTHENTICATOR_SELECTION_REQUEST_H_
 
+#include <optional>
 #include <utility>
 
 #include "components/cbor/values.h"
 #include "device/fido/fido_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
 struct CtapAuthenticatorSelectionRequest {};
 
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const CtapAuthenticatorSelectionRequest&);
 
 }  // namespace device
diff --git a/device/fido/ctap_get_assertion_request.cc b/device/fido/ctap_get_assertion_request.cc
index f4e94f0..9216102f 100644
--- a/device/fido/ctap_get_assertion_request.cc
+++ b/device/fido/ctap_get_assertion_request.cc
@@ -59,7 +59,7 @@
     base::span<const uint8_t, kP256X962Length> in_public_key_x962,
     base::span<const uint8_t> in_encrypted_salts,
     base::span<const uint8_t> in_salts_auth,
-    absl::optional<PINUVAuthProtocol> in_pin_protocol)
+    std::optional<PINUVAuthProtocol> in_pin_protocol)
     : public_key_x962(fido_parsing_utils::Materialize(in_public_key_x962)),
       encrypted_salts(fido_parsing_utils::Materialize(in_encrypted_salts)),
       salts_auth(fido_parsing_utils::Materialize(in_salts_auth)),
@@ -71,22 +71,22 @@
 CtapGetAssertionRequest::HMACSecret::operator=(const HMACSecret&) = default;
 
 // static
-absl::optional<CtapGetAssertionRequest> CtapGetAssertionRequest::Parse(
+std::optional<CtapGetAssertionRequest> CtapGetAssertionRequest::Parse(
     const cbor::Value::MapValue& request_map,
     const ParseOpts& opts) {
   if (!AreGetAssertionRequestMapKeysCorrect(request_map))
-    return absl::nullopt;
+    return std::nullopt;
 
   const auto rp_id_it = request_map.find(cbor::Value(1));
   if (rp_id_it == request_map.end() || !rp_id_it->second.is_string())
-    return absl::nullopt;
+    return std::nullopt;
 
   const auto client_data_hash_it = request_map.find(cbor::Value(2));
   if (client_data_hash_it == request_map.end() ||
       !client_data_hash_it->second.is_bytestring() ||
       client_data_hash_it->second.GetBytestring().size() !=
           kClientDataHashLength) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   base::span<const uint8_t, kClientDataHashLength> client_data_hash(
       client_data_hash_it->second.GetBytestring().data(),
@@ -99,11 +99,11 @@
   const auto allow_list_it = request_map.find(cbor::Value(3));
   if (allow_list_it != request_map.end()) {
     if (!allow_list_it->second.is_array())
-      return absl::nullopt;
+      return std::nullopt;
 
     const auto& credential_descriptors = allow_list_it->second.GetArray();
     if (credential_descriptors.empty())
-      return absl::nullopt;
+      return std::nullopt;
 
     std::vector<PublicKeyCredentialDescriptor> allow_list;
     for (const auto& credential_descriptor : credential_descriptors) {
@@ -111,7 +111,7 @@
           PublicKeyCredentialDescriptor::CreateFromCBORValue(
               credential_descriptor);
       if (!allowed_credential)
-        return absl::nullopt;
+        return std::nullopt;
 
       allow_list.push_back(std::move(*allowed_credential));
     }
@@ -121,40 +121,40 @@
   const auto extensions_it = request_map.find(cbor::Value(4));
   if (extensions_it != request_map.end()) {
     if (!extensions_it->second.is_map()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     const cbor::Value::MapValue& extensions = extensions_it->second.GetMap();
     if (opts.reject_all_extensions && !extensions.empty()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     for (const auto& extension : extensions) {
       if (!extension.first.is_string()) {
-        return absl::nullopt;
+        return std::nullopt;
       }
 
       const std::string& extension_id = extension.first.GetString();
       if (extension_id == kExtensionHmacSecret) {
         if (!extension.second.is_map()) {
-          return absl::nullopt;
+          return std::nullopt;
         }
         const auto& hmac_extension = extension.second.GetMap();
 
         auto hmac_it = hmac_extension.find(cbor::Value(1));
         if (hmac_it == hmac_extension.end() || !hmac_it->second.is_map()) {
-          return absl::nullopt;
+          return std::nullopt;
         }
-        const absl::optional<pin::KeyAgreementResponse> key(
+        const std::optional<pin::KeyAgreementResponse> key(
             pin::KeyAgreementResponse::ParseFromCOSE(hmac_it->second.GetMap()));
         if (!key) {
-          return absl::nullopt;
+          return std::nullopt;
         }
 
         hmac_it = hmac_extension.find(cbor::Value(2));
         if (hmac_it == hmac_extension.end() ||
             !hmac_it->second.is_bytestring()) {
-          return absl::nullopt;
+          return std::nullopt;
         }
         const std::vector<uint8_t>& encrypted_salts =
             hmac_it->second.GetBytestring();
@@ -162,23 +162,23 @@
         hmac_it = hmac_extension.find(cbor::Value(3));
         if (hmac_it == hmac_extension.end() ||
             !hmac_it->second.is_bytestring()) {
-          return absl::nullopt;
+          return std::nullopt;
         }
         const std::vector<uint8_t>& salts_auth =
             hmac_it->second.GetBytestring();
 
-        absl::optional<PINUVAuthProtocol> pin_protocol;
+        std::optional<PINUVAuthProtocol> pin_protocol;
         const auto pin_protocol_it = hmac_extension.find(cbor::Value(4));
         if (pin_protocol_it != hmac_extension.end()) {
           if (!pin_protocol_it->second.is_unsigned() ||
               pin_protocol_it->second.GetUnsigned() >
                   std::numeric_limits<uint8_t>::max()) {
-            return absl::nullopt;
+            return std::nullopt;
           }
           pin_protocol =
               ToPINUVAuthProtocol(pin_protocol_it->second.GetUnsigned());
           if (!pin_protocol) {
-            return absl::nullopt;
+            return std::nullopt;
           }
         }
 
@@ -186,24 +186,24 @@
                                     pin_protocol);
       } else if (extension_id == kExtensionLargeBlobKey) {
         if (!extension.second.is_bool() || !extension.second.GetBool()) {
-          return absl::nullopt;
+          return std::nullopt;
         }
         request.large_blob_key = true;
       } else if (extension_id == kExtensionCredBlob) {
         if (!extension.second.is_bool() || !extension.second.GetBool()) {
-          return absl::nullopt;
+          return std::nullopt;
         }
         request.get_cred_blob = true;
       } else if (extension_id == kExtensionPRF) {
         if (!extension.second.is_map()) {
-          return absl::nullopt;
+          return std::nullopt;
         }
         const cbor::Value::MapValue& prf = extension.second.GetMap();
         const auto eval_it = prf.find(cbor::Value(kExtensionPRFEval));
         if (eval_it != prf.end()) {
-          absl::optional<PRFInput> input = PRFInput::FromCBOR(eval_it->second);
+          std::optional<PRFInput> input = PRFInput::FromCBOR(eval_it->second);
           if (!input) {
-            return absl::nullopt;
+            return std::nullopt;
           }
           request.prf_inputs.emplace_back(std::move(*input));
         }
@@ -211,17 +211,17 @@
             prf.find(cbor::Value(kExtensionPRFEvalByCredential));
         if (by_cred_it != prf.end()) {
           if (!by_cred_it->second.is_map()) {
-            return absl::nullopt;
+            return std::nullopt;
           }
           const cbor::Value::MapValue& by_cred = by_cred_it->second.GetMap();
           for (const auto& cred : by_cred) {
-            absl::optional<PRFInput> input = PRFInput::FromCBOR(cred.second);
+            std::optional<PRFInput> input = PRFInput::FromCBOR(cred.second);
             if (!input || !cred.first.is_bytestring()) {
-              return absl::nullopt;
+              return std::nullopt;
             }
             input->credential_id = cred.first.GetBytestring();
             if (input->credential_id->empty()) {
-              return absl::nullopt;
+              return std::nullopt;
             }
             request.prf_inputs.emplace_back(std::move(*input));
           }
@@ -229,7 +229,7 @@
         std::sort(request.prf_inputs.begin(), request.prf_inputs.end());
       } else if (extension_id == kExtensionLargeBlob) {
         if (!extension.second.is_map()) {
-          return absl::nullopt;
+          return std::nullopt;
         }
         const cbor::Value::MapValue& large_blob_ext = extension.second.GetMap();
         const auto read_it =
@@ -247,7 +247,7 @@
         if ((has_read && !read_it->second.is_bool()) ||
             (has_write && !write_it->second.is_bytestring()) ||
             (has_original_size && !original_size_it->second.is_unsigned())) {
-          return absl::nullopt;
+          return std::nullopt;
         }
 
         if (has_read && !has_write && !has_original_size) {
@@ -259,7 +259,7 @@
                   original_size_it->second.GetUnsigned()));
         } else {
           // No other combinations of keys are acceptable.
-          return absl::nullopt;
+          return std::nullopt;
         }
       }
     }
@@ -268,12 +268,12 @@
   const auto option_it = request_map.find(cbor::Value(5));
   if (option_it != request_map.end()) {
     if (!option_it->second.is_map()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     const auto& option_map = option_it->second.GetMap();
     if (!IsGetAssertionOptionMapFormatCorrect(option_map)) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     const auto user_presence_option =
@@ -295,7 +295,7 @@
   const auto pin_auth_it = request_map.find(cbor::Value(6));
   if (pin_auth_it != request_map.end()) {
     if (!pin_auth_it->second.is_bytestring())
-      return absl::nullopt;
+      return std::nullopt;
 
     request.pin_auth = pin_auth_it->second.GetBytestring();
   }
@@ -305,12 +305,12 @@
     if (!pin_protocol_it->second.is_unsigned() ||
         pin_protocol_it->second.GetUnsigned() >
             std::numeric_limits<uint8_t>::max()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
-    absl::optional<PINUVAuthProtocol> pin_protocol =
+    std::optional<PINUVAuthProtocol> pin_protocol =
         ToPINUVAuthProtocol(pin_protocol_it->second.GetUnsigned());
     if (!pin_protocol) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     request.pin_protocol = *pin_protocol;
   }
@@ -340,7 +340,7 @@
 
 CtapGetAssertionRequest::~CtapGetAssertionRequest() = default;
 
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const CtapGetAssertionRequest& request) {
   cbor::Value::MapValue cbor_map;
   cbor_map[cbor::Value(1)] = cbor::Value(request.rp_id);
@@ -450,10 +450,10 @@
                         cbor::Value(std::move(cbor_map)));
 }
 
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const CtapGetNextAssertionRequest&) {
   return std::make_pair(CtapRequestCommand::kAuthenticatorGetNextAssertion,
-                        absl::nullopt);
+                        std::nullopt);
 }
 
 }  // namespace device
diff --git a/device/fido/ctap_get_assertion_request.h b/device/fido/ctap_get_assertion_request.h
index e43d64e..c135c7c 100644
--- a/device/fido/ctap_get_assertion_request.h
+++ b/device/fido/ctap_get_assertion_request.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <array>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -21,7 +22,6 @@
 #include "device/fido/pin.h"
 #include "device/fido/prf_input.h"
 #include "device/fido/public_key_credential_descriptor.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace cbor {
 class Value;
@@ -44,10 +44,10 @@
 
   // The PUAT used for the request. The caller is expected to set this if needed
   // with the correct permissions. Obtain from |FidoAuthenticator::GetPINToken|.
-  absl::optional<pin::TokenResponse> pin_uv_auth_token;
+  std::optional<pin::TokenResponse> pin_uv_auth_token;
 
   // The ephemeral key use to encrypt PIN material.
-  absl::optional<pin::KeyAgreementResponse> pin_key_agreement;
+  std::optional<pin::KeyAgreementResponse> pin_key_agreement;
 
   // prf_inputs may contain a default PRFInput without a |credential_id|. If so,
   // it will be the first element and all others will have |credential_id|s.
@@ -58,7 +58,7 @@
   bool large_blob_read = false;
 
   // If set, attempt to write a large blob.
-  absl::optional<std::vector<uint8_t>> large_blob_write;
+  std::optional<std::vector<uint8_t>> large_blob_write;
 
   // Indicates whether the request was created in an off-the-record
   // BrowserContext (e.g. Chrome Incognito mode).
@@ -84,7 +84,7 @@
     HMACSecret(base::span<const uint8_t, kP256X962Length> public_key_x962,
                base::span<const uint8_t> encrypted_salts,
                base::span<const uint8_t> salts_auth,
-               absl::optional<PINUVAuthProtocol> pin_protocol);
+               std::optional<PINUVAuthProtocol> pin_protocol);
     HMACSecret(const HMACSecret&);
     ~HMACSecret();
     HMACSecret& operator=(const HMACSecret&);
@@ -94,18 +94,18 @@
     std::vector<uint8_t> salts_auth;
     // pin_protocol is ignored during serialisation and the request's PIN
     // protocol will be used instead.
-    absl::optional<PINUVAuthProtocol> pin_protocol;
+    std::optional<PINUVAuthProtocol> pin_protocol;
   };
 
   // Decodes a CTAP2 authenticatorGetAssertion request message. The request's
   // |client_data_json| will be empty and |client_data_hash| will be set.
   //
   // A |uv| bit of 0 is mapped to UserVerificationRequirement::kDiscouraged.
-  static absl::optional<CtapGetAssertionRequest> Parse(
+  static std::optional<CtapGetAssertionRequest> Parse(
       const cbor::Value::MapValue& request_map) {
     return Parse(request_map, ParseOpts());
   }
-  static absl::optional<CtapGetAssertionRequest> Parse(
+  static std::optional<CtapGetAssertionRequest> Parse(
       const cbor::Value::MapValue& request_map,
       const ParseOpts& opts);
 
@@ -124,13 +124,13 @@
   bool user_presence_required = true;
 
   std::vector<PublicKeyCredentialDescriptor> allow_list;
-  absl::optional<std::vector<uint8_t>> pin_auth;
-  absl::optional<PINUVAuthProtocol> pin_protocol;
-  absl::optional<std::vector<CableDiscoveryData>> cable_extension;
-  absl::optional<std::string> app_id;
-  absl::optional<std::array<uint8_t, crypto::kSHA256Length>>
+  std::optional<std::vector<uint8_t>> pin_auth;
+  std::optional<PINUVAuthProtocol> pin_protocol;
+  std::optional<std::vector<CableDiscoveryData>> cable_extension;
+  std::optional<std::string> app_id;
+  std::optional<std::array<uint8_t, crypto::kSHA256Length>>
       alternative_application_parameter;
-  absl::optional<HMACSecret> hmac_secret;
+  std::optional<HMACSecret> hmac_secret;
   bool large_blob_key = false;
   bool get_cred_blob = false;
 
@@ -148,7 +148,7 @@
   // using the largeBlob extension that includes largeBlob data directly
   // in getAssertion requests.
   bool large_blob_extension_read = false;
-  absl::optional<LargeBlob> large_blob_extension_write;
+  std::optional<LargeBlob> large_blob_extension_write;
 };
 
 struct CtapGetNextAssertionRequest {};
@@ -157,11 +157,11 @@
 // integer keys and CBOR encoded values as defined by the CTAP spec.
 // https://drafts.fidoalliance.org/fido-2/latest/fido-client-to-authenticator-protocol-v2.0-wd-20180305.html#authenticatorGetAssertion
 COMPONENT_EXPORT(DEVICE_FIDO)
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const CtapGetAssertionRequest&);
 
 COMPONENT_EXPORT(DEVICE_FIDO)
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const CtapGetNextAssertionRequest&);
 
 }  // namespace device
diff --git a/device/fido/ctap_make_credential_request.cc b/device/fido/ctap_make_credential_request.cc
index 79fc4b04..9049da65 100644
--- a/device/fido/ctap_make_credential_request.cc
+++ b/device/fido/ctap_make_credential_request.cc
@@ -40,18 +40,18 @@
 }  // namespace
 
 // static
-absl::optional<CtapMakeCredentialRequest> CtapMakeCredentialRequest::Parse(
+std::optional<CtapMakeCredentialRequest> CtapMakeCredentialRequest::Parse(
     const cbor::Value::MapValue& request_map,
     const ParseOpts& opts) {
   if (!AreMakeCredentialRequestMapKeysCorrect(request_map))
-    return absl::nullopt;
+    return std::nullopt;
 
   const auto client_data_hash_it = request_map.find(cbor::Value(1));
   if (client_data_hash_it == request_map.end() ||
       !client_data_hash_it->second.is_bytestring() ||
       client_data_hash_it->second.GetBytestring().size() !=
           kClientDataHashLength) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   base::span<const uint8_t, kClientDataHashLength> client_data_hash(
       client_data_hash_it->second.GetBytestring().data(),
@@ -59,30 +59,30 @@
 
   const auto rp_entity_it = request_map.find(cbor::Value(2));
   if (rp_entity_it == request_map.end() || !rp_entity_it->second.is_map())
-    return absl::nullopt;
+    return std::nullopt;
 
   auto rp_entity =
       PublicKeyCredentialRpEntity::CreateFromCBORValue(rp_entity_it->second);
   if (!rp_entity)
-    return absl::nullopt;
+    return std::nullopt;
 
   const auto user_entity_it = request_map.find(cbor::Value(3));
   if (user_entity_it == request_map.end() || !user_entity_it->second.is_map())
-    return absl::nullopt;
+    return std::nullopt;
 
   auto user_entity = PublicKeyCredentialUserEntity::CreateFromCBORValue(
       user_entity_it->second);
   if (!user_entity)
-    return absl::nullopt;
+    return std::nullopt;
 
   const auto credential_params_it = request_map.find(cbor::Value(4));
   if (credential_params_it == request_map.end())
-    return absl::nullopt;
+    return std::nullopt;
 
   auto credential_params = PublicKeyCredentialParams::CreateFromCBORValue(
       credential_params_it->second);
   if (!credential_params)
-    return absl::nullopt;
+    return std::nullopt;
 
   CtapMakeCredentialRequest request(
       /*client_data_json=*/std::string(), std::move(*rp_entity),
@@ -92,7 +92,7 @@
   const auto exclude_list_it = request_map.find(cbor::Value(5));
   if (exclude_list_it != request_map.end()) {
     if (!exclude_list_it->second.is_array())
-      return absl::nullopt;
+      return std::nullopt;
 
     const auto& credential_descriptors = exclude_list_it->second.GetArray();
     std::vector<PublicKeyCredentialDescriptor> exclude_list;
@@ -101,7 +101,7 @@
           PublicKeyCredentialDescriptor::CreateFromCBORValue(
               credential_descriptor);
       if (!excluded_credential)
-        return absl::nullopt;
+        return std::nullopt;
 
       exclude_list.push_back(std::move(*excluded_credential));
     }
@@ -111,7 +111,7 @@
   const auto enterprise_attestation_it = request_map.find(cbor::Value(10));
   if (enterprise_attestation_it != request_map.end()) {
     if (!enterprise_attestation_it->second.is_unsigned()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     switch (enterprise_attestation_it->second.GetUnsigned()) {
       case 1:
@@ -123,32 +123,32 @@
             AttestationConveyancePreference::kEnterpriseApprovedByBrowser;
         break;
       default:
-        return absl::nullopt;
+        return std::nullopt;
     }
   }
 
   const auto extensions_it = request_map.find(cbor::Value(6));
   if (extensions_it != request_map.end()) {
     if (!extensions_it->second.is_map()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     const cbor::Value::MapValue& extensions = extensions_it->second.GetMap();
 
     if (opts.reject_all_extensions && !extensions.empty()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     for (const auto& extension : extensions) {
       if (!extension.first.is_string()) {
-        return absl::nullopt;
+        return std::nullopt;
       }
 
       const std::string& extension_name = extension.first.GetString();
 
       if (extension_name == kExtensionCredProtect) {
         if (!extension.second.is_unsigned()) {
-          return absl::nullopt;
+          return std::nullopt;
         }
         switch (extension.second.GetUnsigned()) {
           case 1:
@@ -161,41 +161,41 @@
             request.cred_protect = device::CredProtect::kUVRequired;
             break;
           default:
-            return absl::nullopt;
+            return std::nullopt;
         }
       } else if (extension_name == kExtensionHmacSecret) {
         if (!extension.second.is_bool()) {
-          return absl::nullopt;
+          return std::nullopt;
         }
         request.hmac_secret = extension.second.GetBool();
       } else if (extension_name == kExtensionPRF) {
         if (!extension.second.is_map()) {
-          return absl::nullopt;
+          return std::nullopt;
         }
         const cbor::Value::MapValue& prf = extension.second.GetMap();
         const auto eval_it = prf.find(cbor::Value(kExtensionPRFEval));
         if (eval_it != prf.end()) {
           request.prf_input = PRFInput::FromCBOR(eval_it->second);
           if (!request.prf_input) {
-            return absl::nullopt;
+            return std::nullopt;
           }
         }
         request.prf = true;
       } else if (extension_name == kExtensionLargeBlobKey) {
         if (!extension.second.is_bool() || !extension.second.GetBool()) {
-          return absl::nullopt;
+          return std::nullopt;
         }
         request.large_blob_key = true;
       } else if (extension_name == kExtensionLargeBlob) {
         if (!extension.second.is_map()) {
-          return absl::nullopt;
+          return std::nullopt;
         }
         const cbor::Value::MapValue& large_blob_ext = extension.second.GetMap();
         const auto support_it =
             large_blob_ext.find(cbor::Value(kExtensionLargeBlobSupport));
         if (support_it != large_blob_ext.end()) {
           if (!support_it->second.is_string()) {
-            return absl::nullopt;
+            return std::nullopt;
           }
           const std::string& support = support_it->second.GetString();
           if (support == kExtensionLargeBlobSupportRequired) {
@@ -203,17 +203,17 @@
           } else if (support == kExtensionLargeBlobSupportPreferred) {
             request.large_blob_support = LargeBlobSupport::kPreferred;
           } else {
-            return absl::nullopt;
+            return std::nullopt;
           }
         }
       } else if (extension_name == kExtensionCredBlob) {
         if (!extension.second.is_bytestring()) {
-          return absl::nullopt;
+          return std::nullopt;
         }
         request.cred_blob = extension.second.GetBytestring();
       } else if (extension_name == kExtensionMinPINLength) {
         if (!extension.second.is_bool()) {
-          return absl::nullopt;
+          return std::nullopt;
         }
         request.min_pin_length_requested = extension.second.GetBool();
       }
@@ -223,11 +223,11 @@
   const auto option_it = request_map.find(cbor::Value(7));
   if (option_it != request_map.end()) {
     if (!option_it->second.is_map())
-      return absl::nullopt;
+      return std::nullopt;
 
     const auto& option_map = option_it->second.GetMap();
     if (!IsMakeCredentialOptionMapFormatCorrect(option_map))
-      return absl::nullopt;
+      return std::nullopt;
 
     const auto resident_key_option =
         option_map.find(cbor::Value(kResidentKeyMapKey));
@@ -248,7 +248,7 @@
   const auto pin_auth_it = request_map.find(cbor::Value(8));
   if (pin_auth_it != request_map.end()) {
     if (!pin_auth_it->second.is_bytestring())
-      return absl::nullopt;
+      return std::nullopt;
 
     request.pin_auth = pin_auth_it->second.GetBytestring();
   }
@@ -258,12 +258,12 @@
     if (!pin_protocol_it->second.is_unsigned() ||
         pin_protocol_it->second.GetUnsigned() >
             std::numeric_limits<uint8_t>::max()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
-    absl::optional<PINUVAuthProtocol> pin_protocol =
+    std::optional<PINUVAuthProtocol> pin_protocol =
         ToPINUVAuthProtocol(pin_protocol_it->second.GetUnsigned());
     if (!pin_protocol) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     request.pin_protocol = *pin_protocol;
   }
@@ -297,7 +297,7 @@
 
 CtapMakeCredentialRequest::~CtapMakeCredentialRequest() = default;
 
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const CtapMakeCredentialRequest& request) {
   cbor::Value::MapValue cbor_map;
   cbor_map[cbor::Value(1)] = cbor::Value(request.client_data_hash);
diff --git a/device/fido/ctap_make_credential_request.h b/device/fido/ctap_make_credential_request.h
index 2ec8e7e..f2c3f44 100644
--- a/device/fido/ctap_make_credential_request.h
+++ b/device/fido/ctap_make_credential_request.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <array>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -21,7 +22,6 @@
 #include "device/fido/public_key_credential_params.h"
 #include "device/fido/public_key_credential_rp_entity.h"
 #include "device/fido/public_key_credential_user_entity.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace cbor {
 class Value;
@@ -44,11 +44,11 @@
 
   // Decodes a CTAP2 authenticatorMakeCredential request message. The request's
   // |client_data_json| will be empty and |client_data_hash| will be set.
-  static absl::optional<CtapMakeCredentialRequest> Parse(
+  static std::optional<CtapMakeCredentialRequest> Parse(
       const cbor::Value::MapValue& request_map) {
     return Parse(request_map, ParseOpts());
   }
-  static absl::optional<CtapMakeCredentialRequest> Parse(
+  static std::optional<CtapMakeCredentialRequest> Parse(
       const cbor::Value::MapValue& request_map,
       const ParseOpts& opts);
 
@@ -85,7 +85,7 @@
   // prf_input contains the hashed salts for doing a PRF evaluation at
   // credential creation time. This is only possible when the authenticator
   // supports the "prf" extension, i.e. over hybrid CTAP.
-  absl::optional<PRFInput> prf_input;
+  std::optional<PRFInput> prf_input;
 
   // large_blob_support indicates whether support for largeBlobs should be
   // requested using the `largeBlob` extension. This should be mutually
@@ -102,29 +102,29 @@
   // The pinUvAuthParam field. This is the result of calling
   // |pin::TokenResponse::PinAuth(client_data_hash)| with the PIN/UV Auth Token
   // response obtained from the authenticator.
-  absl::optional<std::vector<uint8_t>> pin_auth;
+  std::optional<std::vector<uint8_t>> pin_auth;
 
   // The pinUvAuthProtocol field. It is the version of the PIN/UV Auth Token
   // response obtained from the authenticator.
-  absl::optional<PINUVAuthProtocol> pin_protocol;
+  std::optional<PINUVAuthProtocol> pin_protocol;
 
   // The PIN/UV Auth Token response obtained from the authenticator. This field
   // is only used for computing a fresh pinUvAuthParam for getAssertion requests
   // during silent probing of |exclude_list| credentials. It is ignored when
   // encoding this request to CBOR (|pin_auth| and |pin_protocol| are used for
   // that).
-  absl::optional<pin::TokenResponse> pin_token_for_exclude_list_probing;
+  std::optional<pin::TokenResponse> pin_token_for_exclude_list_probing;
 
   AttestationConveyancePreference attestation_preference =
       AttestationConveyancePreference::kNone;
 
   // U2F AppID for excluding credentials.
-  absl::optional<std::string> app_id_exclude;
+  std::optional<std::string> app_id_exclude;
 
   // cred_protect indicates the level of protection afforded to a credential.
   // This depends on a CTAP2 extension that not all authenticators will support.
   // This is filled out by |MakeCredentialRequestHandler|.
-  absl::optional<CredProtect> cred_protect;
+  std::optional<CredProtect> cred_protect;
 
   // If |cred_protect| is not |nullopt|, this is true if the credProtect level
   // must be provided by the target authenticator for the MakeCredential request
@@ -142,7 +142,7 @@
 
   // cred_blob contains an optional credBlob extension.
   // https://fidoalliance.org/specs/fido-v2.1-rd-20201208/fido-client-to-authenticator-protocol-v2.1-rd-20201208.html#sctn-credBlob-extension
-  absl::optional<std::vector<uint8_t>> cred_blob;
+  std::optional<std::vector<uint8_t>> cred_blob;
 };
 
 // MakeCredentialOptions contains higher-level request parameters that aren't
@@ -180,7 +180,7 @@
   // applies at request-routing time. The second element is true if the
   // indicated protection level must be provided by the target authenticator
   // for the MakeCredential request to be sent.
-  absl::optional<std::pair<CredProtectRequest, bool>> cred_protect_request;
+  std::optional<std::pair<CredProtectRequest, bool>> cred_protect_request;
 
   // allow_skipping_pin_touch causes the handler to forego the first
   // "touch-only" step to collect a PIN if exactly one authenticator is
@@ -204,7 +204,7 @@
 // integer keys and CBOR encoded values as defined by the CTAP spec.
 // https://drafts.fidoalliance.org/fido-2/latest/fido-client-to-authenticator-protocol-v2.0-wd-20180305.html#authenticatorMakeCredential
 COMPONENT_EXPORT(DEVICE_FIDO)
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const CtapMakeCredentialRequest& request);
 
 }  // namespace device
diff --git a/device/fido/ctap_request_unittest.cc b/device/fido/ctap_request_unittest.cc
index 52a4ea2..369c54b 100644
--- a/device/fido/ctap_request_unittest.cc
+++ b/device/fido/ctap_request_unittest.cc
@@ -96,7 +96,7 @@
     if (extra_key) {
       map.emplace("unexpected", "value");
     }
-    const absl::optional<PublicKeyCredentialDescriptor> descriptor(
+    const std::optional<PublicKeyCredentialDescriptor> descriptor(
         PublicKeyCredentialDescriptor::CreateFromCBORValue(
             cbor::Value(std::move(map))));
     ASSERT_TRUE(descriptor);
diff --git a/device/fido/ctap_response_fuzzer.cc b/device/fido/ctap_response_fuzzer.cc
index 323e1e9..c675d56b 100644
--- a/device/fido/ctap_response_fuzzer.cc
+++ b/device/fido/ctap_response_fuzzer.cc
@@ -37,7 +37,7 @@
   cbor::Reader::Config config;
   config.allow_invalid_utf8 = true;
   std::vector<uint8_t> input(data, data + size);
-  absl::optional<cbor::Value> input_cbor = cbor::Reader::Read(input, config);
+  std::optional<cbor::Value> input_cbor = cbor::Reader::Read(input, config);
   if (input_cbor) {
     input_cbor =
         FixInvalidUTF8(std::move(*input_cbor),
diff --git a/device/fido/ctap_response_unittest.cc b/device/fido/ctap_response_unittest.cc
index b5490212..adc81dd1 100644
--- a/device/fido/ctap_response_unittest.cc
+++ b/device/fido/ctap_response_unittest.cc
@@ -441,7 +441,7 @@
 
 // DecodeCBOR parses a CBOR structure, ignoring the first byte of |in|, which is
 // assumed to be a CTAP2 status byte.
-absl::optional<cbor::Value> DecodeCBOR(base::span<const uint8_t> in) {
+std::optional<cbor::Value> DecodeCBOR(base::span<const uint8_t> in) {
   CHECK(!in.empty());
   return cbor::Reader::Read(in.subspan(1));
 }
@@ -643,7 +643,7 @@
 
   EXPECT_THAT(
       AuthenticatorData(test_data::kApplicationParameter, flags,
-                        test_data::kTestSignatureCounter, absl::nullopt)
+                        test_data::kTestSignatureCounter, std::nullopt)
           .SerializeToByteArray(),
       ::testing::ElementsAreArray(test_data::kTestSignAuthenticatorData));
 }
@@ -752,7 +752,7 @@
   uint8_t* first_version = base::ranges::search(get_info, kU2Fv9);
   ASSERT_TRUE(first_version);
   memcpy(first_version, "U2F_V3", 6);
-  absl::optional<AuthenticatorGetInfoResponse> response =
+  std::optional<AuthenticatorGetInfoResponse> response =
       ReadCTAPGetInfoResponse(get_info);
   ASSERT_TRUE(response);
   EXPECT_EQ(1u, response->versions.size());
@@ -858,7 +858,7 @@
           test_data::kCtap2MakeCredentialCredentialId),
       std::make_unique<PublicKey>(
           static_cast<int32_t>(CoseAlgorithmIdentifier::kEs256),
-          kCoseEncodedPublicKey, absl::nullopt));
+          kCoseEncodedPublicKey, std::nullopt));
   AuthenticatorData authenticator_data(application_parameter, flag,
                                        signature_counter,
                                        std::move(attested_credential_data));
@@ -885,7 +885,7 @@
 
 TEST(CTAPResponseTest, AttestationObjectResponseFields) {
   static const std::vector<uint8_t> kInvalidAttestationObject = {1, 2, 3};
-  const absl::optional<AttestationObject::ResponseFields> invalid =
+  const std::optional<AttestationObject::ResponseFields> invalid =
       AttestationObject::ParseForResponseFields(
           kInvalidAttestationObject, /*attestation_acceptable=*/false);
   EXPECT_FALSE(invalid.has_value());
@@ -991,7 +991,7 @@
   };
 
   {
-    const absl::optional<AttestationObject::ResponseFields> fields =
+    const std::optional<AttestationObject::ResponseFields> fields =
         AttestationObject::ParseForResponseFields(
             kAttestationObjectBytes, /*attestation_acceptable=*/true);
     ASSERT_TRUE(fields);
@@ -1006,7 +1006,7 @@
   }
 
   {
-    const absl::optional<AttestationObject::ResponseFields> fields =
+    const std::optional<AttestationObject::ResponseFields> fields =
         AttestationObject::ParseForResponseFields(
             kAttestationObjectBytes, /*attestation_acceptable=*/false);
     ASSERT_TRUE(fields);
diff --git a/device/fido/device_operation.h b/device/fido/device_operation.h
index b917231e..d3fe831 100644
--- a/device/fido/device_operation.h
+++ b/device/fido/device_operation.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include <optional>
 #include <utility>
 #include <vector>
 
@@ -16,7 +17,6 @@
 #include "base/task/sequenced_task_runner.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_device.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -37,8 +37,7 @@
 class DeviceOperation : public GenericDeviceOperation {
  public:
   using DeviceResponseCallback =
-      base::OnceCallback<void(CtapDeviceResponseCode,
-                              absl::optional<Response>)>;
+      base::OnceCallback<void(CtapDeviceResponseCode, std::optional<Response>)>;
   // Represents a per device logic that is owned by FidoTask. Thus,
   // DeviceOperation does not outlive |request|.
   DeviceOperation(FidoDevice* device,
@@ -54,11 +53,11 @@
   ~DeviceOperation() override = default;
 
  protected:
-  void DispatchU2FCommand(absl::optional<std::vector<uint8_t>> command,
+  void DispatchU2FCommand(std::optional<std::vector<uint8_t>> command,
                           FidoDevice::DeviceCallback callback) {
     if (!command || device_->is_in_error_state()) {
       base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
-          FROM_HERE, base::BindOnce(std::move(callback), absl::nullopt));
+          FROM_HERE, base::BindOnce(std::move(callback), std::nullopt));
       return;
     }
 
@@ -68,7 +67,7 @@
   const Request& request() const { return request_; }
   FidoDevice* device() const { return device_; }
   DeviceResponseCallback callback() { return std::move(callback_); }
-  absl::optional<FidoDevice::CancelToken> token_;
+  std::optional<FidoDevice::CancelToken> token_;
 
  private:
   const raw_ptr<FidoDevice> device_ = nullptr;
diff --git a/device/fido/device_response_converter.cc b/device/fido/device_response_converter.cc
index 7081942..55fd78d 100644
--- a/device/fido/device_response_converter.cc
+++ b/device/fido/device_response_converter.cc
@@ -5,6 +5,7 @@
 #include "device/fido/device_response_converter.h"
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <string_view>
 #include <utility>
@@ -26,7 +27,6 @@
 #include "device/fido/fido_parsing_utils.h"
 #include "device/fido/fido_transport_protocol.h"
 #include "device/fido/opaque_attestation_statement.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -43,39 +43,39 @@
   return ProtocolVersion::kUnknown;
 }
 
-absl::optional<Ctap2Version> ConvertStringToCtap2Version(
+std::optional<Ctap2Version> ConvertStringToCtap2Version(
     std::string_view version) {
   if (version == kCtap2Version)
     return Ctap2Version::kCtap2_0;
   if (version == kCtap2_1Version)
     return Ctap2Version::kCtap2_1;
 
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-absl::optional<std::vector<uint8_t>> GetPRFOutputs(
+std::optional<std::vector<uint8_t>> GetPRFOutputs(
     const cbor::Value& results_value) {
   if (!results_value.is_map()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   const cbor::Value::MapValue& results = results_value.GetMap();
   auto first = results.find(cbor::Value(kExtensionPRFFirst));
   if (first == results.end() || !first->second.is_bytestring()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   std::vector<uint8_t> output = first->second.GetBytestring();
   if (output.size() != kExtensionPRFOutputSize) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   auto second = results.find(cbor::Value(kExtensionPRFSecond));
   if (second != results.end()) {
     if (!second->second.is_bytestring()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     const std::vector<uint8_t>& second_bytes = second->second.GetBytestring();
     if (second_bytes.size() != kExtensionPRFOutputSize) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     output.insert(output.end(), second_bytes.begin(), second_bytes.end());
   }
@@ -97,30 +97,30 @@
 
 // Decodes byte array response from authenticator to CBOR value object and
 // checks for correct encoding format.
-absl::optional<AuthenticatorMakeCredentialResponse>
+std::optional<AuthenticatorMakeCredentialResponse>
 ReadCTAPMakeCredentialResponse(FidoTransportProtocol transport_used,
-                               const absl::optional<cbor::Value>& cbor) {
+                               const std::optional<cbor::Value>& cbor) {
   if (!cbor || !cbor->is_map())
-    return absl::nullopt;
+    return std::nullopt;
 
   const auto& decoded_map = cbor->GetMap();
   auto it = decoded_map.find(CBOR(0x01));
   if (it == decoded_map.end() || !it->second.is_string())
-    return absl::nullopt;
+    return std::nullopt;
   auto format = it->second.GetString();
 
   it = decoded_map.find(CBOR(0x02));
   if (it == decoded_map.end() || !it->second.is_bytestring())
-    return absl::nullopt;
+    return std::nullopt;
 
   auto authenticator_data =
       AuthenticatorData::DecodeAuthenticatorData(it->second.GetBytestring());
   if (!authenticator_data)
-    return absl::nullopt;
+    return std::nullopt;
 
   it = decoded_map.find(CBOR(0x03));
   if (it == decoded_map.end() || !it->second.is_map())
-    return absl::nullopt;
+    return std::nullopt;
 
   AuthenticatorMakeCredentialResponse response(
       transport_used,
@@ -131,7 +131,7 @@
   it = decoded_map.find(CBOR(0x04));
   if (it != decoded_map.end()) {
     if (!it->second.is_bool()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     response.enterprise_attestation_returned = it->second.GetBool();
   }
@@ -140,7 +140,7 @@
   if (it != decoded_map.end()) {
     if (!it->second.is_bytestring() ||
         it->second.GetBytestring().size() != kLargeBlobKeyLength) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     response.large_blob_type = LargeBlobSupportType::kKey;
   }
@@ -148,23 +148,23 @@
   it = decoded_map.find(CBOR(0x06));
   if (it != decoded_map.end()) {
     if (!it->second.is_map()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     const auto& unsigned_extension_outputs_map = it->second.GetMap();
     for (const auto& map_it : unsigned_extension_outputs_map) {
       if (!map_it.first.is_string()) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       const std::string& extension_name = map_it.first.GetString();
       if (extension_name == kExtensionPRF) {
         if (!map_it.second.is_map()) {
-          return absl::nullopt;
+          return std::nullopt;
         }
         const cbor::Value::MapValue& prf = map_it.second.GetMap();
         const auto enabled_it = prf.find(cbor::Value(kExtensionPRFEnabled));
         if (enabled_it != prf.end()) {
           if (!enabled_it->second.is_bool()) {
-            return absl::nullopt;
+            return std::nullopt;
           }
           response.prf_enabled = enabled_it->second.GetBool();
         }
@@ -172,20 +172,20 @@
         if (results_it != prf.end()) {
           response.prf_results = GetPRFOutputs(results_it->second);
           if (!response.prf_results) {
-            return absl::nullopt;
+            return std::nullopt;
           }
         }
       } else if (extension_name == kExtensionLargeBlob) {
         if (response.large_blob_type || !map_it.second.is_map()) {
           // Authenticators cannot support both methods.
-          return absl::nullopt;
+          return std::nullopt;
         }
         const cbor::Value::MapValue& large_blob_ext = map_it.second.GetMap();
         const auto supported_it = large_blob_ext.find(
             cbor::Value(device::kExtensionLargeBlobSupported));
         if (supported_it != large_blob_ext.end()) {
           if (!supported_it->second.is_bool()) {
-            return absl::nullopt;
+            return std::nullopt;
           }
           if (supported_it->second.GetBool()) {
             response.large_blob_type = LargeBlobSupportType::kExtension;
@@ -198,26 +198,26 @@
   return response;
 }
 
-absl::optional<AuthenticatorGetAssertionResponse> ReadCTAPGetAssertionResponse(
+std::optional<AuthenticatorGetAssertionResponse> ReadCTAPGetAssertionResponse(
     FidoTransportProtocol transport_used,
-    const absl::optional<cbor::Value>& cbor) {
+    const std::optional<cbor::Value>& cbor) {
   if (!cbor || !cbor->is_map())
-    return absl::nullopt;
+    return std::nullopt;
 
   auto& response_map = cbor->GetMap();
 
   auto it = response_map.find(CBOR(0x02));
   if (it == response_map.end() || !it->second.is_bytestring())
-    return absl::nullopt;
+    return std::nullopt;
 
   auto auth_data =
       AuthenticatorData::DecodeAuthenticatorData(it->second.GetBytestring());
   if (!auth_data)
-    return absl::nullopt;
+    return std::nullopt;
 
   it = response_map.find(CBOR(0x03));
   if (it == response_map.end() || !it->second.is_bytestring())
-    return absl::nullopt;
+    return std::nullopt;
 
   auto signature = it->second.GetBytestring();
   AuthenticatorGetAssertionResponse response(
@@ -228,7 +228,7 @@
     auto credential =
         PublicKeyCredentialDescriptor::CreateFromCBORValue(it->second);
     if (!credential)
-      return absl::nullopt;
+      return std::nullopt;
     response.credential = std::move(*credential);
   }
 
@@ -236,14 +236,14 @@
   if (it != response_map.end()) {
     auto user = PublicKeyCredentialUserEntity::CreateFromCBORValue(it->second);
     if (!user)
-      return absl::nullopt;
+      return std::nullopt;
     response.user_entity = std::move(*user);
   }
 
   it = response_map.find(CBOR(0x05));
   if (it != response_map.end()) {
     if (!it->second.is_unsigned())
-      return absl::nullopt;
+      return std::nullopt;
 
     response.num_credentials = it->second.GetUnsigned();
   }
@@ -251,7 +251,7 @@
   it = response_map.find(CBOR(0x06));
   if (it != response_map.end()) {
     if (!it->second.is_bool() || response.num_credentials.has_value()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     response.user_selected = it->second.GetBool();
@@ -260,12 +260,12 @@
   it = response_map.find(CBOR(0x07));
   if (it != response_map.end()) {
     if (!it->second.is_bytestring()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     const std::vector<uint8_t>& key = it->second.GetBytestring();
     response.large_blob_key.emplace();
     if (key.size() != response.large_blob_key->size()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     memcpy(response.large_blob_key->data(), key.data(),
            response.large_blob_key->size());
@@ -274,30 +274,30 @@
   it = response_map.find(CBOR(0x08));
   if (it != response_map.end()) {
     if (!it->second.is_map()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     const auto& unsigned_extension_outputs_map = it->second.GetMap();
     for (const auto& map_it : unsigned_extension_outputs_map) {
       if (!map_it.first.is_string()) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       const std::string& extension_name = map_it.first.GetString();
       if (extension_name == kExtensionPRF) {
         if (!map_it.second.is_map()) {
-          return absl::nullopt;
+          return std::nullopt;
         }
         const cbor::Value::MapValue& prf = map_it.second.GetMap();
         auto results_it = prf.find(cbor::Value(kExtensionPRFResults));
         if (results_it != prf.end()) {
           response.hmac_secret = GetPRFOutputs(results_it->second);
           if (!response.hmac_secret) {
-            return absl::nullopt;
+            return std::nullopt;
           }
         }
       } else if (extension_name == kExtensionLargeBlob) {
         if (response.large_blob_key || !map_it.second.is_map()) {
           // Authenticators cannot support both large blob methods.
-          return absl::nullopt;
+          return std::nullopt;
         }
         const cbor::Value::MapValue& large_blob_ext = map_it.second.GetMap();
         const auto written_it =
@@ -315,7 +315,7 @@
         if ((has_written && !written_it->second.is_bool()) ||
             (has_blob && !blob_it->second.is_bytestring()) ||
             (has_original_size && !original_size_it->second.is_unsigned())) {
-          return absl::nullopt;
+          return std::nullopt;
         }
 
         if (has_written && !has_blob && !has_original_size) {
@@ -327,7 +327,7 @@
                   original_size_it->second.GetUnsigned()));
         } else {
           // No other pattern of members is allowed.
-          return absl::nullopt;
+          return std::nullopt;
         }
       }
     }
@@ -336,38 +336,38 @@
   return response;
 }
 
-absl::optional<AuthenticatorGetInfoResponse> ReadCTAPGetInfoResponse(
+std::optional<AuthenticatorGetInfoResponse> ReadCTAPGetInfoResponse(
     base::span<const uint8_t> buffer) {
   if (buffer.size() <= kResponseCodeLength) {
     FIDO_LOG(ERROR) << "-> (GetInfo response too short: " << buffer.size()
                     << " bytes)";
-    return absl::nullopt;
+    return std::nullopt;
   }
   if (GetResponseCode(buffer) != CtapDeviceResponseCode::kSuccess) {
     FIDO_LOG(ERROR) << "-> (GetInfo CTAP2 error code " << +buffer[0] << ")";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   cbor::Reader::DecoderError error;
-  absl::optional<CBOR> decoded_response =
+  std::optional<CBOR> decoded_response =
       cbor::Reader::Read(buffer.subspan(1), &error);
 
   if (!decoded_response) {
     FIDO_LOG(ERROR) << "-> (CBOR parse error from GetInfo response '"
                     << cbor::Reader::ErrorCodeToString(error)
                     << "' from raw message " << base::HexEncode(buffer) << ")";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   if (!decoded_response->is_map())
-    return absl::nullopt;
+    return std::nullopt;
 
   FIDO_LOG(DEBUG) << "-> " << cbor::DiagnosticWriter::Write(*decoded_response);
   const auto& response_map = decoded_response->GetMap();
 
   auto it = response_map.find(CBOR(0x01));
   if (it == response_map.end() || !it->second.is_array()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   base::flat_set<ProtocolVersion> protocol_versions;
@@ -375,12 +375,12 @@
   base::flat_set<std::string_view> advertised_protocols;
   for (const auto& version : it->second.GetArray()) {
     if (!version.is_string())
-      return absl::nullopt;
+      return std::nullopt;
     const std::string& version_string = version.GetString();
 
     if (!advertised_protocols.insert(version_string).second) {
       // Duplicate versions are not allowed.
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     ProtocolVersion protocol = ConvertStringToProtocolVersion(version_string);
@@ -390,7 +390,7 @@
     }
 
     if (protocol == ProtocolVersion::kCtap2) {
-      absl::optional<Ctap2Version> ctap2_version =
+      std::optional<Ctap2Version> ctap2_version =
           ConvertStringToCtap2Version(version_string);
       if (ctap2_version) {
         ctap2_versions.insert(*ctap2_version);
@@ -403,13 +403,13 @@
   if (protocol_versions.empty() ||
       (base::Contains(protocol_versions, ProtocolVersion::kCtap2) &&
        ctap2_versions.empty())) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   it = response_map.find(CBOR(0x03));
   if (it == response_map.end() || !it->second.is_bytestring() ||
       it->second.GetBytestring().size() != kAaguidLength) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   AuthenticatorGetInfoResponse response(
@@ -422,12 +422,12 @@
   it = response_map.find(CBOR(0x02));
   if (it != response_map.end()) {
     if (!it->second.is_array())
-      return absl::nullopt;
+      return std::nullopt;
 
     std::vector<std::string> extensions;
     for (const auto& extension : it->second.GetArray()) {
       if (!extension.is_string())
-        return absl::nullopt;
+        return std::nullopt;
 
       const std::string& extension_str = extension.GetString();
       if (extension_str == kExtensionCredProtect) {
@@ -452,19 +452,19 @@
 
   // credBlob requires credProtect support.
   if (cred_blob_extension_seen && !options.supports_cred_protect) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   it = response_map.find(CBOR(0x04));
   if (it != response_map.end()) {
     if (!it->second.is_map())
-      return absl::nullopt;
+      return std::nullopt;
 
     const auto& option_map = it->second.GetMap();
     auto option_map_it = option_map.find(CBOR(kPlatformDeviceMapKey));
     if (option_map_it != option_map.end()) {
       if (!option_map_it->second.is_bool())
-        return absl::nullopt;
+        return std::nullopt;
 
       options.is_platform_device =
           option_map_it->second.GetBool()
@@ -475,7 +475,7 @@
     option_map_it = option_map.find(CBOR(kResidentKeyMapKey));
     if (option_map_it != option_map.end()) {
       if (!option_map_it->second.is_bool())
-        return absl::nullopt;
+        return std::nullopt;
 
       options.supports_resident_key = option_map_it->second.GetBool();
     }
@@ -483,7 +483,7 @@
     option_map_it = option_map.find(CBOR(kUserPresenceMapKey));
     if (option_map_it != option_map.end()) {
       if (!option_map_it->second.is_bool())
-        return absl::nullopt;
+        return std::nullopt;
 
       options.supports_user_presence = option_map_it->second.GetBool();
     }
@@ -491,7 +491,7 @@
     option_map_it = option_map.find(CBOR(kUserVerificationMapKey));
     if (option_map_it != option_map.end()) {
       if (!option_map_it->second.is_bool())
-        return absl::nullopt;
+        return std::nullopt;
 
       if (option_map_it->second.GetBool()) {
         options.user_verification_availability = AuthenticatorSupportedOptions::
@@ -505,7 +505,7 @@
     option_map_it = option_map.find(CBOR(kClientPinMapKey));
     if (option_map_it != option_map.end()) {
       if (!option_map_it->second.is_bool())
-        return absl::nullopt;
+        return std::nullopt;
 
       if (option_map_it->second.GetBool()) {
         options.client_pin_availability = AuthenticatorSupportedOptions::
@@ -519,7 +519,7 @@
     option_map_it = option_map.find(CBOR(kCredentialManagementMapKey));
     if (option_map_it != option_map.end()) {
       if (!option_map_it->second.is_bool()) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       options.supports_credential_management = option_map_it->second.GetBool();
     }
@@ -527,7 +527,7 @@
     option_map_it = option_map.find(CBOR(kCredentialManagementPreviewMapKey));
     if (option_map_it != option_map.end()) {
       if (!option_map_it->second.is_bool()) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       options.supports_credential_management_preview =
           option_map_it->second.GetBool();
@@ -536,7 +536,7 @@
     option_map_it = option_map.find(CBOR(kBioEnrollmentMapKey));
     if (option_map_it != option_map.end()) {
       if (!option_map_it->second.is_bool()) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       using Availability =
           AuthenticatorSupportedOptions::BioEnrollmentAvailability;
@@ -550,7 +550,7 @@
     option_map_it = option_map.find(CBOR(kBioEnrollmentPreviewMapKey));
     if (option_map_it != option_map.end()) {
       if (!option_map_it->second.is_bool()) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       using Availability =
           AuthenticatorSupportedOptions::BioEnrollmentAvailability;
@@ -564,7 +564,7 @@
     option_map_it = option_map.find(CBOR(kPinUvTokenMapKey));
     if (option_map_it != option_map.end()) {
       if (!option_map_it->second.is_bool()) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       options.supports_pin_uv_auth_token = option_map_it->second.GetBool();
     }
@@ -572,12 +572,12 @@
     option_map_it = option_map.find(CBOR(kDefaultCredProtectKey));
     if (option_map_it != option_map.end()) {
       if (!option_map_it->second.is_unsigned()) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       const int64_t value = option_map_it->second.GetInteger();
       if (value != static_cast<uint8_t>(CredProtect::kUVOrCredIDRequired) &&
           value != static_cast<uint8_t>(CredProtect::kUVRequired)) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       options.default_cred_protect = static_cast<CredProtect>(value);
     }
@@ -585,7 +585,7 @@
     option_map_it = option_map.find(CBOR(kEnterpriseAttestationKey));
     if (option_map_it != option_map.end()) {
       if (!option_map_it->second.is_bool()) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       options.enterprise_attestation = option_map_it->second.GetBool();
     }
@@ -593,12 +593,12 @@
     option_map_it = option_map.find(CBOR(kLargeBlobsKey));
     if (option_map_it != option_map.end()) {
       if (!option_map_it->second.is_bool() || !options.supports_resident_key) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       if (option_map_it->second.GetBool() & large_blob_key_extension_seen) {
         if (options.large_blob_type) {
           // Authenticators cannot support both.
-          return absl::nullopt;
+          return std::nullopt;
         }
         options.large_blob_type = LargeBlobSupportType::kKey;
       }
@@ -607,7 +607,7 @@
     option_map_it = option_map.find(CBOR(kAlwaysUvKey));
     if (option_map_it != option_map.end()) {
       if (!option_map_it->second.is_bool()) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       options.always_uv = option_map_it->second.GetBool();
     }
@@ -615,7 +615,7 @@
     option_map_it = option_map.find(CBOR(kMakeCredUvNotRqdKey));
     if (option_map_it != option_map.end()) {
       if (!option_map_it->second.is_bool()) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       options.make_cred_uv_not_required = option_map_it->second.GetBool();
     }
@@ -626,7 +626,7 @@
   it = response_map.find(CBOR(0x05));
   if (it != response_map.end()) {
     if (!it->second.is_unsigned())
-      return absl::nullopt;
+      return std::nullopt;
 
     response.max_msg_size =
         base::saturated_cast<uint32_t>(it->second.GetUnsigned());
@@ -635,14 +635,14 @@
   it = response_map.find(CBOR(0x06));
   if (it != response_map.end()) {
     if (!it->second.is_array())
-      return absl::nullopt;
+      return std::nullopt;
 
     base::flat_set<PINUVAuthProtocol> pin_protocols;
     for (const auto& protocol : it->second.GetArray()) {
       if (!protocol.is_unsigned()) {
-        return absl::nullopt;
+        return std::nullopt;
       }
-      absl::optional<PINUVAuthProtocol> pin_protocol =
+      std::optional<PINUVAuthProtocol> pin_protocol =
           ToPINUVAuthProtocol(protocol.GetUnsigned());
       if (!pin_protocol) {
         continue;
@@ -655,7 +655,7 @@
       response.options.client_pin_availability !=
           AuthenticatorSupportedOptions::ClientPinAvailability::kNotSupported) {
     if (!response.pin_protocols) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     if (response.pin_protocols->empty()) {
       // The authenticator only offers unsupported pinUvAuthToken versions.
@@ -670,7 +670,7 @@
   it = response_map.find(CBOR(0x07));
   if (it != response_map.end()) {
     if (!it->second.is_unsigned())
-      return absl::nullopt;
+      return std::nullopt;
 
     response.max_credential_count_in_list =
         base::saturated_cast<uint32_t>(it->second.GetUnsigned());
@@ -679,7 +679,7 @@
   it = response_map.find(CBOR(0x08));
   if (it != response_map.end()) {
     if (!it->second.is_unsigned())
-      return absl::nullopt;
+      return std::nullopt;
 
     response.max_credential_id_length =
         base::saturated_cast<uint32_t>(it->second.GetUnsigned());
@@ -688,14 +688,14 @@
   it = response_map.find(CBOR(0x09));
   if (it != response_map.end()) {
     if (!it->second.is_array())
-      return absl::nullopt;
+      return std::nullopt;
 
     response.transports.emplace();
     for (const auto& transport_str : it->second.GetArray()) {
       if (!transport_str.is_string())
-        return absl::nullopt;
+        return std::nullopt;
 
-      absl::optional<FidoTransportProtocol> maybe_transport(
+      std::optional<FidoTransportProtocol> maybe_transport(
           ConvertToFidoTransportProtocol(transport_str.GetString()));
       if (maybe_transport.has_value()) {
         response.transports->insert(*maybe_transport);
@@ -706,7 +706,7 @@
   it = response_map.find(CBOR(0x0a));
   if (it != response_map.end()) {
     if (!it->second.is_array()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     response.algorithms.emplace();
@@ -716,13 +716,13 @@
       // Entries are PublicKeyCredentialParameters
       // https://w3c.github.io/webauthn/#dictdef-publickeycredentialparameters
       if (!algorithm.is_map()) {
-        return absl::nullopt;
+        return std::nullopt;
       }
 
       const auto& map = algorithm.GetMap();
       const auto type_it = map.find(CBOR("type"));
       if (type_it == map.end() || !type_it->second.is_string()) {
-        return absl::nullopt;
+        return std::nullopt;
       }
 
       if (type_it->second.GetString() != "public-key") {
@@ -731,7 +731,7 @@
 
       const auto alg_it = map.find(CBOR("alg"));
       if (alg_it == map.end() || !alg_it->second.is_integer()) {
-        return absl::nullopt;
+        return std::nullopt;
       }
 
       const int64_t alg = alg_it->second.GetInteger();
@@ -747,7 +747,7 @@
   it = response_map.find(CBOR(0x0b));
   if (it != response_map.end()) {
     if (!it->second.is_unsigned()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     response.max_serialized_large_blob_array =
@@ -757,7 +757,7 @@
   it = response_map.find(CBOR(0x0c));
   if (it != response_map.end()) {
     if (!it->second.is_bool()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     response.force_pin_change = it->second.GetBool();
@@ -766,7 +766,7 @@
   it = response_map.find(CBOR(0x0d));
   if (it != response_map.end()) {
     if (!it->second.is_unsigned()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     response.min_pin_length =
         base::saturated_cast<uint32_t>(it->second.GetUnsigned());
@@ -775,17 +775,17 @@
   it = response_map.find(CBOR(0x0f));
   // The maxCredBlobLength field is present iff credBlob is supported.
   if ((it != response_map.end()) != cred_blob_extension_seen) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   if (cred_blob_extension_seen) {
     if (!it->second.is_unsigned()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     const uint16_t max_cred_blob_length =
         base::saturated_cast<uint16_t>(it->second.GetUnsigned());
     // CTAP 2.1 requires at least 32 bytes of credBlob to be supported.
     if (max_cred_blob_length < 32) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     response.options.max_cred_blob_length = max_cred_blob_length;
   }
@@ -793,22 +793,22 @@
   it = response_map.find(CBOR(0x14));
   if (it != response_map.end()) {
     if (!it->second.is_unsigned()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     response.remaining_discoverable_credentials =
         base::saturated_cast<uint32_t>(it->second.GetUnsigned());
   }
 
-  return absl::optional<AuthenticatorGetInfoResponse>(std::move(response));
+  return std::optional<AuthenticatorGetInfoResponse>(std::move(response));
 }
 
-static absl::optional<std::string> FixInvalidUTF8String(
+static std::optional<std::string> FixInvalidUTF8String(
     base::span<const uint8_t> utf8_bytes) {
   // CTAP2 devices must store at least 64 bytes of any string.
   if (utf8_bytes.size() < 64) {
     FIDO_LOG(ERROR) << "Not accepting invalid UTF-8 string because it's only "
                     << utf8_bytes.size() << " bytes long";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   base::StreamingUtf8Validator validator;
@@ -823,7 +823,7 @@
         break;
 
       case base::StreamingUtf8Validator::INVALID:
-        return absl::nullopt;
+        return std::nullopt;
 
       case base::StreamingUtf8Validator::VALID_MIDPOINT:
         break;
@@ -837,13 +837,13 @@
       // points that should never appear. Therefore, if this case occurs, the
       // string is structurally valid as UTF-8, but includes invalid code points
       // and thus we reject it.
-      return absl::nullopt;
+      return std::nullopt;
 
     case base::StreamingUtf8Validator::INVALID:
       // This shouldn't happen because we should return immediately if
       // |INVALID| occurs.
       NOTREACHED();
-      return absl::nullopt;
+      return std::nullopt;
 
     case base::StreamingUtf8Validator::VALID_MIDPOINT: {
       // This string has been truncated. This is the case that we expect to
@@ -859,26 +859,26 @@
       if (base::IsStringUTF8(candidate)) {
         return candidate;
       }
-      return absl::nullopt;
+      return std::nullopt;
     }
   }
 }
 
 typedef bool (*PathPredicate)(const std::vector<const cbor::Value*>&);
 
-static absl::optional<cbor::Value> FixInvalidUTF8Value(
+static std::optional<cbor::Value> FixInvalidUTF8Value(
     const cbor::Value& v,
     std::vector<const cbor::Value*>* path,
     PathPredicate predicate) {
   switch (v.type()) {
     case cbor::Value::Type::INVALID_UTF8: {
       if (!predicate(*path)) {
-        return absl::nullopt;
+        return std::nullopt;
       }
-      absl::optional<std::string> maybe_fixed(
+      std::optional<std::string> maybe_fixed(
           FixInvalidUTF8String(v.GetInvalidUTF8()));
       if (!maybe_fixed) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       return cbor::Value(*maybe_fixed);
     }
@@ -899,10 +899,10 @@
       new_array.reserve(old_array.size());
 
       for (const auto& child : old_array) {
-        absl::optional<cbor::Value> maybe_fixed =
+        std::optional<cbor::Value> maybe_fixed =
             FixInvalidUTF8Value(child, path, predicate);
         if (!maybe_fixed) {
-          return absl::nullopt;
+          return std::nullopt;
         }
         new_array.emplace_back(std::move(*maybe_fixed));
       }
@@ -919,7 +919,7 @@
         switch (it.first.type()) {
           case cbor::Value::Type::INVALID_UTF8:
             // Invalid strings in map keys are not supported.
-            return absl::nullopt;
+            return std::nullopt;
 
           case cbor::Value::Type::UNSIGNED:
           case cbor::Value::Type::NEGATIVE:
@@ -928,15 +928,15 @@
 
           default:
             // Other types are not permitted as map keys in CTAP2.
-            return absl::nullopt;
+            return std::nullopt;
         }
 
         path->push_back(&it.first);
-        absl::optional<cbor::Value> maybe_fixed =
+        std::optional<cbor::Value> maybe_fixed =
             FixInvalidUTF8Value(it.second, path, predicate);
         path->pop_back();
         if (!maybe_fixed) {
-          return absl::nullopt;
+          return std::nullopt;
         }
 
         new_map.emplace(it.first.Clone(), std::move(*maybe_fixed));
@@ -990,8 +990,8 @@
   }
 }
 
-absl::optional<cbor::Value> FixInvalidUTF8(cbor::Value in,
-                                           PathPredicate predicate) {
+std::optional<cbor::Value> FixInvalidUTF8(cbor::Value in,
+                                          PathPredicate predicate) {
   if (!ContainsInvalidUTF8(in)) {
     // Common case that everything is fine.
     return in;
@@ -1001,10 +1001,10 @@
   return FixInvalidUTF8Value(in, &path, predicate);
 }
 
-absl::optional<PINUVAuthProtocol> ToPINUVAuthProtocol(int64_t in) {
+std::optional<PINUVAuthProtocol> ToPINUVAuthProtocol(int64_t in) {
   if (in != static_cast<uint8_t>(PINUVAuthProtocol::kV1) &&
       in != static_cast<uint8_t>(PINUVAuthProtocol::kV2)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   return static_cast<PINUVAuthProtocol>(in);
 }
diff --git a/device/fido/device_response_converter.h b/device/fido/device_response_converter.h
index 9e4efcb..9811b7b6 100644
--- a/device/fido/device_response_converter.h
+++ b/device/fido/device_response_converter.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include <optional>
 #include <vector>
 
 #include "base/component_export.h"
@@ -15,7 +16,6 @@
 #include "device/fido/authenticator_make_credential_response.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_transport_protocol.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 // Converts response from authenticators to CTAPResponse objects. If the
 // response of the authenticator does not conform to format specified by the
@@ -31,31 +31,31 @@
 // that conform to format of attestation object defined by the Webauthn spec:
 // https://w3c.github.io/webauthn/#fig-attStructs
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<AuthenticatorMakeCredentialResponse>
+std::optional<AuthenticatorMakeCredentialResponse>
 ReadCTAPMakeCredentialResponse(FidoTransportProtocol transport_used,
-                               const absl::optional<cbor::Value>& cbor);
+                               const std::optional<cbor::Value>& cbor);
 
 // Converts |cbor|, the response to an |AuthenticatorGetAssertion| /
 // |AuthenticatorGetNextAssertion| request, to an
 // |AuthenticatorGetAssertionResponse|.
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<AuthenticatorGetAssertionResponse> ReadCTAPGetAssertionResponse(
+std::optional<AuthenticatorGetAssertionResponse> ReadCTAPGetAssertionResponse(
     FidoTransportProtocol transport_used,
-    const absl::optional<cbor::Value>& cbor);
+    const std::optional<cbor::Value>& cbor);
 
 // De-serializes CBOR encoded response to AuthenticatorGetInfo request to
 // AuthenticatorGetInfoResponse object.
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<AuthenticatorGetInfoResponse> ReadCTAPGetInfoResponse(
+std::optional<AuthenticatorGetInfoResponse> ReadCTAPGetInfoResponse(
     base::span<const uint8_t> buffer);
 
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<cbor::Value> FixInvalidUTF8(
+std::optional<cbor::Value> FixInvalidUTF8(
     cbor::Value in,
     bool (*predicate)(const std::vector<const cbor::Value*>&));
 
 // Converts |in| to the equivalent |PINUVAuthProtocol|.
-absl::optional<PINUVAuthProtocol> ToPINUVAuthProtocol(int64_t in);
+std::optional<PINUVAuthProtocol> ToPINUVAuthProtocol(int64_t in);
 
 }  // namespace device
 
diff --git a/device/fido/enclave/enclave_authenticator.cc b/device/fido/enclave/enclave_authenticator.cc
index 46822d1e..9287db1 100644
--- a/device/fido/enclave/enclave_authenticator.cc
+++ b/device/fido/enclave/enclave_authenticator.cc
@@ -127,13 +127,13 @@
 }
 
 void EnclaveAuthenticator::ProcessMakeCredentialResponse(
-    absl::optional<cbor::Value> response) {
+    std::optional<cbor::Value> response) {
   if (!response) {
     CompleteRequestWithError(CtapDeviceResponseCode::kCtap2ErrOther);
     return;
   }
-  absl::optional<AuthenticatorMakeCredentialResponse> opt_response;
-  absl::optional<sync_pb::WebauthnCredentialSpecifics> opt_entity;
+  std::optional<AuthenticatorMakeCredentialResponse> opt_response;
+  std::optional<sync_pb::WebauthnCredentialSpecifics> opt_entity;
   std::string error_description;
   std::tie(opt_response, opt_entity, error_description) =
       ParseMakeCredentialResponse(std::move(*response),
@@ -151,7 +151,7 @@
 }
 
 void EnclaveAuthenticator::ProcessGetAssertionResponse(
-    absl::optional<cbor::Value> response) {
+    std::optional<cbor::Value> response) {
   if (!response) {
     CompleteRequestWithError(CtapDeviceResponseCode::kCtap2ErrOther);
     return;
@@ -178,13 +178,13 @@
   }
 
   if (pending_make_credential_request_) {
-    CompleteMakeCredentialRequest(error, absl::nullopt);
+    CompleteMakeCredentialRequest(error, std::nullopt);
   }
 }
 
 void EnclaveAuthenticator::CompleteMakeCredentialRequest(
     CtapDeviceResponseCode status,
-    absl::optional<AuthenticatorMakeCredentialResponse> response) {
+    std::optional<AuthenticatorMakeCredentialResponse> response) {
   // Using PostTask guards against any lifetime concerns for this class and
   // EnclaveWebSocketClient. It is safe to do cleanup after invoking the
   // callback.
@@ -192,7 +192,7 @@
       FROM_HERE,
       base::BindOnce(
           [](MakeCredentialCallback callback, CtapDeviceResponseCode status,
-             absl::optional<AuthenticatorMakeCredentialResponse> response) {
+             std::optional<AuthenticatorMakeCredentialResponse> response) {
             std::move(callback).Run(status, std::move(response));
           },
           std::move(pending_make_credential_request_->callback), status,
@@ -234,7 +234,7 @@
   return *options;
 }
 
-absl::optional<FidoTransportProtocol>
+std::optional<FidoTransportProtocol>
 EnclaveAuthenticator::AuthenticatorTransport() const {
   return FidoTransportProtocol::kInternal;
 }
diff --git a/device/fido/enclave/enclave_authenticator.h b/device/fido/enclave/enclave_authenticator.h
index caeada6..2c8224a 100644
--- a/device/fido/enclave/enclave_authenticator.h
+++ b/device/fido/enclave/enclave_authenticator.h
@@ -7,6 +7,7 @@
 
 #include <array>
 #include <memory>
+#include <optional>
 #include <string>
 #include <string_view>
 #include <vector>
@@ -26,7 +27,6 @@
 #include "device/fido/fido_authenticator.h"
 #include "device/fido/fido_types.h"
 #include "services/network/public/mojom/network_context.mojom.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 namespace device::enclave {
@@ -46,7 +46,7 @@
   EnclaveAuthenticator(const EnclaveAuthenticator&) = delete;
   EnclaveAuthenticator& operator=(const EnclaveAuthenticator&) = delete;
 
-  void SetOauthToken(absl::optional<std::string_view> token);
+  void SetOauthToken(std::optional<std::string_view> token);
 
   // FidoAuthenticator:
   void GetAssertion(CtapGetAssertionRequest request,
@@ -60,7 +60,7 @@
   AuthenticatorType GetType() const override;
   std::string GetId() const override;
   const AuthenticatorSupportedOptions& Options() const override;
-  absl::optional<FidoTransportProtocol> AuthenticatorTransport() const override;
+  std::optional<FidoTransportProtocol> AuthenticatorTransport() const override;
   base::WeakPtr<FidoAuthenticator> GetWeakPtr() override;
 
  private:
@@ -92,12 +92,12 @@
     MakeCredentialCallback callback;
   };
 
-  void ProcessMakeCredentialResponse(absl::optional<cbor::Value> response);
-  void ProcessGetAssertionResponse(absl::optional<cbor::Value> response);
+  void ProcessMakeCredentialResponse(std::optional<cbor::Value> response);
+  void ProcessGetAssertionResponse(std::optional<cbor::Value> response);
   void CompleteRequestWithError(CtapDeviceResponseCode error);
   void CompleteMakeCredentialRequest(
       CtapDeviceResponseCode status,
-      absl::optional<AuthenticatorMakeCredentialResponse> response);
+      std::optional<AuthenticatorMakeCredentialResponse> response);
   void CompleteGetAssertionRequest(
       CtapDeviceResponseCode status,
       std::vector<AuthenticatorGetAssertionResponse> responses);
diff --git a/device/fido/enclave/enclave_discovery.h b/device/fido/enclave/enclave_discovery.h
index 6b0c9f8..171fe64 100644
--- a/device/fido/enclave/enclave_discovery.h
+++ b/device/fido/enclave/enclave_discovery.h
@@ -6,6 +6,7 @@
 #define DEVICE_FIDO_ENCLAVE_ENCLAVE_DISCOVERY_H_
 
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "base/component_export.h"
@@ -16,7 +17,6 @@
 #include "device/fido/enclave/types.h"
 #include "device/fido/fido_discovery_base.h"
 #include "services/network/public/mojom/network_context.mojom.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device::enclave {
 
@@ -48,7 +48,7 @@
   base::RepeatingCallback<void(sync_pb::WebauthnCredentialSpecifics)>
       save_passkey_callback_;
   raw_ptr<network::mojom::NetworkContext> network_context_;
-  std::unique_ptr<EventStream<absl::optional<std::string_view>>>
+  std::unique_ptr<EventStream<std::optional<std::string_view>>>
       oauth_token_provider_;
 
   base::WeakPtrFactory<EnclaveAuthenticatorDiscovery> weak_factory_{this};
diff --git a/device/fido/enclave/enclave_protocol_utils.cc b/device/fido/enclave/enclave_protocol_utils.cc
index 7e35229..fb5113b 100644
--- a/device/fido/enclave/enclave_protocol_utils.cc
+++ b/device/fido/enclave/enclave_protocol_utils.cc
@@ -152,23 +152,23 @@
 
 }  // namespace
 
-std::pair<absl::optional<AuthenticatorGetAssertionResponse>, std::string>
+std::pair<std::optional<AuthenticatorGetAssertionResponse>, std::string>
 ParseGetAssertionResponse(cbor::Value response_value,
                           base::span<const uint8_t> credential_id) {
   if (!response_value.is_array() || response_value.GetArray().empty()) {
-    return {absl::nullopt, "Command response was not a valid CBOR array."};
+    return {std::nullopt, "Command response was not a valid CBOR array."};
   }
 
   base::Value response_element =
       CborValueToBaseValue(response_value.GetArray()[0]);
 
   if (!response_element.is_dict()) {
-    return {absl::nullopt, "Command response element is not a map."};
+    return {std::nullopt, "Command response element is not a map."};
   }
 
   if (const std::string* error =
           response_element.GetDict().FindString(kResponseErrorKey)) {
-    return {absl::nullopt,
+    return {std::nullopt,
             base::StrCat({"Error received from enclave: ", *error})};
   }
 
@@ -176,21 +176,20 @@
       response_element.GetDict().FindDict(kResponseSuccessKey);
   if (!success_response) {
     return {
-        absl::nullopt,
+        std::nullopt,
         "Command response did not contain a successful response or an error."};
   }
 
   base::Value* assertion_response =
       success_response->Find(kGetAssertionResponseKey);
   if (!assertion_response) {
-    return {absl::nullopt,
-            "Command response did not contain a response field."};
+    return {std::nullopt, "Command response did not contain a response field."};
   }
 
-  absl::optional<AuthenticatorGetAssertionResponse> response =
+  std::optional<AuthenticatorGetAssertionResponse> response =
       AuthenticatorGetAssertionResponseFromValue(*assertion_response);
   if (!response) {
-    return {absl::nullopt, "Assertion response failed to parse."};
+    return {std::nullopt, "Assertion response failed to parse."};
   }
 
   response->credential = PublicKeyCredentialDescriptor(
@@ -200,14 +199,14 @@
   return {std::move(response), std::string()};
 }
 
-std::tuple<absl::optional<AuthenticatorMakeCredentialResponse>,
-           absl::optional<sync_pb::WebauthnCredentialSpecifics>,
+std::tuple<std::optional<AuthenticatorMakeCredentialResponse>,
+           std::optional<sync_pb::WebauthnCredentialSpecifics>,
            std::string>
 ParseMakeCredentialResponse(cbor::Value response_value,
                             const CtapMakeCredentialRequest& request,
                             int32_t wrapped_secret_version) {
   if (!response_value.is_array() || response_value.GetArray().empty()) {
-    return {absl::nullopt, absl::nullopt,
+    return {std::nullopt, std::nullopt,
             "Command response was not a valid CBOR array."};
   }
 
@@ -219,13 +218,13 @@
       CborValueToBaseValue(response_value.GetArray()[0]);
 
   if (!response_element.is_dict()) {
-    return {absl::nullopt, absl::nullopt,
+    return {std::nullopt, std::nullopt,
             "Command response element is not a map."};
   }
 
   if (const std::string* error =
           response_element.GetDict().FindString(kResponseErrorKey)) {
-    return {absl::nullopt, absl::nullopt,
+    return {std::nullopt, std::nullopt,
             base::StrCat({"Error received from enclave: ", *error})};
   }
 
@@ -233,21 +232,21 @@
       response_element.GetDict().FindDict(kResponseSuccessKey);
   if (!success_response) {
     return {
-        absl::nullopt, absl::nullopt,
+        std::nullopt, std::nullopt,
         "Command response did not contain a successful response or an error."};
   }
 
   const std::vector<uint8_t>* pubkey_field =
       success_response->FindBlob(kMakeCredentialResponsePubKeyKey);
   if (!pubkey_field) {
-    return {absl::nullopt, absl::nullopt,
+    return {std::nullopt, std::nullopt,
             "MakeCredential response did not contain a public key."};
   }
 
   const std::vector<uint8_t>* encrypted_field =
       success_response->FindBlob(kMakeCredentialResponseEncryptedKey);
   if (!encrypted_field) {
-    return {absl::nullopt, absl::nullopt,
+    return {std::nullopt, std::nullopt,
             "MakeCredential response did not contain an encrypted passkey."};
   }
 
@@ -363,7 +362,7 @@
     command = cbor::Value(std::move(requests));
   }
 
-  absl::optional<std::vector<uint8_t>> serialized_requests =
+  std::optional<std::vector<uint8_t>> serialized_requests =
       cbor::Writer::Write(command);
   std::array<uint8_t, crypto::kSHA256Length> serialized_requests_hash;
   if (!signing_callback.is_null()) {
@@ -398,7 +397,7 @@
         request_body_map.emplace(
             cbor::Value(kCommandSigKey),
             cbor::Value(std::move(client_signature.signature)));
-        absl::optional<std::vector<uint8_t>> serialized_request =
+        std::optional<std::vector<uint8_t>> serialized_request =
             cbor::Writer::Write(cbor::Value(std::move(request_body_map)));
         std::move(complete_callback).Run(*serialized_request);
       };
diff --git a/device/fido/enclave/enclave_protocol_utils.h b/device/fido/enclave/enclave_protocol_utils.h
index 44a8d54e..a0bf545 100644
--- a/device/fido/enclave/enclave_protocol_utils.h
+++ b/device/fido/enclave/enclave_protocol_utils.h
@@ -5,6 +5,7 @@
 #ifndef DEVICE_FIDO_ENCLAVE_ENCLAVE_PROTOCOL_UTILS_H_
 #define DEVICE_FIDO_ENCLAVE_ENCLAVE_PROTOCOL_UTILS_H_
 
+#include <optional>
 #include <string>
 #include <tuple>
 #include <utility>
@@ -20,7 +21,6 @@
 #include "device/fido/ctap_get_assertion_request.h"
 #include "device/fido/ctap_make_credential_request.h"
 #include "device/fido/enclave/types.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace sync_pb {
 class WebauthnCredentialSpecifics;
@@ -37,14 +37,14 @@
 namespace enclave {
 
 // Parses a decrypted assertion command response from the enclave.
-std::pair<absl::optional<AuthenticatorGetAssertionResponse>, std::string>
+std::pair<std::optional<AuthenticatorGetAssertionResponse>, std::string>
     COMPONENT_EXPORT(DEVICE_FIDO)
         ParseGetAssertionResponse(cbor::Value response_value,
                                   base::span<const uint8_t> credential_id);
 
 // Parses a decrypted registration command response from the enclave.
-std::tuple<absl::optional<AuthenticatorMakeCredentialResponse>,
-           absl::optional<sync_pb::WebauthnCredentialSpecifics>,
+std::tuple<std::optional<AuthenticatorMakeCredentialResponse>,
+           std::optional<sync_pb::WebauthnCredentialSpecifics>,
            std::string>
     COMPONENT_EXPORT(DEVICE_FIDO)
         ParseMakeCredentialResponse(cbor::Value response,
diff --git a/device/fido/enclave/enclave_protocol_utils_unittest.cc b/device/fido/enclave/enclave_protocol_utils_unittest.cc
index 5d66817..751c7d2 100644
--- a/device/fido/enclave/enclave_protocol_utils_unittest.cc
+++ b/device/fido/enclave/enclave_protocol_utils_unittest.cc
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "device/fido/enclave/enclave_protocol_utils.h"
+
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -17,7 +20,6 @@
 #include "components/cbor/values.h"
 #include "components/sync/protocol/webauthn_credential_specifics.pb.h"
 #include "device/fido/ctap_make_credential_request.h"
-#include "device/fido/enclave/enclave_protocol_utils.h"
 #include "device/fido/fido_parsing_utils.h"
 #include "device/fido/fido_transport_protocol.h"
 #include "device/fido/json_request.h"
@@ -26,7 +28,6 @@
 #include "device/fido/public_key_credential_user_entity.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 namespace {
@@ -192,7 +193,7 @@
 
   // This checks the outer map values of a request, which are common to all
   // request types.
-  absl::optional<cbor::Value> ValidateRequestFormatAndReturnCommandList(
+  std::optional<cbor::Value> ValidateRequestFormatAndReturnCommandList(
       const cbor::Value& request_cbor) {
     EXPECT_TRUE(request_cbor.is_map());
     EXPECT_NE(request_cbor.GetMap().find(cbor::Value("sig")),
@@ -208,7 +209,7 @@
     auto encoded_request =
         request_cbor.GetMap().find(cbor::Value("encoded_requests"));
     EXPECT_NE(encoded_request, request_cbor.GetMap().end());
-    absl::optional<cbor::Value> decoded_command =
+    std::optional<cbor::Value> decoded_command =
         cbor::Reader::Read(encoded_request->second.GetBytestring());
     EXPECT_TRUE(decoded_command);
     EXPECT_TRUE(decoded_command->is_array());
@@ -247,7 +248,7 @@
   BuildCommandCompletionWaiter waiter;
   auto entity = PasskeyEntity();
   entity.set_rp_id(kRpId);
-  absl::optional<base::Value> parsed_json =
+  std::optional<base::Value> parsed_json =
       base::JSONReader::Read(kGetAssertionRequestJson);
   EXPECT_TRUE(parsed_json);
   auto json_request =
@@ -261,8 +262,7 @@
 
   waiter.Wait();
 
-  absl::optional<cbor::Value> request_cbor =
-      cbor::Reader::Read(waiter.result());
+  std::optional<cbor::Value> request_cbor = cbor::Reader::Read(waiter.result());
   auto decoded_command =
       ValidateRequestFormatAndReturnCommandList(*request_cbor);
   auto& command_element = decoded_command->GetArray()[0];
@@ -291,7 +291,7 @@
 
 TEST_F(EnclaveProtocolUtilsTest, BuildMakeCredentialRequest_Success) {
   BuildCommandCompletionWaiter waiter;
-  absl::optional<base::Value> parsed_json =
+  std::optional<base::Value> parsed_json =
       base::JSONReader::Read(kMakeCredentialRequestJson);
   EXPECT_TRUE(parsed_json);
   auto json_request =
@@ -304,8 +304,7 @@
 
   waiter.Wait();
 
-  absl::optional<cbor::Value> request_cbor =
-      cbor::Reader::Read(waiter.result());
+  std::optional<cbor::Value> request_cbor = cbor::Reader::Read(waiter.result());
   auto decoded_command =
       ValidateRequestFormatAndReturnCommandList(*request_cbor);
   auto& command_element = decoded_command->GetArray()[0];
@@ -327,11 +326,11 @@
   cbor::Value response_cbor = cbor::Reader::Read(response_serialized).value();
   std::vector<uint8_t> cred_id = {0, 1, 2};
 
-  absl::optional<AuthenticatorGetAssertionResponse> response;
+  std::optional<AuthenticatorGetAssertionResponse> response;
   std::string error_string;
   std::tie(response, error_string) =
       ParseGetAssertionResponse(std::move(response_cbor), cred_id);
-  bool pass = (response != absl::nullopt) && (error_string.empty());
+  bool pass = (response != std::nullopt) && (error_string.empty());
   EXPECT_TRUE(pass);
   EXPECT_EQ(response->user_entity->id, std::vector<uint8_t>({'a', 'b'}));
   EXPECT_EQ(response->credential->id, std::vector<uint8_t>({0, 1, 2}));
@@ -339,7 +338,7 @@
 
 TEST_F(EnclaveProtocolUtilsTest, ParseGetAssertionResponse_Failures) {
   for (auto& test_case : kFailingGetAssertionResponses) {
-    absl::optional<AuthenticatorGetAssertionResponse> response;
+    std::optional<AuthenticatorGetAssertionResponse> response;
     std::string error_string;
 
     std::vector<uint8_t> response_serialized;
@@ -348,7 +347,7 @@
     std::vector<uint8_t> cred_id = {0, 1, 2};
     std::tie(response, error_string) =
         ParseGetAssertionResponse(std::move(response_cbor), cred_id);
-    bool pass = (response == absl::nullopt) && (!error_string.empty());
+    bool pass = (response == std::nullopt) && (!error_string.empty());
     EXPECT_TRUE(pass) << "Failed GetAssertion response parsing for: "
                       << test_case.name;
   }
@@ -365,12 +364,12 @@
       PublicKeyCredentialParams(
           std::vector<PublicKeyCredentialParams::CredentialInfo>()));
 
-  absl::optional<AuthenticatorMakeCredentialResponse> response;
-  absl::optional<sync_pb::WebauthnCredentialSpecifics> entity;
+  std::optional<AuthenticatorMakeCredentialResponse> response;
+  std::optional<sync_pb::WebauthnCredentialSpecifics> entity;
   std::string error_string;
   std::tie(response, entity, error_string) = ParseMakeCredentialResponse(
       std::move(response_cbor), ctap_request, kWrappedSecretVersion);
-  bool pass = (response != absl::nullopt) && (entity != absl::nullopt) &&
+  bool pass = (response != std::nullopt) && (entity != std::nullopt) &&
               (error_string.empty());
   EXPECT_TRUE(pass) << error_string;
   EXPECT_EQ(entity->rp_id(), std::string(kRpId));
@@ -395,8 +394,8 @@
           std::vector<PublicKeyCredentialParams::CredentialInfo>()));
 
   for (auto& test_case : kFailingMakeCredentialResponses) {
-    absl::optional<AuthenticatorMakeCredentialResponse> response;
-    absl::optional<sync_pb::WebauthnCredentialSpecifics> entity;
+    std::optional<AuthenticatorMakeCredentialResponse> response;
+    std::optional<sync_pb::WebauthnCredentialSpecifics> entity;
     std::string error_string;
 
     std::vector<uint8_t> response_serialized;
@@ -404,7 +403,7 @@
     cbor::Value response_cbor = cbor::Reader::Read(response_serialized).value();
     std::tie(response, entity, error_string) = ParseMakeCredentialResponse(
         std::move(response_cbor), ctap_request, kWrappedSecretVersion);
-    bool pass = (response == absl::nullopt) && (entity == absl::nullopt) &&
+    bool pass = (response == std::nullopt) && (entity == std::nullopt) &&
                 (!error_string.empty());
     EXPECT_TRUE(pass) << "Failed MakeCredential response parsing for: "
                       << test_case.name;
diff --git a/device/fido/enclave/enclave_websocket_client.cc b/device/fido/enclave/enclave_websocket_client.cc
index 57b0c65b7..4f6d0b7 100644
--- a/device/fido/enclave/enclave_websocket_client.cc
+++ b/device/fido/enclave/enclave_websocket_client.cc
@@ -132,7 +132,7 @@
       /*url_loader_network_observer=*/mojo::NullRemote(),
       /*auth_handler=*/mojo::NullRemote(),
       /*header_client=*/mojo::NullRemote(),
-      /*throttling_profile_id=*/absl::nullopt);
+      /*throttling_profile_id=*/std::nullopt);
 }
 
 void EnclaveWebSocketClient::InternalWrite(base::span<const uint8_t> data) {
@@ -199,7 +199,7 @@
 
   if (pending_write_data_) {
     InternalWrite(*pending_write_data_);
-    pending_write_data_ = absl::nullopt;
+    pending_write_data_ = std::nullopt;
   }
 }
 
@@ -299,7 +299,7 @@
   }
   state_ = State::kDisconnected;
   client_receiver_.reset();
-  pending_write_data_ = absl::nullopt;
+  pending_write_data_ = std::nullopt;
   pending_read_data_index_ = 0;
   pending_read_finished_ = false;
   pending_read_data_.clear();
diff --git a/device/fido/enclave/enclave_websocket_client.h b/device/fido/enclave/enclave_websocket_client.h
index b5213245..c9971b7 100644
--- a/device/fido/enclave/enclave_websocket_client.h
+++ b/device/fido/enclave/enclave_websocket_client.h
@@ -6,6 +6,7 @@
 #define DEVICE_FIDO_ENCLAVE_ENCLAVE_WEBSOCKET_CLIENT_H_
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -18,7 +19,6 @@
 #include "mojo/public/cpp/system/simple_watcher.h"
 #include "services/network/public/mojom/network_context.mojom.h"
 #include "services/network/public/mojom/websocket.mojom.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 namespace device::enclave {
@@ -34,7 +34,7 @@
 
   using OnResponseCallback =
       base::RepeatingCallback<void(SocketStatus,
-                                   absl::optional<std::vector<uint8_t>>)>;
+                                   std::optional<std::vector<uint8_t>>)>;
 
   EnclaveWebSocketClient(
       const GURL& service_url,
@@ -105,7 +105,7 @@
 
   // pending_write_data_ contains a message to be sent which can be delayed if
   // the socket is still connecting.
-  absl::optional<std::vector<uint8_t>> pending_write_data_;
+  std::optional<std::vector<uint8_t>> pending_write_data_;
 
   mojo::Receiver<network::mojom::WebSocketHandshakeClient> handshake_receiver_{
       this};
diff --git a/device/fido/enclave/transact.cc b/device/fido/enclave/transact.cc
index 6293755..98d4927 100644
--- a/device/fido/enclave/transact.cc
+++ b/device/fido/enclave/transact.cc
@@ -24,12 +24,12 @@
   Transaction(const EnclaveIdentity& enclave,
               cbor::Value request,
               SigningCallback signing_callback,
-              base::OnceCallback<void(absl::optional<cbor::Value>)> callback)
+              base::OnceCallback<void(std::optional<cbor::Value>)> callback)
       : enclave_public_key_(enclave.public_key),
         request_(std::move(request)),
         signing_callback_(std::move(signing_callback)),
         callback_(std::move(callback)),
-        handshake_(absl::nullopt, enclave.public_key, absl::nullopt) {}
+        handshake_(std::nullopt, enclave.public_key, std::nullopt) {}
 
   void set_client(std::unique_ptr<EnclaveWebSocketClient> client) {
     client_ = std::move(client);
@@ -38,11 +38,11 @@
   void Start() { client_->Write(handshake_.BuildInitialMessage()); }
 
   void OnData(device::enclave::EnclaveWebSocketClient::SocketStatus status,
-              absl::optional<std::vector<uint8_t>> data) {
+              std::optional<std::vector<uint8_t>> data) {
     if (!done_handshake_) {
       if (status != EnclaveWebSocketClient::SocketStatus::kOk) {
         LOG(ERROR) << "Enclave WebSocket connection failed";
-        std::move(callback_).Run(absl::nullopt);
+        std::move(callback_).Run(std::nullopt);
         // client_ holds a RepeatingCallback that has a reference to this
         // object. Thus, by deleting it, this object should also be destroyed.
         client_.reset();
@@ -52,7 +52,7 @@
       cablev2::HandshakeResult result = handshake_.ProcessResponse(*data);
       if (!result) {
         LOG(ERROR) << "Enclave handshake failed";
-        std::move(callback_).Run(absl::nullopt);
+        std::move(callback_).Run(std::nullopt);
         client_.reset();
         return;
       }
@@ -70,14 +70,14 @@
         std::vector<uint8_t> plaintext;
         if (!crypter_->Decrypt(*data, &plaintext)) {
           LOG(ERROR) << "Failed to decrypt enclave response";
-          std::move(callback_).Run(absl::nullopt);
+          std::move(callback_).Run(std::nullopt);
           break;
         }
 
-        absl::optional<cbor::Value> response = cbor::Reader::Read(plaintext);
+        std::optional<cbor::Value> response = cbor::Reader::Read(plaintext);
         if (!response) {
           LOG(ERROR) << "Failed to parse enclave response";
-          std::move(callback_).Run(absl::nullopt);
+          std::move(callback_).Run(std::nullopt);
           break;
         }
 
@@ -96,7 +96,7 @@
   void RequestReady(std::vector<uint8_t> request) {
     if (!crypter_->Encrypt(&request)) {
       LOG(ERROR) << "Failed to encrypt message to enclave";
-      std::move(callback_).Run(absl::nullopt);
+      std::move(callback_).Run(std::nullopt);
       client_.reset();
       return;
     }
@@ -106,11 +106,11 @@
   const std::array<uint8_t, kP256X962Length> enclave_public_key_;
   cbor::Value request_;
   SigningCallback signing_callback_;
-  base::OnceCallback<void(absl::optional<cbor::Value>)> callback_;
+  base::OnceCallback<void(std::optional<cbor::Value>)> callback_;
   cablev2::HandshakeInitiator handshake_;
   std::unique_ptr<EnclaveWebSocketClient> client_;
   std::unique_ptr<cablev2::Crypter> crypter_;
-  absl::optional<std::array<uint8_t, 32>> handshake_hash_;
+  std::optional<std::array<uint8_t, 32>> handshake_hash_;
   bool done_handshake_ = false;
 };
 
@@ -121,7 +121,7 @@
               std::string access_token,
               cbor::Value request,
               SigningCallback signing_callback,
-              base::OnceCallback<void(absl::optional<cbor::Value>)> callback) {
+              base::OnceCallback<void(std::optional<cbor::Value>)> callback) {
   auto transaction = base::MakeRefCounted<Transaction>(
       enclave, std::move(request), std::move(signing_callback),
       std::move(callback));
diff --git a/device/fido/enclave/transact.h b/device/fido/enclave/transact.h
index 4a06e89..f9ae7762 100644
--- a/device/fido/enclave/transact.h
+++ b/device/fido/enclave/transact.h
@@ -5,6 +5,7 @@
 #ifndef DEVICE_FIDO_ENCLAVE_TRANSACT_H_
 #define DEVICE_FIDO_ENCLAVE_TRANSACT_H_
 
+#include <optional>
 #include <string>
 
 #include "base/component_export.h"
@@ -12,7 +13,6 @@
 #include "base/memory/raw_ptr.h"
 #include "device/fido/enclave/types.h"
 #include "services/network/public/mojom/network_context.mojom-forward.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace network::mojom {
 class NetworkContext;
@@ -34,7 +34,7 @@
               std::string access_token,
               cbor::Value request,
               SigningCallback signing_callback,
-              base::OnceCallback<void(absl::optional<cbor::Value>)> callback);
+              base::OnceCallback<void(std::optional<cbor::Value>)> callback);
 
 }  // namespace device::enclave
 
diff --git a/device/fido/fido_authenticator.cc b/device/fido/fido_authenticator.cc
index 92300d73..ffcfc0b 100644
--- a/device/fido/fido_authenticator.cc
+++ b/device/fido/fido_authenticator.cc
@@ -16,9 +16,9 @@
 void FidoAuthenticator::ExcludeAppIdCredentialsBeforeMakeCredential(
     CtapMakeCredentialRequest request,
     MakeCredentialOptions options,
-    base::OnceCallback<void(CtapDeviceResponseCode, absl::optional<bool>)>
+    base::OnceCallback<void(CtapDeviceResponseCode, std::optional<bool>)>
         callback) {
-  std::move(callback).Run(CtapDeviceResponseCode::kSuccess, absl::nullopt);
+  std::move(callback).Run(CtapDeviceResponseCode::kSuccess, std::nullopt);
 }
 
 void FidoAuthenticator::GetPlatformCredentialInfoForRequest(
@@ -38,7 +38,7 @@
 void FidoAuthenticator::GetPINToken(
     std::string pin,
     std::vector<pin::Permissions> permissions,
-    absl::optional<std::string> rp_id,
+    std::optional<std::string> rp_id,
     FidoAuthenticator::GetTokenCallback callback) {
   NOTREACHED();
 }
@@ -54,7 +54,7 @@
 
 void FidoAuthenticator::GetUvToken(
     std::vector<pin::Permissions> permissions,
-    absl::optional<std::string> rp_id,
+    std::optional<std::string> rp_id,
     FidoAuthenticator::GetTokenCallback callback) {
   NOTREACHED();
 }
@@ -140,7 +140,7 @@
 
 void FidoAuthenticator::BioEnrollFingerprint(
     const pin::TokenResponse&,
-    absl::optional<std::vector<uint8_t>> template_id,
+    std::optional<std::vector<uint8_t>> template_id,
     BioEnrollmentCallback) {
   NOTREACHED();
 }
@@ -173,8 +173,8 @@
   NOTREACHED();
 }
 
-absl::optional<base::span<const int32_t>> FidoAuthenticator::GetAlgorithms() {
-  return absl::nullopt;
+std::optional<base::span<const int32_t>> FidoAuthenticator::GetAlgorithms() {
+  return std::nullopt;
 }
 
 bool FidoAuthenticator::DiscoverableCredentialStorageFull() const {
@@ -183,7 +183,7 @@
 
 void FidoAuthenticator::Reset(ResetCallback callback) {
   std::move(callback).Run(CtapDeviceResponseCode::kCtap1ErrInvalidCommand,
-                          absl::nullopt);
+                          std::nullopt);
 }
 
 AuthenticatorType FidoAuthenticator::GetType() const {
diff --git a/device/fido/fido_authenticator.h b/device/fido/fido_authenticator.h
index bffbfeb..f6032c83 100644
--- a/device/fido/fido_authenticator.h
+++ b/device/fido/fido_authenticator.h
@@ -6,6 +6,7 @@
 #define DEVICE_FIDO_FIDO_AUTHENTICATOR_H_
 
 #include <cstdint>
+#include <optional>
 #include <string>
 
 #include "base/component_export.h"
@@ -26,7 +27,6 @@
 #include "device/fido/fido_types.h"
 #include "device/fido/large_blob.h"
 #include "device/fido/make_credential_request_handler.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -48,7 +48,7 @@
  public:
   using MakeCredentialCallback = base::OnceCallback<void(
       CtapDeviceResponseCode,
-      absl::optional<AuthenticatorMakeCredentialResponse>)>;
+      std::optional<AuthenticatorMakeCredentialResponse>)>;
   using GetAssertionCallback =
       base::OnceCallback<void(CtapDeviceResponseCode,
                               std::vector<AuthenticatorGetAssertionResponse>)>;
@@ -58,31 +58,31 @@
 
   using GetRetriesCallback =
       base::OnceCallback<void(CtapDeviceResponseCode,
-                              absl::optional<pin::RetriesResponse>)>;
+                              std::optional<pin::RetriesResponse>)>;
   using GetTokenCallback =
       base::OnceCallback<void(CtapDeviceResponseCode,
-                              absl::optional<pin::TokenResponse>)>;
+                              std::optional<pin::TokenResponse>)>;
   using SetPINCallback =
       base::OnceCallback<void(CtapDeviceResponseCode,
-                              absl::optional<pin::EmptyResponse>)>;
+                              std::optional<pin::EmptyResponse>)>;
   using ResetCallback =
       base::OnceCallback<void(CtapDeviceResponseCode,
-                              absl::optional<pin::EmptyResponse>)>;
+                              std::optional<pin::EmptyResponse>)>;
   using GetCredentialsMetadataCallback =
       base::OnceCallback<void(CtapDeviceResponseCode,
-                              absl::optional<CredentialsMetadataResponse>)>;
+                              std::optional<CredentialsMetadataResponse>)>;
   using EnumerateCredentialsCallback = base::OnceCallback<void(
       CtapDeviceResponseCode,
-      absl::optional<std::vector<AggregatedEnumerateCredentialsResponse>>)>;
+      std::optional<std::vector<AggregatedEnumerateCredentialsResponse>>)>;
   using DeleteCredentialCallback =
       base::OnceCallback<void(CtapDeviceResponseCode,
-                              absl::optional<DeleteCredentialResponse>)>;
+                              std::optional<DeleteCredentialResponse>)>;
   using UpdateUserInformationCallback =
       base::OnceCallback<void(CtapDeviceResponseCode,
-                              absl::optional<UpdateUserInformationResponse>)>;
+                              std::optional<UpdateUserInformationResponse>)>;
   using BioEnrollmentCallback =
       base::OnceCallback<void(CtapDeviceResponseCode,
-                              absl::optional<BioEnrollmentResponse>)>;
+                              std::optional<BioEnrollmentResponse>)>;
 
   FidoAuthenticator() = default;
 
@@ -108,7 +108,7 @@
   virtual void ExcludeAppIdCredentialsBeforeMakeCredential(
       CtapMakeCredentialRequest request,
       MakeCredentialOptions options,
-      base::OnceCallback<void(CtapDeviceResponseCode, absl::optional<bool>)>);
+      base::OnceCallback<void(CtapDeviceResponseCode, std::optional<bool>)>);
 
   // Makes a FIDO credential given |request| and |options|.
   // https://drafts.fidoalliance.org/fido-2/stable-links-to-latest/fido-client-to-authenticator-protocol.html#authenticatorMakeCredential
@@ -161,7 +161,7 @@
   // must be set if |permissions| includes MakeCredential or GetAssertion.
   virtual void GetPINToken(std::string pin,
                            std::vector<pin::Permissions> permissions,
-                           absl::optional<std::string> rp_id,
+                           std::optional<std::string> rp_id,
                            GetTokenCallback callback);
   // Returns |true| if the authenticator supports GetUvToken.
   virtual bool CanGetUvToken();
@@ -171,7 +171,7 @@
   // |rp_id| must be set if the PinUvAuthToken will be used for MakeCredential
   // or GetAssertion.
   virtual void GetUvToken(std::vector<pin::Permissions> permissions,
-                          absl::optional<std::string> rp_id,
+                          std::optional<std::string> rp_id,
                           GetTokenCallback callback);
   // Returns the minimum PIN length for this authenticator's currently set PIN.
   virtual uint32_t CurrentMinPINLength();
@@ -251,7 +251,7 @@
   virtual void GetSensorInfo(BioEnrollmentCallback callback);
   virtual void BioEnrollFingerprint(
       const pin::TokenResponse&,
-      absl::optional<std::vector<uint8_t>> template_id,
+      std::optional<std::vector<uint8_t>> template_id,
       BioEnrollmentCallback);
   virtual void BioEnrollCancel(BioEnrollmentCallback);
   virtual void BioEnrollEnumerate(const pin::TokenResponse&,
@@ -273,7 +273,7 @@
   // GetAlgorithms returns the list of supported COSEAlgorithmIdentifiers, or
   // |nullopt| if this is unknown and thus all requests should be tried in case
   // they work.
-  virtual absl::optional<base::span<const int32_t>> GetAlgorithms();
+  virtual std::optional<base::span<const int32_t>> GetAlgorithms();
 
   // DiscoverableCredentialStorageFull returns true if creation of a
   // discoverable credential is likely to fail because authenticator storage is
@@ -299,7 +299,7 @@
   virtual std::string GetDisplayName() const;
   virtual ProtocolVersion SupportedProtocol() const;
   virtual const AuthenticatorSupportedOptions& Options() const = 0;
-  virtual absl::optional<FidoTransportProtocol> AuthenticatorTransport()
+  virtual std::optional<FidoTransportProtocol> AuthenticatorTransport()
       const = 0;
   virtual base::WeakPtr<FidoAuthenticator> GetWeakPtr() = 0;
 };
diff --git a/device/fido/fido_device.cc b/device/fido/fido_device.cc
index 62523397..79977654 100644
--- a/device/fido/fido_device.cc
+++ b/device/fido/fido_device.cc
@@ -47,14 +47,14 @@
 
 void FidoDevice::OnDeviceInfoReceived(
     base::OnceClosure done,
-    absl::optional<std::vector<uint8_t>> response) {
+    std::optional<std::vector<uint8_t>> response) {
   // TODO(hongjunchoi): Add tests that verify this behavior.
   if (state_ == FidoDevice::State::kDeviceError)
     return;
 
   state_ = FidoDevice::State::kReady;
-  absl::optional<AuthenticatorGetInfoResponse> get_info_response =
-      response ? ReadCTAPGetInfoResponse(*response) : absl::nullopt;
+  std::optional<AuthenticatorGetInfoResponse> get_info_response =
+      response ? ReadCTAPGetInfoResponse(*response) : std::nullopt;
   if (!get_info_response ||
       !base::Contains(get_info_response->versions, ProtocolVersion::kCtap2)) {
     supported_protocol_ = ProtocolVersion::kU2f;
diff --git a/device/fido/fido_device.h b/device/fido/fido_device.h
index ebbfd17..6464006a 100644
--- a/device/fido/fido_device.h
+++ b/device/fido/fido_device.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -16,7 +17,6 @@
 #include "device/fido/authenticator_get_info_response.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_transport_protocol.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -36,7 +36,7 @@
   static constexpr CancelToken kInvalidCancelToken = 0;
 
   using DeviceCallback =
-      base::OnceCallback<void(absl::optional<std::vector<uint8_t>>)>;
+      base::OnceCallback<void(std::optional<std::vector<uint8_t>>)>;
 
   // Internal state machine states.
   enum class State {
@@ -111,7 +111,7 @@
   }
 
   ProtocolVersion supported_protocol() const { return supported_protocol_; }
-  const absl::optional<AuthenticatorGetInfoResponse>& device_info() const {
+  const std::optional<AuthenticatorGetInfoResponse>& device_info() const {
     return device_info_;
   }
   bool is_in_error_state() const {
@@ -129,12 +129,12 @@
 
  protected:
   void OnDeviceInfoReceived(base::OnceClosure done,
-                            absl::optional<std::vector<uint8_t>> response);
+                            std::optional<std::vector<uint8_t>> response);
   void SetDeviceInfo(AuthenticatorGetInfoResponse device_info);
 
   State state_ = State::kInit;
   ProtocolVersion supported_protocol_ = ProtocolVersion::kUnknown;
-  absl::optional<AuthenticatorGetInfoResponse> device_info_;
+  std::optional<AuthenticatorGetInfoResponse> device_info_;
   // If `true`, the device needs to be sent a specific wink command to flash
   // when user presence is required.
   bool needs_explicit_wink_ = false;
diff --git a/device/fido/fido_device_authenticator.cc b/device/fido/fido_device_authenticator.cc
index d24d2fb..34a2376 100644
--- a/device/fido/fido_device_authenticator.cc
+++ b/device/fido/fido_device_authenticator.cc
@@ -762,7 +762,7 @@
     base::OnceCallback<void(CtapDeviceResponseCode, std::optional<Response>)>
         callback,
     base::OnceCallback<
-        std::optional<Response>(const absl::optional<cbor::Value>&)> parser,
+        std::optional<Response>(const std::optional<cbor::Value>&)> parser,
     bool (*string_fixup_predicate)(const std::vector<const cbor::Value*>&)) {
   DCHECK(!task_);
   DCHECK(!operation_);
diff --git a/device/fido/fido_device_authenticator.h b/device/fido/fido_device_authenticator.h
index 41d262d..b68d533 100644
--- a/device/fido/fido_device_authenticator.h
+++ b/device/fido/fido_device_authenticator.h
@@ -6,6 +6,7 @@
 #define DEVICE_FIDO_FIDO_DEVICE_AUTHENTICATOR_H_
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -22,7 +23,6 @@
 #include "device/fido/pin.h"
 #include "mojo/public/cpp/base/big_buffer.h"
 #include "services/data_decoder/public/cpp/data_decoder.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -52,7 +52,7 @@
   void ExcludeAppIdCredentialsBeforeMakeCredential(
       CtapMakeCredentialRequest request,
       MakeCredentialOptions options,
-      base::OnceCallback<void(CtapDeviceResponseCode, absl::optional<bool>)>)
+      base::OnceCallback<void(CtapDeviceResponseCode, std::optional<bool>)>)
       override;
   void MakeCredential(CtapMakeCredentialRequest request,
                       MakeCredentialOptions options,
@@ -64,12 +64,12 @@
   void GetPinRetries(GetRetriesCallback callback) override;
   void GetPINToken(std::string pin,
                    std::vector<pin::Permissions> permissions,
-                   absl::optional<std::string> rp_id,
+                   std::optional<std::string> rp_id,
                    GetTokenCallback callback) override;
   void GetUvRetries(GetRetriesCallback callback) override;
   bool CanGetUvToken() override;
   void GetUvToken(std::vector<pin::Permissions> permissions,
-                  absl::optional<std::string> rp_id,
+                  std::optional<std::string> rp_id,
                   GetTokenCallback callback) override;
   uint32_t CurrentMinPINLength() override;
   uint32_t NewMinPINLength() override;
@@ -104,7 +104,7 @@
   void GetModality(BioEnrollmentCallback callback) override;
   void GetSensorInfo(BioEnrollmentCallback callback) override;
   void BioEnrollFingerprint(const pin::TokenResponse&,
-                            absl::optional<std::vector<uint8_t>> template_id,
+                            std::optional<std::vector<uint8_t>> template_id,
                             BioEnrollmentCallback) override;
   void BioEnrollCancel(BioEnrollmentCallback) override;
   void BioEnrollEnumerate(const pin::TokenResponse&,
@@ -120,7 +120,7 @@
       const pin::TokenResponse& pin_uv_auth_token,
       base::OnceCallback<void(CtapDeviceResponseCode)> callback) override;
 
-  absl::optional<base::span<const int32_t>> GetAlgorithms() override;
+  std::optional<base::span<const int32_t>> GetAlgorithms() override;
   bool DiscoverableCredentialStorageFull() const override;
 
   void Reset(ResetCallback callback) override;
@@ -130,7 +130,7 @@
   std::string GetDisplayName() const override;
   ProtocolVersion SupportedProtocol() const override;
   const AuthenticatorSupportedOptions& Options() const override;
-  absl::optional<FidoTransportProtocol> AuthenticatorTransport() const override;
+  std::optional<FidoTransportProtocol> AuthenticatorTransport() const override;
   base::WeakPtr<FidoAuthenticator> GetWeakPtr() override;
 
   FidoDevice* device() { return device_.get(); }
@@ -139,11 +139,10 @@
  private:
   using GetEphemeralKeyCallback =
       base::OnceCallback<void(CtapDeviceResponseCode,
-                              absl::optional<pin::KeyAgreementResponse>)>;
+                              std::optional<pin::KeyAgreementResponse>)>;
   using LargeBlobReadCallback = base::OnceCallback<void(
       CtapDeviceResponseCode,
-      absl::optional<std::vector<std::pair<LargeBlobKey, LargeBlob>>>
-          callback)>;
+      std::optional<std::vector<std::pair<LargeBlobKey, LargeBlob>>> callback)>;
   void InitializeAuthenticatorDone(base::OnceClosure callback);
   void GetEphemeralKey(GetEphemeralKeyCallback callback);
   void DoGetAssertion(CtapGetAssertionRequest request,
@@ -174,31 +173,31 @@
       CtapGetAssertionOptions options,
       GetAssertionCallback callback,
       CtapDeviceResponseCode status,
-      absl::optional<pin::KeyAgreementResponse> key);
+      std::optional<pin::KeyAgreementResponse> key);
   void OnHaveEphemeralKeyForGetPINToken(
       std::string pin,
       std::vector<pin::Permissions> permissions,
-      absl::optional<std::string> rp_id,
+      std::optional<std::string> rp_id,
       GetTokenCallback callback,
       CtapDeviceResponseCode status,
-      absl::optional<pin::KeyAgreementResponse> key);
+      std::optional<pin::KeyAgreementResponse> key);
   void OnHaveEphemeralKeyForSetPIN(
       std::string pin,
       SetPINCallback callback,
       CtapDeviceResponseCode status,
-      absl::optional<pin::KeyAgreementResponse> key);
+      std::optional<pin::KeyAgreementResponse> key);
   void OnHaveEphemeralKeyForChangePIN(
       std::string old_pin,
       std::string new_pin,
       SetPINCallback callback,
       CtapDeviceResponseCode status,
-      absl::optional<pin::KeyAgreementResponse> key);
+      std::optional<pin::KeyAgreementResponse> key);
   void OnHaveEphemeralKeyForUvToken(
-      absl::optional<std::string> rp_id,
+      std::optional<std::string> rp_id,
       std::vector<pin::Permissions> permissions,
       GetTokenCallback callback,
       CtapDeviceResponseCode status,
-      absl::optional<pin::KeyAgreementResponse> key);
+      std::optional<pin::KeyAgreementResponse> key);
 
   // Attempts to read large blobs from the credential encrypted with
   // |large_blob_keys|. Returns a map of keys to their blobs.
@@ -208,24 +207,24 @@
   void FetchLargeBlobArray(
       LargeBlobArrayReader large_blob_array_reader,
       base::OnceCallback<void(CtapDeviceResponseCode,
-                              absl::optional<LargeBlobArrayReader>)> callback);
+                              std::optional<LargeBlobArrayReader>)> callback);
   void WriteLargeBlobArray(
-      absl::optional<pin::TokenResponse> pin_uv_auth_token,
+      std::optional<pin::TokenResponse> pin_uv_auth_token,
       LargeBlobArrayWriter large_blob_array_writer,
       base::OnceCallback<void(CtapDeviceResponseCode)> callback);
   void OnReadLargeBlobFragment(
       const size_t bytes_requested,
       LargeBlobArrayReader large_blob_array_reader,
       base::OnceCallback<void(CtapDeviceResponseCode,
-                              absl::optional<LargeBlobArrayReader>)> callback,
+                              std::optional<LargeBlobArrayReader>)> callback,
       CtapDeviceResponseCode status,
-      absl::optional<LargeBlobsResponse> response);
+      std::optional<LargeBlobsResponse> response);
   void OnWriteLargeBlobFragment(
       LargeBlobArrayWriter large_blob_array_writer,
-      absl::optional<pin::TokenResponse> pin_uv_auth_token,
+      std::optional<pin::TokenResponse> pin_uv_auth_token,
       base::OnceCallback<void(CtapDeviceResponseCode)> callback,
       CtapDeviceResponseCode status,
-      absl::optional<LargeBlobsResponse> response);
+      std::optional<LargeBlobsResponse> response);
   void OnWroteLargeBlobForGetAssertion(
       std::vector<AuthenticatorGetAssertionResponse> responses,
       GetAssertionCallback callback,
@@ -234,7 +233,7 @@
       std::vector<AuthenticatorGetAssertionResponse> responses,
       GetAssertionCallback callback,
       CtapDeviceResponseCode status,
-      absl::optional<std::vector<std::pair<LargeBlobKey, LargeBlob>>> blobs);
+      std::optional<std::vector<std::pair<LargeBlobKey, LargeBlob>>> blobs);
   void OnBlobUncompressed(
       std::vector<AuthenticatorGetAssertionResponse> responses,
       std::vector<std::pair<LargeBlobKey, LargeBlob>> blobs,
@@ -249,25 +248,25 @@
       const pin::TokenResponse& pin_uv_auth_token,
       base::OnceCallback<void(CtapDeviceResponseCode)> callback,
       CtapDeviceResponseCode status,
-      absl::optional<std::vector<AggregatedEnumerateCredentialsResponse>>
+      std::optional<std::vector<AggregatedEnumerateCredentialsResponse>>
           credentials);
   void OnHaveLargeBlobArrayForWrite(
       const LargeBlobKey& large_blob_key,
-      absl::optional<pin::TokenResponse> pin_uv_auth_token,
+      std::optional<pin::TokenResponse> pin_uv_auth_token,
       base::OnceCallback<void(CtapDeviceResponseCode)> callback,
       CtapDeviceResponseCode status,
-      absl::optional<LargeBlobArrayReader> large_blob_array_reader);
+      std::optional<LargeBlobArrayReader> large_blob_array_reader);
   void OnHaveLargeBlobArrayForRead(
       const std::vector<LargeBlobKey>& large_blob_keys,
       LargeBlobReadCallback callback,
       CtapDeviceResponseCode status,
-      absl::optional<LargeBlobArrayReader> large_blob_array_reader);
+      std::optional<LargeBlobArrayReader> large_blob_array_reader);
   void OnHaveLargeBlobArrayForGarbageCollect(
       std::vector<AggregatedEnumerateCredentialsResponse> credentials,
       const pin::TokenResponse& pin_uv_auth_token,
       base::OnceCallback<void(CtapDeviceResponseCode)> callback,
       CtapDeviceResponseCode status,
-      absl::optional<LargeBlobArrayReader> large_blob_array_reader);
+      std::optional<LargeBlobArrayReader> large_blob_array_reader);
 
   template <typename... Args>
   void TaskClearProxy(base::OnceCallback<void(Args...)> callback, Args... args);
@@ -281,20 +280,20 @@
   template <typename Request, typename Response>
   void RunOperation(Request request,
                     base::OnceCallback<void(CtapDeviceResponseCode,
-                                            absl::optional<Response>)> callback,
-                    base::OnceCallback<absl::optional<Response>(
-                        const absl::optional<cbor::Value>&)> parser,
+                                            std::optional<Response>)> callback,
+                    base::OnceCallback<std::optional<Response>(
+                        const std::optional<cbor::Value>&)> parser,
                     bool (*string_fixup_predicate)(
                         const std::vector<const cbor::Value*>&) = nullptr);
 
   struct EnumerateCredentialsState;
   void OnEnumerateRPsDone(EnumerateCredentialsState state,
                           CtapDeviceResponseCode status,
-                          absl::optional<EnumerateRPsResponse> response);
+                          std::optional<EnumerateRPsResponse> response);
   void OnEnumerateCredentialsDone(
       EnumerateCredentialsState state,
       CtapDeviceResponseCode status,
-      absl::optional<EnumerateCredentialsResponse> response);
+      std::optional<EnumerateCredentialsResponse> response);
 
   size_t max_large_blob_fragment_length();
 
@@ -307,12 +306,12 @@
   // The highest advertised PINUVAuthProtocol version that the authenticator
   // supports. This is guaranteed to be non-null after authenticator
   // initialization if |options_| indicates that PIN is supported.
-  absl::optional<PINUVAuthProtocol> chosen_pin_uv_auth_protocol_;
+  std::optional<PINUVAuthProtocol> chosen_pin_uv_auth_protocol_;
 
   data_decoder::DataDecoder data_decoder_;
   // large_blob_ contains a compressed largeBlob and indicates that an
   // largeBlobKey-based write will occur in a `GetAssertion` operation.
-  absl::optional<LargeBlob> large_blob_;
+  std::optional<LargeBlob> large_blob_;
   // large_blob_read_ indicates that a largeBlobKey-based read will occur
   // in a `GetAssertion` operation.
   bool large_blob_read_;
diff --git a/device/fido/fido_device_authenticator_unittest.cc b/device/fido/fido_device_authenticator_unittest.cc
index 72e13268..44755bb 100644
--- a/device/fido/fido_device_authenticator_unittest.cc
+++ b/device/fido/fido_device_authenticator_unittest.cc
@@ -5,6 +5,7 @@
 #include "device/fido/fido_device_authenticator.h"
 
 #include <memory>
+#include <optional>
 
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
@@ -25,7 +26,6 @@
 #include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -36,7 +36,7 @@
     std::vector<AuthenticatorGetAssertionResponse>>;
 using PinCallback = device::test::StatusAndValueCallbackReceiver<
     CtapDeviceResponseCode,
-    absl::optional<pin::TokenResponse>>;
+    std::optional<pin::TokenResponse>>;
 using GarbageCollectionCallback =
     device::test::ValueCallbackReceiver<CtapDeviceResponseCode>;
 using TouchCallback = device::test::TestCallbackReceiver<>;
@@ -313,7 +313,7 @@
 // key set.
 TEST_F(FidoDeviceAuthenticatorTest, TestWriteLargeBlobNoLargeBlobKey) {
   for (auto& registration : virtual_device_->mutable_state()->registrations) {
-    registration.second.large_blob_key = absl::nullopt;
+    registration.second.large_blob_key = std::nullopt;
   }
   AuthenticatorGetAssertionResponse write = GetAssertionForWrite(kSmallBlob1);
   EXPECT_FALSE(write.large_blob_written);
diff --git a/device/fido/fido_discovery_factory.cc b/device/fido/fido_discovery_factory.cc
index 08a5828..e3c7552 100644
--- a/device/fido/fido_discovery_factory.cc
+++ b/device/fido/fido_discovery_factory.cc
@@ -134,7 +134,7 @@
 void FidoDiscoveryFactory::set_cable_data(
     FidoRequestType request_type,
     std::vector<CableDiscoveryData> cable_data,
-    const absl::optional<std::array<uint8_t, cablev2::kQRKeySize>>&
+    const std::optional<std::array<uint8_t, cablev2::kQRKeySize>>&
         qr_generator_key) {
   request_type_ = request_type;
   cable_data_ = std::move(cable_data);
diff --git a/device/fido/fido_discovery_factory.h b/device/fido/fido_discovery_factory.h
index a928129e..2491c709 100644
--- a/device/fido/fido_discovery_factory.h
+++ b/device/fido/fido_discovery_factory.h
@@ -6,6 +6,7 @@
 #define DEVICE_FIDO_FIDO_DISCOVERY_FACTORY_H_
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <string_view>
 #include <vector>
@@ -27,7 +28,6 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/device/public/mojom/usb_manager.mojom.h"
 #include "services/network/public/mojom/network_context.mojom-forward.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_MAC)
 #include "device/fido/mac/authenticator_config.h"
@@ -60,7 +60,7 @@
   virtual void set_cable_data(
       FidoRequestType request_type,
       std::vector<CableDiscoveryData> cable_data,
-      const absl::optional<std::array<uint8_t, cablev2::kQRKeySize>>&
+      const std::optional<std::array<uint8_t, cablev2::kQRKeySize>>&
           qr_generator_key);
 
   // set_android_accessory_params configures values necessary for discovering
@@ -108,9 +108,9 @@
           std::unique_ptr<enclave::CredentialRequest>>> stream);
 
 #if BUILDFLAG(IS_MAC)
-  // Configures the Touch ID authenticator. Set to absl::nullopt to disable it.
+  // Configures the Touch ID authenticator. Set to std::nullopt to disable it.
   void set_mac_touch_id_info(
-      absl::optional<fido::mac::AuthenticatorConfig> mac_touch_id_config) {
+      std::optional<fido::mac::AuthenticatorConfig> mac_touch_id_config) {
     mac_touch_id_config_ = std::move(mac_touch_id_config);
   }
   // Sets the window on top of which macOS will show any iCloud Keychain UI.
@@ -164,31 +164,31 @@
       std::vector<std::unique_ptr<FidoDiscoveryBase>>& discoveries);
 
 #if BUILDFLAG(IS_MAC)
-  absl::optional<fido::mac::AuthenticatorConfig> mac_touch_id_config_;
+  std::optional<fido::mac::AuthenticatorConfig> mac_touch_id_config_;
   uintptr_t nswindow_ = 0;
 #endif  // BUILDFLAG(IS_MAC)
-  absl::optional<mojo::Remote<device::mojom::UsbDeviceManager>>
+  std::optional<mojo::Remote<device::mojom::UsbDeviceManager>>
       usb_device_manager_;
   std::string aoa_request_description_;
   raw_ptr<network::mojom::NetworkContext> network_context_ = nullptr;
-  absl::optional<std::vector<CableDiscoveryData>> cable_data_;
-  absl::optional<std::array<uint8_t, cablev2::kQRKeySize>> qr_generator_key_;
-  absl::optional<FidoRequestType> request_type_;
+  std::optional<std::vector<CableDiscoveryData>> cable_data_;
+  std::optional<std::array<uint8_t, cablev2::kQRKeySize>> qr_generator_key_;
+  std::optional<FidoRequestType> request_type_;
   std::unique_ptr<
       FidoDiscoveryBase::EventStream<std::unique_ptr<cablev2::Pairing>>>
       contact_device_stream_;
-  absl::optional<
+  std::optional<
       base::RepeatingCallback<void(std::unique_ptr<cablev2::Pairing>)>>
       cable_pairing_callback_;
-  absl::optional<
+  std::optional<
       base::RepeatingCallback<void(std::unique_ptr<cablev2::Pairing>)>>
       cable_invalidated_pairing_callback_;
-  absl::optional<base::RepeatingCallback<void(cablev2::Event)>>
+  std::optional<base::RepeatingCallback<void(cablev2::Event)>>
       cable_event_callback_;
 #if BUILDFLAG(IS_CHROMEOS)
   base::RepeatingCallback<std::string()> generate_request_id_callback_;
   bool require_legacy_cros_authenticator_ = false;
-  absl::optional<CtapGetAssertionRequest>
+  std::optional<CtapGetAssertionRequest>
       get_assertion_request_for_legacy_credential_check_;
 #endif  // BUILDFLAG(IS_CHROMEOS)
   base::flat_set<VidPid> hid_ignore_list_;
diff --git a/device/fido/fido_parsing_utils.cc b/device/fido/fido_parsing_utils.cc
index 845660c..b9a9a9e 100644
--- a/device/fido/fido_parsing_utils.cc
+++ b/device/fido/fido_parsing_utils.cc
@@ -27,11 +27,11 @@
   return std::vector<uint8_t>(span.begin(), span.end());
 }
 
-absl::optional<std::vector<uint8_t>> MaterializeOrNull(
-    absl::optional<base::span<const uint8_t>> span) {
+std::optional<std::vector<uint8_t>> MaterializeOrNull(
+    std::optional<base::span<const uint8_t>> span) {
   if (span)
     return Materialize(*span);
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 void Append(std::vector<uint8_t>* target, base::span<const uint8_t> in_values) {
diff --git a/device/fido/fido_parsing_utils.h b/device/fido/fido_parsing_utils.h
index c138c72..e28ed540 100644
--- a/device/fido/fido_parsing_utils.h
+++ b/device/fido/fido_parsing_utils.h
@@ -10,6 +10,7 @@
 
 #include <array>
 #include <iterator>
+#include <optional>
 #include <string_view>
 #include <utility>
 #include <vector>
@@ -19,7 +20,6 @@
 #include "base/ranges/algorithm.h"
 #include "components/cbor/values.h"
 #include "crypto/sha2.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 namespace fido_parsing_utils {
@@ -52,8 +52,8 @@
 COMPONENT_EXPORT(DEVICE_FIDO)
 std::vector<uint8_t> Materialize(base::span<const uint8_t> span);
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<std::vector<uint8_t>> MaterializeOrNull(
-    absl::optional<base::span<const uint8_t>> span);
+std::optional<std::vector<uint8_t>> MaterializeOrNull(
+    std::optional<base::span<const uint8_t>> span);
 
 // Returns a materialized copy of the static |span|, that is, an array with the
 // same elements.
diff --git a/device/fido/fido_parsing_utils_unittest.cc b/device/fido/fido_parsing_utils_unittest.cc
index 07125e2..b55e2d0 100644
--- a/device/fido/fido_parsing_utils_unittest.cc
+++ b/device/fido/fido_parsing_utils_unittest.cc
@@ -118,7 +118,7 @@
   ASSERT_TRUE(result.has_value());
   EXPECT_THAT(*result, ::testing::ElementsAreArray(kOneTwoThree));
 
-  EXPECT_EQ(MaterializeOrNull(absl::nullopt), absl::nullopt);
+  EXPECT_EQ(MaterializeOrNull(std::nullopt), std::nullopt);
 }
 
 TEST(U2fParsingUtils, Append) {
diff --git a/device/fido/fido_request_handler_base.h b/device/fido/fido_request_handler_base.h
index 3061878d..113b851 100644
--- a/device/fido/fido_request_handler_base.h
+++ b/device/fido/fido_request_handler_base.h
@@ -9,6 +9,7 @@
 #include <functional>
 #include <map>
 #include <memory>
+#include <optional>
 #include <set>
 #include <string>
 #include <string_view>
@@ -25,7 +26,6 @@
 #include "device/fido/fido_discovery_base.h"
 #include "device/fido/fido_transport_protocol.h"
 #include "device/fido/pin.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -182,7 +182,7 @@
     // make_credential_attachment contains the attachment preference for
     // makeCredential requests. See also `request_is_internal_only`, which isn't
     // specific to makeCredential requests.
-    absl::optional<AuthenticatorAttachment> make_credential_attachment;
+    std::optional<AuthenticatorAttachment> make_credential_attachment;
 
     // conditional_ui_treatment_ controls how conditional UI will be handled for
     // this request.
diff --git a/device/fido/fido_request_handler_unittest.cc b/device/fido/fido_request_handler_unittest.cc
index d31805f..8db757c 100644
--- a/device/fido/fido_request_handler_unittest.cc
+++ b/device/fido/fido_request_handler_unittest.cc
@@ -42,10 +42,10 @@
 
 using FakeTaskCallback =
     base::OnceCallback<void(CtapDeviceResponseCode status_code,
-                            absl::optional<std::vector<uint8_t>>)>;
+                            std::optional<std::vector<uint8_t>>)>;
 using FakeHandlerCallbackReceiver =
     test::StatusAndValuesCallbackReceiver<bool,
-                                          absl::optional<std::vector<uint8_t>>,
+                                          std::optional<std::vector<uint8_t>>,
                                           const FidoAuthenticator*>;
 
 enum class FakeTaskResponse : uint8_t {
@@ -158,8 +158,7 @@
                        weak_factory_.GetWeakPtr()));
   }
 
-  void CompletionCallback(
-      absl::optional<std::vector<uint8_t>> device_response) {
+  void CompletionCallback(std::optional<std::vector<uint8_t>> device_response) {
     DCHECK(device_response && device_response->size() == 1);
     switch (static_cast<FakeTaskResponse>(device_response->front())) {
       case FakeTaskResponse::kSuccess:
@@ -174,18 +173,18 @@
 
       case FakeTaskResponse::kOperationDenied:
         std::move(callback_).Run(
-            CtapDeviceResponseCode::kCtap2ErrOperationDenied, absl::nullopt);
+            CtapDeviceResponseCode::kCtap2ErrOperationDenied, std::nullopt);
         return;
       case FakeTaskResponse::kProcessingError:
       default:
         std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
-                                 absl::nullopt);
+                                 std::nullopt);
         return;
     }
   }
 
  private:
-  absl::optional<FidoDevice::CancelToken> token_;
+  std::optional<FidoDevice::CancelToken> token_;
   FakeTaskCallback callback_;
   base::WeakPtrFactory<FakeFidoTask> weak_factory_{this};
 };
@@ -194,7 +193,7 @@
  public:
   using CompletionCallback =
       base::OnceCallback<void(bool,
-                              absl::optional<std::vector<uint8_t>>,
+                              std::optional<std::vector<uint8_t>>,
                               const FidoAuthenticator*)>;
 
   FakeFidoRequestHandler(test::FakeFidoDiscoveryFactory* fake_discovery_factory,
@@ -236,7 +235,7 @@
 
   void HandleResponse(FidoAuthenticator* authenticator,
                       CtapDeviceResponseCode status,
-                      absl::optional<std::vector<uint8_t>> response) {
+                      std::optional<std::vector<uint8_t>> response) {
     auto* device_authenticator =
         static_cast<FidoDeviceAuthenticator*>(authenticator);
     device_authenticator->SetTaskForTesting(nullptr);
@@ -327,7 +326,7 @@
 
   auto device = std::make_unique<MockFidoDevice>();
   device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo, absl::nullopt);
+      CtapRequestCommand::kAuthenticatorGetInfo, std::nullopt);
   EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
   // Device returns success response.
   device->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
diff --git a/device/fido/fido_transport_protocol.cc b/device/fido/fido_transport_protocol.cc
index 2ff7003e..d6d7510d 100644
--- a/device/fido/fido_transport_protocol.cc
+++ b/device/fido/fido_transport_protocol.cc
@@ -13,7 +13,7 @@
 const char kHybrid[] = "hybrid";
 const char kInternal[] = "internal";
 
-absl::optional<FidoTransportProtocol> ConvertToFidoTransportProtocol(
+std::optional<FidoTransportProtocol> ConvertToFidoTransportProtocol(
     std::string_view protocol) {
   if (protocol == kUsbHumanInterfaceDevice)
     return FidoTransportProtocol::kUsbHumanInterfaceDevice;
@@ -29,7 +29,7 @@
   else if (protocol == kInternal)
     return FidoTransportProtocol::kInternal;
   else
-    return absl::nullopt;
+    return std::nullopt;
 }
 
 std::string_view ToString(FidoTransportProtocol protocol) {
diff --git a/device/fido/fido_transport_protocol.h b/device/fido/fido_transport_protocol.h
index 8463254..32a0354 100644
--- a/device/fido/fido_transport_protocol.h
+++ b/device/fido/fido_transport_protocol.h
@@ -5,11 +5,11 @@
 #ifndef DEVICE_FIDO_FIDO_TRANSPORT_PROTOCOL_H_
 #define DEVICE_FIDO_FIDO_TRANSPORT_PROTOCOL_H_
 
+#include <optional>
 #include <string_view>
 
 #include "base/component_export.h"
 #include "device/fido/fido_types.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -36,7 +36,7 @@
 extern const char kInternal[];
 
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<FidoTransportProtocol> ConvertToFidoTransportProtocol(
+std::optional<FidoTransportProtocol> ConvertToFidoTransportProtocol(
     std::string_view protocol);
 
 COMPONENT_EXPORT(DEVICE_FIDO)
diff --git a/device/fido/filter.cc b/device/fido/filter.cc
index eea1625..82b5d8e 100644
--- a/device/fido/filter.cc
+++ b/device/fido/filter.cc
@@ -4,6 +4,8 @@
 
 #include "device/fido/filter.h"
 
+#include <optional>
+
 #include "base/feature_list.h"
 #include "base/json/json_reader.h"
 #include "base/metrics/field_trial_params.h"
@@ -13,7 +15,6 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/values.h"
 #include "components/device_event_log/device_event_log.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 namespace fido_filter {
@@ -31,13 +32,13 @@
 };
 
 struct FilterStep {
-  absl::optional<std::string> operation;
+  std::optional<std::string> operation;
   std::vector<std::string> rp_id;
-  absl::optional<std::string> device;
-  absl::optional<std::string> id_type;
+  std::optional<std::string> device;
+  std::optional<std::string> id_type;
   std::vector<std::string> id;
-  absl::optional<size_t> id_min_size;
-  absl::optional<size_t> id_max_size;
+  std::optional<size_t> id_min_size;
+  std::optional<size_t> id_max_size;
   Action action;
 };
 
@@ -69,23 +70,23 @@
   return ret;
 }
 
-absl::optional<std::vector<FilterStep>> ParseJSON(std::string_view json) {
-  absl::optional<base::Value> v =
+std::optional<std::vector<FilterStep>> ParseJSON(std::string_view json) {
+  std::optional<base::Value> v =
       base::JSONReader::Read(json, base::JSON_ALLOW_TRAILING_COMMAS);
   if (!v || !v->is_dict()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   const base::Value::List* filters = v->GetDict().FindList("filters");
   if (!filters) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   std::vector<FilterStep> ret;
   for (const auto& filter : *filters) {
     const base::Value::Dict* filter_dict = filter.GetIfDict();
     if (!filter_dict) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     // These are the keys that are extracted from the JSON:
@@ -119,7 +120,7 @@
         action = &pair.second;
       } else {
         // Unknown keys are an error.
-        return absl::nullopt;
+        return std::nullopt;
       }
     }
 
@@ -132,19 +133,19 @@
         (id && !IsString(*id) && !IsListOf(id, IsString)) ||
         (id_min_size && !id_min_size->is_int()) ||
         (id_max_size && !id_max_size->is_int())) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     if ((id_min_size || id_max_size || id) && !id_type) {
       // If matches on the contents or size of an ID are given then the type
       // must also be matched.
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     if (!rp_id && !device) {
       // Filter is too broad. For safety this is disallowed, although one can
       // still explicitly use a wildcard.
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     FilterStep step;
@@ -156,7 +157,7 @@
     } else if (action_str == "no-attestation") {
       step.action = Action::NO_ATTESTATION;
     } else {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     if (operation) {
@@ -177,14 +178,14 @@
     if (id_min_size) {
       const int min_size_int = id_min_size->GetInt();
       if (min_size_int < 0) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       step.id_min_size = min_size_int;
     }
     if (id_max_size) {
       const int max_size_int = id_max_size->GetInt();
       if (max_size_int < 0) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       step.id_max_size = max_size_int;
     }
@@ -216,8 +217,8 @@
 size_t g_testing_depth = 0;
 
 struct CurrentFilter {
-  absl::optional<std::vector<FilterStep>> steps;
-  absl::optional<std::string> json;
+  std::optional<std::vector<FilterStep>> steps;
+  std::optional<std::string> json;
 };
 
 CurrentFilter* GetCurrentFilter() {
@@ -263,14 +264,14 @@
 Action Evaluate(
     Operation op,
     std::string_view rp_id,
-    absl::optional<std::string_view> device,
-    absl::optional<std::pair<IDType, base::span<const uint8_t>>> id) {
+    std::optional<std::string_view> device,
+    std::optional<std::pair<IDType, base::span<const uint8_t>>> id) {
   CurrentFilter* const current_filter = GetCurrentFilter();
   if (!current_filter->steps) {
     return Action::ALLOW;
   }
 
-  absl::optional<std::string> id_hex;
+  std::optional<std::string> id_hex;
   if (id) {
     id_hex = base::HexEncode(id->second);
   }
diff --git a/device/fido/filter.h b/device/fido/filter.h
index 3ed9b21..4a5d1ea 100644
--- a/device/fido/filter.h
+++ b/device/fido/filter.h
@@ -5,13 +5,13 @@
 #ifndef DEVICE_FIDO_FILTER_H_
 #define DEVICE_FIDO_FILTER_H_
 
+#include <optional>
 #include <string>
 #include <string_view>
 #include <utility>
 
 #include "base/component_export.h"
 #include "base/containers/span.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 namespace fido_filter {
@@ -102,11 +102,10 @@
 // Evaluate consults any configured filters and returns the result of evaluating
 // them. See above about the format of filters.
 COMPONENT_EXPORT(DEVICE_FIDO)
-Action Evaluate(
-    Operation op,
-    std::string_view rp_id,
-    absl::optional<std::string_view> device,
-    absl::optional<std::pair<IDType, base::span<const uint8_t>>> id);
+Action Evaluate(Operation op,
+                std::string_view rp_id,
+                std::optional<std::string_view> device,
+                std::optional<std::pair<IDType, base::span<const uint8_t>>> id);
 
 // ScopedFilterForTesting sets the current filter JSON for the duration of its
 // lifetime. It is a fatal error if |json| is ill-formed.
@@ -121,7 +120,7 @@
   ~ScopedFilterForTesting();
 
  private:
-  const absl::optional<std::string> previous_json_;
+  const std::optional<std::string> previous_json_;
 };
 
 // ParseForTesting returns true iff |json| is a well-formed filter.
diff --git a/device/fido/filter_unittest.cc b/device/fido/filter_unittest.cc
index 9019619..8e589d96 100644
--- a/device/fido/filter_unittest.cc
+++ b/device/fido/filter_unittest.cc
@@ -22,24 +22,24 @@
     const char* filter;
     Operation op;
     std::string_view rp_id;
-    absl::optional<std::string_view> device;
-    absl::optional<std::pair<IDType, base::span<const uint8_t>>> id;
+    std::optional<std::string_view> device;
+    std::optional<std::pair<IDType, base::span<const uint8_t>>> id;
     Action expected;
   } kTests[] = {
       {
           "",
           Operation::MAKE_CREDENTIAL,
           "example.com",
-          absl::nullopt,
-          absl::nullopt,
+          std::nullopt,
+          std::nullopt,
           Action::ALLOW,
       },
       {
           R"({"filters": []})",
           Operation::MAKE_CREDENTIAL,
           "example.com",
-          absl::nullopt,
-          absl::nullopt,
+          std::nullopt,
+          std::nullopt,
           Action::ALLOW,
       },
       {
@@ -50,8 +50,8 @@
           }]})",
           Operation::MAKE_CREDENTIAL,
           "example.com",
-          absl::nullopt,
-          absl::nullopt,
+          std::nullopt,
+          std::nullopt,
           Action::BLOCK,
       },
       {
@@ -62,8 +62,8 @@
           }]})",
           Operation::MAKE_CREDENTIAL,
           "example.com",
-          absl::nullopt,
-          absl::nullopt,
+          std::nullopt,
+          std::nullopt,
           Action::BLOCK,
       },
       {
@@ -74,8 +74,8 @@
           }]})",
           Operation::MAKE_CREDENTIAL,
           "example.com",
-          absl::nullopt,
-          absl::nullopt,
+          std::nullopt,
+          std::nullopt,
           Action::BLOCK,
       },
       {
@@ -86,8 +86,8 @@
           }]})",
           Operation::MAKE_CREDENTIAL,
           "example.com",
-          absl::nullopt,
-          absl::nullopt,
+          std::nullopt,
+          std::nullopt,
           Action::NO_ATTESTATION,
       },
       {
@@ -97,8 +97,8 @@
           }]})",
           Operation::MAKE_CREDENTIAL,
           "example.com",
-          absl::nullopt,
-          absl::nullopt,
+          std::nullopt,
+          std::nullopt,
           Action::BLOCK,
       },
       {
@@ -108,8 +108,8 @@
           }]})",
           Operation::MAKE_CREDENTIAL,
           "example.com",
-          absl::nullopt,
-          absl::nullopt,
+          std::nullopt,
+          std::nullopt,
           Action::ALLOW,
       },
       {
@@ -120,7 +120,7 @@
           Operation::MAKE_CREDENTIAL,
           "example.com",
           "usb-1234:4321",
-          absl::nullopt,
+          std::nullopt,
           Action::BLOCK,
       },
       {
@@ -131,7 +131,7 @@
           Operation::MAKE_CREDENTIAL,
           "example.com",
           "usb-0000:4321",
-          absl::nullopt,
+          std::nullopt,
           Action::ALLOW,
       },
       {
@@ -142,7 +142,7 @@
           Operation::MAKE_CREDENTIAL,
           "example.com",
           "usb-0000:4321",
-          absl::nullopt,
+          std::nullopt,
           Action::BLOCK,
       },
       {
@@ -153,7 +153,7 @@
           Operation::MAKE_CREDENTIAL,
           "example.com",
           "usb-0000:4321",
-          absl::nullopt,
+          std::nullopt,
           Action::BLOCK,
       },
       {
@@ -164,7 +164,7 @@
           }]})",
           Operation::MAKE_CREDENTIAL,
           "example.com",
-          absl::nullopt,
+          std::nullopt,
           cred_id,
           Action::BLOCK,
       },
@@ -176,7 +176,7 @@
           }]})",
           Operation::MAKE_CREDENTIAL,
           "example.com",
-          absl::nullopt,
+          std::nullopt,
           cred_id,
           Action::ALLOW,
       },
@@ -189,7 +189,7 @@
           }]})",
           Operation::MAKE_CREDENTIAL,
           "example.com",
-          absl::nullopt,
+          std::nullopt,
           cred_id,
           Action::BLOCK,
       },
@@ -202,7 +202,7 @@
           }]})",
           Operation::MAKE_CREDENTIAL,
           "example.com",
-          absl::nullopt,
+          std::nullopt,
           cred_id,
           Action::BLOCK,
       },
@@ -215,7 +215,7 @@
           }]})",
           Operation::MAKE_CREDENTIAL,
           "example.com",
-          absl::nullopt,
+          std::nullopt,
           cred_id,
           Action::BLOCK,
       },
@@ -228,7 +228,7 @@
           }]})",
           Operation::MAKE_CREDENTIAL,
           "example.com",
-          absl::nullopt,
+          std::nullopt,
           cred_id,
           Action::ALLOW,
       },
@@ -242,7 +242,7 @@
           }]})",
           Operation::MAKE_CREDENTIAL,
           "example.com",
-          absl::nullopt,
+          std::nullopt,
           cred_id,
           Action::BLOCK,
       },
@@ -257,7 +257,7 @@
           }]})",
           Operation::MAKE_CREDENTIAL,
           "example.com",
-          absl::nullopt,
+          std::nullopt,
           empty_cred_id,
           Action::BLOCK,
       },
@@ -270,8 +270,8 @@
           }]})",
           Operation::MAKE_CREDENTIAL,
           "a.com",
-          absl::nullopt,
-          absl::nullopt,
+          std::nullopt,
+          std::nullopt,
           Action::BLOCK,
       },
       {
@@ -282,8 +282,8 @@
           }]})",
           Operation::MAKE_CREDENTIAL,
           "b.com",
-          absl::nullopt,
-          absl::nullopt,
+          std::nullopt,
+          std::nullopt,
           Action::BLOCK,
       },
       {
@@ -294,8 +294,8 @@
           }]})",
           Operation::MAKE_CREDENTIAL,
           "c.com",
-          absl::nullopt,
-          absl::nullopt,
+          std::nullopt,
+          std::nullopt,
           Action::ALLOW,
       },
       // id can be a list of strings, any of which may match.
@@ -309,7 +309,7 @@
           }]})",
           Operation::MAKE_CREDENTIAL,
           "a.com",
-          absl::nullopt,
+          std::nullopt,
           cred_id,
           Action::BLOCK,
       },
@@ -328,7 +328,7 @@
           Operation::MAKE_CREDENTIAL,
           "example.com",
           "usb-1234:5678",
-          absl::nullopt,
+          std::nullopt,
           Action::BLOCK,
       },
       {
@@ -344,7 +344,7 @@
           Operation::MAKE_CREDENTIAL,
           "example.com",
           "usb-1234:1234",
-          absl::nullopt,
+          std::nullopt,
           Action::ALLOW,
       },
   };
@@ -448,11 +448,11 @@
   // Testing that nothing crashes, etc.
   ScopedFilterForTesting filter(
       "nonsense", ScopedFilterForTesting::PermitInvalidJSON::kYes);
-  ASSERT_EQ(Evaluate(Operation::GET_ASSERTION, "example.com", absl::nullopt,
-                     absl::nullopt),
+  ASSERT_EQ(Evaluate(Operation::GET_ASSERTION, "example.com", std::nullopt,
+                     std::nullopt),
             Action::ALLOW);
-  ASSERT_EQ(Evaluate(Operation::MAKE_CREDENTIAL, "example.com", absl::nullopt,
-                     absl::nullopt),
+  ASSERT_EQ(Evaluate(Operation::MAKE_CREDENTIAL, "example.com", std::nullopt,
+                     std::nullopt),
             Action::ALLOW);
 }
 
diff --git a/device/fido/get_assertion_handler_unittest.cc b/device/fido/get_assertion_handler_unittest.cc
index b39d651bf..0136f59 100644
--- a/device/fido/get_assertion_handler_unittest.cc
+++ b/device/fido/get_assertion_handler_unittest.cc
@@ -57,7 +57,7 @@
 
 using TestGetAssertionRequestCallback = test::StatusAndValuesCallbackReceiver<
     GetAssertionStatus,
-    absl::optional<std::vector<AuthenticatorGetAssertionResponse>>,
+    std::optional<std::vector<AuthenticatorGetAssertionResponse>>,
     FidoAuthenticator*>;
 
 }  // namespace
diff --git a/device/fido/get_assertion_request_handler.cc b/device/fido/get_assertion_request_handler.cc
index 98716e3..df29a32 100644
--- a/device/fido/get_assertion_request_handler.cc
+++ b/device/fido/get_assertion_request_handler.cc
@@ -65,7 +65,7 @@
   return permissions;
 }
 
-absl::optional<GetAssertionStatus> ConvertDeviceResponseCode(
+std::optional<GetAssertionStatus> ConvertDeviceResponseCode(
     CtapDeviceResponseCode device_response_code,
     AuthenticatorType auth_type) {
   switch (device_response_code) {
@@ -101,12 +101,12 @@
     // Ignore this error to avoid canceling the request without user
     // interaction.
     case CtapDeviceResponseCode::kCtap2ErrInvalidCredential:
-      return absl::nullopt;
+      return std::nullopt;
 
     // For all other errors, the authenticator will be dropped, and other
     // authenticators may continue.
     default:
-      return absl::nullopt;
+      return std::nullopt;
   }
 }
 
@@ -198,7 +198,7 @@
       return false;
     }
 
-    const absl::optional<cbor::Value>& extensions =
+    const std::optional<cbor::Value>& extensions =
         response.authenticator_data.extensions();
     if (extensions &&
         !ValidateResponseExtensions(request, options, response, *extensions)) {
@@ -319,7 +319,7 @@
 
   if (!auth_options.large_blob_type) {
     specialized_options.large_blob_read = false;
-    specialized_options.large_blob_write = absl::nullopt;
+    specialized_options.large_blob_write = std::nullopt;
   }
 
   return specialized_options;
@@ -456,7 +456,7 @@
 
   if (fido_filter::Evaluate(fido_filter::Operation::GET_ASSERTION,
                             request_.rp_id, authenticator_name,
-                            absl::nullopt) == fido_filter::Action::BLOCK) {
+                            std::nullopt) == fido_filter::Action::BLOCK) {
     FIDO_LOG(DEBUG) << "Filtered request to device " << authenticator_name;
     return;
   }
@@ -540,7 +540,7 @@
       state_ = State::kFinished;
       std::move(completion_callback_)
           .Run(GetAssertionStatus::kAuthenticatorRemovedDuringPINEntry,
-               absl::nullopt, authenticator);
+               std::nullopt, authenticator);
     }
   }
 }
@@ -596,8 +596,8 @@
 void GetAssertionRequestHandler::HavePINUVAuthTokenResultForAuthenticator(
     FidoAuthenticator* authenticator,
     AuthTokenRequester::Result result,
-    absl::optional<pin::TokenResponse> token_response) {
-  absl::optional<GetAssertionStatus> error;
+    std::optional<pin::TokenResponse> token_response) {
+  std::optional<GetAssertionStatus> error;
   switch (result) {
     case AuthTokenRequester::Result::kPreTouchUnsatisfiableRequest:
     case AuthTokenRequester::Result::kPreTouchAuthenticatorResponseInvalid:
@@ -629,7 +629,7 @@
   DCHECK_EQ(selected_authenticator_for_pin_uv_auth_token_, authenticator);
   if (error) {
     state_ = State::kFinished;
-    std::move(completion_callback_).Run(*error, absl::nullopt, authenticator);
+    std::move(completion_callback_).Run(*error, std::nullopt, authenticator);
     return;
   }
 
@@ -685,14 +685,14 @@
     if (status != CtapDeviceResponseCode::kSuccess) {
       std::move(completion_callback_)
           .Run(WinCtapDeviceResponseCodeToGetAssertionStatus(status),
-               absl::nullopt, authenticator);
+               std::nullopt, authenticator);
       return;
     }
     if (!ResponseValid(*authenticator, request, options_, responses)) {
       FIDO_LOG(ERROR) << "Failing assertion request due to bad response from "
                       << authenticator->GetDisplayName();
       std::move(completion_callback_)
-          .Run(GetAssertionStatus::kWinNotAllowedError, absl::nullopt,
+          .Run(GetAssertionStatus::kWinNotAllowedError, std::nullopt,
                authenticator);
       return;
     }
@@ -730,12 +730,12 @@
     return;
   }
 
-  const absl::optional<GetAssertionStatus> maybe_result =
+  const std::optional<GetAssertionStatus> maybe_result =
       ConvertDeviceResponseCode(status, authenticator->GetType());
   if (!maybe_result) {
     if (state_ == State::kWaitingForResponseWithToken) {
       std::move(completion_callback_)
-          .Run(GetAssertionStatus::kAuthenticatorResponseInvalid, absl::nullopt,
+          .Run(GetAssertionStatus::kAuthenticatorResponseInvalid, std::nullopt,
                authenticator);
     } else if (authenticator->GetType() == AuthenticatorType::kPhone ||
                authenticator->GetType() == AuthenticatorType::kEnclave) {
@@ -746,7 +746,7 @@
           .Run(authenticator->GetType() == AuthenticatorType::kPhone
                    ? GetAssertionStatus::kHybridTransportError
                    : GetAssertionStatus::kEnclaveError,
-               absl::nullopt, authenticator);
+               std::nullopt, authenticator);
     } else {
       FIDO_LOG(ERROR) << "Ignoring status " << static_cast<int>(status)
                       << " from " << authenticator->GetDisplayName();
@@ -761,7 +761,7 @@
     FIDO_LOG(ERROR) << "Failing assertion request due to status " << status
                     << " from " << authenticator->GetDisplayName();
     std::move(completion_callback_)
-        .Run(*maybe_result, absl::nullopt, authenticator);
+        .Run(*maybe_result, std::nullopt, authenticator);
     return;
   }
 
@@ -769,7 +769,7 @@
     FIDO_LOG(ERROR) << "Failing assertion request due to bad response from "
                     << authenticator->GetDisplayName();
     std::move(completion_callback_)
-        .Run(GetAssertionStatus::kAuthenticatorResponseInvalid, absl::nullopt,
+        .Run(GetAssertionStatus::kAuthenticatorResponseInvalid, std::nullopt,
              authenticator);
     return;
   }
@@ -798,7 +798,7 @@
   CancelActiveAuthenticators(authenticator->GetId());
   std::move(completion_callback_)
       .Run(GetAssertionStatus::kAuthenticatorMissingUserVerification,
-           absl::nullopt, authenticator);
+           std::nullopt, authenticator);
 }
 
 void GetAssertionRequestHandler::DispatchRequestWithToken(
diff --git a/device/fido/get_assertion_request_handler.h b/device/fido/get_assertion_request_handler.h
index 42d3263..5d3e4ec 100644
--- a/device/fido/get_assertion_request_handler.h
+++ b/device/fido/get_assertion_request_handler.h
@@ -6,6 +6,7 @@
 #define DEVICE_FIDO_GET_ASSERTION_REQUEST_HANDLER_H_
 
 #include <memory>
+#include <optional>
 #include <set>
 #include <vector>
 
@@ -19,7 +20,6 @@
 #include "device/fido/fido_request_handler_base.h"
 #include "device/fido/fido_transport_protocol.h"
 #include "device/fido/public_key_credential_descriptor.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
 class ElapsedTimer;
@@ -60,7 +60,7 @@
  public:
   using CompletionCallback = base::OnceCallback<void(
       GetAssertionStatus,
-      absl::optional<std::vector<AuthenticatorGetAssertionResponse>>,
+      std::optional<std::vector<AuthenticatorGetAssertionResponse>>,
       FidoAuthenticator* authenticator)>;
 
   GetAssertionRequestHandler(
@@ -114,7 +114,7 @@
   void HavePINUVAuthTokenResultForAuthenticator(
       FidoAuthenticator* authenticator,
       AuthTokenRequester::Result result,
-      absl::optional<pin::TokenResponse> response) override;
+      std::optional<pin::TokenResponse> response) override;
 
   void ObtainPINUVAuthToken(FidoAuthenticator* authenticator,
                             std::set<pin::Permissions> permissions,
@@ -154,7 +154,7 @@
   // preselected_credential_ is set when the UI invokes `PreselectAccount()`. It
   // contains the credential chosen by the user during a request prior to
   // dispatching to the authenticator.
-  absl::optional<PublicKeyCredentialDescriptor> preselected_credential_;
+  std::optional<PublicKeyCredentialDescriptor> preselected_credential_;
 
   SEQUENCE_CHECKER(my_sequence_checker_);
   base::WeakPtrFactory<GetAssertionRequestHandler> weak_factory_{this};
diff --git a/device/fido/get_assertion_task.cc b/device/fido/get_assertion_task.cc
index f322cc4..68ea3262 100644
--- a/device/fido/get_assertion_task.cc
+++ b/device/fido/get_assertion_task.cc
@@ -246,7 +246,7 @@
 void GetAssertionTask::HandleResponse(
     std::vector<PublicKeyCredentialDescriptor> allow_list,
     CtapDeviceResponseCode response_code,
-    absl::optional<AuthenticatorGetAssertionResponse> response_data) {
+    std::optional<AuthenticatorGetAssertionResponse> response_data) {
   if (canceled_) {
     return;
   }
@@ -296,13 +296,13 @@
 void GetAssertionTask::HandleNextResponse(
     uint8_t num_responses,
     CtapDeviceResponseCode response_code,
-    absl::optional<AuthenticatorGetAssertionResponse> response_data) {
+    std::optional<AuthenticatorGetAssertionResponse> response_data) {
   if (response_code != CtapDeviceResponseCode::kSuccess) {
     std::move(callback_).Run(response_code, {});
   }
 
   // Extract any hmac-secret or prf response.
-  const absl::optional<cbor::Value>& extensions_cbor =
+  const std::optional<cbor::Value>& extensions_cbor =
       response_data->authenticator_data.extensions();
   if (extensions_cbor) {
     // Parsing has already checked that |extensions_cbor| is a map.
@@ -316,7 +316,7 @@
         return LogAndFail(
             "Assertion response has both hmac-secret and prf extensions");
       }
-      absl::optional<std::vector<uint8_t>> plaintext =
+      std::optional<std::vector<uint8_t>> plaintext =
           hmac_secret_request_->Decrypt(it->second.GetBytestring());
       if (!plaintext) {
         return LogAndFail("Failed to decrypt hmac-secret extension");
@@ -345,7 +345,7 @@
 
 void GetAssertionTask::HandleResponseToSilentRequest(
     CtapDeviceResponseCode response_code,
-    absl::optional<AuthenticatorGetAssertionResponse> response_data) {
+    std::optional<AuthenticatorGetAssertionResponse> response_data) {
   DCHECK(request_.allow_list.size() > 0);
   DCHECK(allow_list_batches_.size() > 0);
   DCHECK(0 < current_allow_list_batch_ &&
@@ -417,7 +417,7 @@
 
 void GetAssertionTask::HandleDummyMakeCredentialComplete(
     CtapDeviceResponseCode response_code,
-    absl::optional<AuthenticatorMakeCredentialResponse> response_data) {
+    std::optional<AuthenticatorMakeCredentialResponse> response_data) {
   std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrNoCredentials, {});
 }
 
@@ -435,12 +435,12 @@
                                hmac_secret_request_->salts_auth,
                                // The correct PIN protocol will be inserted
                                // automatically when needed.
-                               /*pin_protocol=*/absl::nullopt);
+                               /*pin_protocol=*/std::nullopt);
 }
 
 void GetAssertionTask::MaybeRevertU2fFallbackAndInvokeCallback(
     CtapDeviceResponseCode status,
-    absl::optional<AuthenticatorGetAssertionResponse> response) {
+    std::optional<AuthenticatorGetAssertionResponse> response) {
   DCHECK_EQ(ProtocolVersion::kU2f, device()->supported_protocol());
   if (device()->device_info()) {
     // This was actually a CTAP2 device, but the protocol version was set to U2F
diff --git a/device/fido/get_assertion_task.h b/device/fido/get_assertion_task.h
index d05fc5f..835a3ee15 100644
--- a/device/fido/get_assertion_task.h
+++ b/device/fido/get_assertion_task.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "base/component_export.h"
@@ -19,7 +20,6 @@
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_task.h"
 #include "device/fido/pin.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace cbor {
 class Value;
@@ -78,33 +78,33 @@
   void HandleResponse(
       std::vector<PublicKeyCredentialDescriptor> allow_list,
       CtapDeviceResponseCode response_code,
-      absl::optional<AuthenticatorGetAssertionResponse> response_data);
+      std::optional<AuthenticatorGetAssertionResponse> response_data);
 
   // HandleNextResponse processes an assertion and requests the next one if
   // necessary.
   void HandleNextResponse(
       uint8_t num_responses,
       CtapDeviceResponseCode response_code,
-      absl::optional<AuthenticatorGetAssertionResponse> response_data);
+      std::optional<AuthenticatorGetAssertionResponse> response_data);
 
   // HandleResponseToSilentRequest is a callback to a request without user-
   // presence requested used to silently probe credentials from the allow list.
   void HandleResponseToSilentRequest(
       CtapDeviceResponseCode response_code,
-      absl::optional<AuthenticatorGetAssertionResponse> response_data);
+      std::optional<AuthenticatorGetAssertionResponse> response_data);
 
   // HandleDummyMakeCredentialComplete is the callback for the dummy credential
   // creation request that will be triggered, if needed, to get a touch.
   void HandleDummyMakeCredentialComplete(
       CtapDeviceResponseCode response_code,
-      absl::optional<AuthenticatorMakeCredentialResponse> response_data);
+      std::optional<AuthenticatorMakeCredentialResponse> response_data);
 
   void MaybeSetPRFParameters(CtapGetAssertionRequest* request,
                              const PRFInput* maybe_inputs);
 
   void MaybeRevertU2fFallbackAndInvokeCallback(
       CtapDeviceResponseCode status,
-      absl::optional<AuthenticatorGetAssertionResponse> response);
+      std::optional<AuthenticatorGetAssertionResponse> response);
 
   void LogAndFail(const char* error);
 
diff --git a/device/fido/get_assertion_task_unittest.cc b/device/fido/get_assertion_task_unittest.cc
index 1511a54b..c97e559a 100644
--- a/device/fido/get_assertion_task_unittest.cc
+++ b/device/fido/get_assertion_task_unittest.cc
@@ -140,7 +140,7 @@
 TEST_F(FidoGetAssertionTaskTest, TestIncorrectGetAssertionResponse) {
   auto device = MockFidoDevice::MakeCtap();
   device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetAssertion, absl::nullopt);
+      CtapRequestCommand::kAuthenticatorGetAssertion, std::nullopt);
 
   auto task = std::make_unique<GetAssertionTask>(
       device.get(),
diff --git a/device/fido/hid/fido_hid_device.cc b/device/fido/hid/fido_hid_device.cc
index ec08e1e..12afdcd 100644
--- a/device/fido/hid/fido_hid_device.cc
+++ b/device/fido/hid/fido_hid_device.cc
@@ -104,7 +104,7 @@
   }
 }
 
-void FidoHidDevice::Transition(absl::optional<State> next_state) {
+void FidoHidDevice::Transition(std::optional<State> next_state) {
   if (next_state) {
     state_ = *next_state;
   }
@@ -128,7 +128,7 @@
         DeviceCallback pending_cb =
             std::move(pending_transactions_.front().callback);
         pending_transactions_.pop_front();
-        std::move(pending_cb).Run(absl::nullopt);
+        std::move(pending_cb).Run(std::nullopt);
         break;
       }
 
@@ -158,7 +158,7 @@
         DeviceCallback pending_cb =
             std::move(pending_transactions_.front().callback);
         pending_transactions_.pop_front();
-        std::move(pending_cb).Run(absl::nullopt);
+        std::move(pending_cb).Run(std::nullopt);
       }
       break;
   }
@@ -230,7 +230,7 @@
 
 // ParseInitReply parses a potential reply to a U2FHID_INIT message. If the
 // reply matches the given nonce then the assigned channel ID is returned.
-absl::optional<uint32_t> FidoHidDevice::ParseInitReply(
+std::optional<uint32_t> FidoHidDevice::ParseInitReply(
     const std::vector<uint8_t>& nonce,
     const std::vector<uint8_t>& buf) {
   auto message = FidoHidMessage::CreateFromSerializedData(buf);
@@ -240,7 +240,7 @@
       // Init replies must fit in a single frame.
       !message->MessageComplete() ||
       message->cmd() != FidoHidDeviceCommand::kInit) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   auto payload = message->GetMessagePayload();
@@ -254,7 +254,7 @@
   // 16: Capabilities
   DCHECK_EQ(8u, nonce.size());
   if (payload.size() != 17 || memcmp(nonce.data(), payload.data(), 8) != 0) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   capabilities_ = payload[16];
@@ -269,7 +269,7 @@
     std::vector<uint8_t> nonce,
     bool success,
     uint8_t report_id,
-    const absl::optional<std::vector<uint8_t>>& buf) {
+    const std::optional<std::vector<uint8_t>>& buf) {
   if (state_ == State::kDeviceError) {
     return;
   }
@@ -280,7 +280,7 @@
   }
   DCHECK(buf);
 
-  absl::optional<uint32_t> maybe_channel_id = ParseInitReply(nonce, *buf);
+  std::optional<uint32_t> maybe_channel_id = ParseInitReply(nonce, *buf);
   if (!maybe_channel_id) {
     // This instance of Chromium may not be the only process communicating with
     // this HID device, but all processes will see all the messages from the
@@ -348,7 +348,7 @@
 
 void FidoHidDevice::OnRead(bool success,
                            uint8_t report_id,
-                           const absl::optional<std::vector<uint8_t>>& buf) {
+                           const std::optional<std::vector<uint8_t>>& buf) {
   if (state_ == State::kDeviceError) {
     return;
   }
@@ -408,7 +408,7 @@
     FidoHidMessage message,
     bool success,
     uint8_t report_id,
-    const absl::optional<std::vector<uint8_t>>& buf) {
+    const std::optional<std::vector<uint8_t>>& buf) {
   if (state_ == State::kDeviceError) {
     return;
   }
@@ -531,7 +531,7 @@
   pending_transactions_.emplace_back(
       FidoHidDeviceCommand::kWink, std::vector<uint8_t>(),
       base::BindOnce(
-          [](base::OnceClosure cb, absl::optional<std::vector<uint8_t>> data) {
+          [](base::OnceClosure cb, std::optional<std::vector<uint8_t>> data) {
             std::move(cb).Run();
           },
           std::move(callback)),
diff --git a/device/fido/hid/fido_hid_device.h b/device/fido/hid/fido_hid_device.h
index 544b5fa..ad8aa819 100644
--- a/device/fido/hid/fido_hid_device.h
+++ b/device/fido/hid/fido_hid_device.h
@@ -6,6 +6,7 @@
 #define DEVICE_FIDO_HID_FIDO_HID_DEVICE_H_
 
 #include <list>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -24,7 +25,6 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/device/public/mojom/hid.mojom.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -101,7 +101,7 @@
   using RefCountedHidConnection =
       base::RefCountedData<mojo::Remote<mojom::HidConnection>>;
 
-  void Transition(absl::optional<State> next_state = absl::nullopt);
+  void Transition(std::optional<State> next_state = std::nullopt);
 
   // Open a connection to this device.
   void Connect(device::mojom::HidManager::ConnectCallback callback);
@@ -109,13 +109,13 @@
   void OnInitWriteComplete(std::vector<uint8_t> nonce, bool success);
   // Ask device to allocate a unique channel id for this connection.
   void OnAllocateChannel(std::vector<uint8_t> nonce,
-                         absl::optional<FidoHidMessage> message);
-  absl::optional<uint32_t> ParseInitReply(const std::vector<uint8_t>& nonce,
-                                          const std::vector<uint8_t>& buf);
+                         std::optional<FidoHidMessage> message);
+  std::optional<uint32_t> ParseInitReply(const std::vector<uint8_t>& nonce,
+                                         const std::vector<uint8_t>& buf);
   void OnPotentialInitReply(std::vector<uint8_t> nonce,
                             bool success,
                             uint8_t report_id,
-                            const absl::optional<std::vector<uint8_t>>& buf);
+                            const std::optional<std::vector<uint8_t>>& buf);
   // Write all message packets to device, and read response if expected.
   void WriteMessage(FidoHidMessage message);
   void PacketWritten(FidoHidMessage message, bool success);
@@ -123,11 +123,11 @@
   void ReadMessage();
   void OnRead(bool success,
               uint8_t report_id,
-              const absl::optional<std::vector<uint8_t>>& buf);
+              const std::optional<std::vector<uint8_t>>& buf);
   void OnReadContinuation(FidoHidMessage message,
                           bool success,
                           uint8_t report_id,
-                          const absl::optional<std::vector<uint8_t>>& buf);
+                          const std::optional<std::vector<uint8_t>>& buf);
   void MessageReceived(FidoHidMessage message);
   void RetryAfterChannelBusy();
   void ArmTimeout();
diff --git a/device/fido/hid/fido_hid_device_unittest.cc b/device/fido/hid/fido_hid_device_unittest.cc
index 3dc7c9e..0958ad60 100644
--- a/device/fido/hid/fido_hid_device_unittest.cc
+++ b/device/fido/hid/fido_hid_device_unittest.cc
@@ -237,7 +237,7 @@
 };
 
 using TestDeviceCallbackReceiver =
-    ::device::test::ValueCallbackReceiver<absl::optional<std::vector<uint8_t>>>;
+    ::device::test::ValueCallbackReceiver<std::optional<std::vector<uint8_t>>>;
 
 }  // namespace
 
diff --git a/device/fido/hid/fido_hid_message.cc b/device/fido/hid/fido_hid_message.cc
index 4825154..12fbced 100644
--- a/device/fido/hid/fido_hid_message.cc
+++ b/device/fido/hid/fido_hid_message.cc
@@ -15,18 +15,18 @@
 namespace device {
 
 // static
-absl::optional<FidoHidMessage> FidoHidMessage::Create(
+std::optional<FidoHidMessage> FidoHidMessage::Create(
     uint32_t channel_id,
     FidoHidDeviceCommand type,
     size_t max_report_size,
     base::span<const uint8_t> data) {
   if (data.size() > kHidMaxMessageSize)
-    return absl::nullopt;
+    return std::nullopt;
 
   if (max_report_size <= kHidInitPacketHeaderSize ||
       max_report_size > kHidMaxPacketSize) {
     NOTREACHED();
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   switch (type) {
@@ -35,48 +35,48 @@
     case FidoHidDeviceCommand::kMsg:
     case FidoHidDeviceCommand::kCbor: {
       if (data.empty())
-        return absl::nullopt;
+        return std::nullopt;
       break;
     }
 
     case FidoHidDeviceCommand::kCancel:
     case FidoHidDeviceCommand::kWink: {
       if (!data.empty())
-        return absl::nullopt;
+        return std::nullopt;
       break;
     }
     case FidoHidDeviceCommand::kLock: {
       if (data.size() != 1 || data[0] > kHidMaxLockSeconds)
-        return absl::nullopt;
+        return std::nullopt;
       break;
     }
     case FidoHidDeviceCommand::kInit: {
       if (data.size() != 8)
-        return absl::nullopt;
+        return std::nullopt;
       break;
     }
     case FidoHidDeviceCommand::kKeepAlive:
     case FidoHidDeviceCommand::kError:
       if (data.size() != 1)
-        return absl::nullopt;
+        return std::nullopt;
   }
 
   return FidoHidMessage(channel_id, type, max_report_size, data);
 }
 
 // static
-absl::optional<FidoHidMessage> FidoHidMessage::CreateFromSerializedData(
+std::optional<FidoHidMessage> FidoHidMessage::CreateFromSerializedData(
     base::span<const uint8_t> serialized_data) {
   size_t remaining_size = 0;
   if (serialized_data.size() > kHidMaxPacketSize ||
       serialized_data.size() < kHidInitPacketHeaderSize)
-    return absl::nullopt;
+    return std::nullopt;
 
   auto init_packet = FidoHidInitPacket::CreateFromSerializedData(
       serialized_data, &remaining_size);
 
   if (init_packet == nullptr)
-    return absl::nullopt;
+    return std::nullopt;
 
   return FidoHidMessage(std::move(init_packet), remaining_size);
 }
diff --git a/device/fido/hid/fido_hid_message.h b/device/fido/hid/fido_hid_message.h
index 70fc2ba..86d6d9b6 100644
--- a/device/fido/hid/fido_hid_message.h
+++ b/device/fido/hid/fido_hid_message.h
@@ -9,6 +9,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "base/component_export.h"
@@ -17,7 +18,6 @@
 #include "base/containers/span.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/hid/fido_hid_packet.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -26,13 +26,13 @@
 class COMPONENT_EXPORT(DEVICE_FIDO) FidoHidMessage {
  public:
   // Static functions to create CTAP/U2F HID commands.
-  static absl::optional<FidoHidMessage> Create(uint32_t channel_id,
-                                               FidoHidDeviceCommand cmd,
-                                               size_t max_report_size,
-                                               base::span<const uint8_t> data);
+  static std::optional<FidoHidMessage> Create(uint32_t channel_id,
+                                              FidoHidDeviceCommand cmd,
+                                              size_t max_report_size,
+                                              base::span<const uint8_t> data);
 
   // Reconstruct a message from serialized message data.
-  static absl::optional<FidoHidMessage> CreateFromSerializedData(
+  static std::optional<FidoHidMessage> CreateFromSerializedData(
       base::span<const uint8_t> serialized_data);
 
   FidoHidMessage(FidoHidMessage&& that);
diff --git a/device/fido/large_blob.cc b/device/fido/large_blob.cc
index 8232fdb..b756c9d 100644
--- a/device/fido/large_blob.cc
+++ b/device/fido/large_blob.cc
@@ -117,48 +117,48 @@
 }
 
 // static
-absl::optional<LargeBlobsResponse> LargeBlobsResponse::ParseForRead(
+std::optional<LargeBlobsResponse> LargeBlobsResponse::ParseForRead(
     const size_t bytes_to_read,
-    const absl::optional<cbor::Value>& cbor_response) {
+    const std::optional<cbor::Value>& cbor_response) {
   if (!cbor_response || !cbor_response->is_map()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   const cbor::Value::MapValue& map = cbor_response->GetMap();
   auto it =
       map.find(cbor::Value(static_cast<int>(LargeBlobsResponseKey::kConfig)));
   if (it == map.end() || !it->second.is_bytestring()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   const std::vector<uint8_t>& config = it->second.GetBytestring();
   if (config.size() > bytes_to_read) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return LargeBlobsResponse(std::move(config));
 }
 
 // static
-absl::optional<LargeBlobsResponse> LargeBlobsResponse::ParseForWrite(
-    const absl::optional<cbor::Value>& cbor_response) {
+std::optional<LargeBlobsResponse> LargeBlobsResponse::ParseForWrite(
+    const std::optional<cbor::Value>& cbor_response) {
   // For writing, we expect an empty response.
   if (cbor_response) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return LargeBlobsResponse();
 }
 
 LargeBlobsResponse::LargeBlobsResponse(
-    absl::optional<std::vector<uint8_t>> config)
+    std::optional<std::vector<uint8_t>> config)
     : config_(std::move(config)) {}
 LargeBlobsResponse::LargeBlobsResponse(LargeBlobsResponse&& other) = default;
 LargeBlobsResponse& LargeBlobsResponse::operator=(LargeBlobsResponse&& other) =
     default;
 LargeBlobsResponse::~LargeBlobsResponse() = default;
 
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const LargeBlobsRequest& request) {
   cbor::Value::MapValue map;
   if (request.get_) {
@@ -185,26 +185,26 @@
 }
 
 // static.
-absl::optional<LargeBlobData> LargeBlobData::Parse(const cbor::Value& value) {
+std::optional<LargeBlobData> LargeBlobData::Parse(const cbor::Value& value) {
   if (!value.is_map()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   const cbor::Value::MapValue& map = value.GetMap();
   auto ciphertext_it =
       map.find(cbor::Value(static_cast<int>(LargeBlobDataKeys::kCiphertext)));
   if (ciphertext_it == map.end() || !ciphertext_it->second.is_bytestring()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   auto nonce_it =
       map.find(cbor::Value(static_cast<int>(LargeBlobDataKeys::kNonce)));
   if (nonce_it == map.end() || !nonce_it->second.is_bytestring() ||
       nonce_it->second.GetBytestring().size() != kLargeBlobArrayNonceLength) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   auto orig_size_it =
       map.find(cbor::Value(static_cast<int>(LargeBlobDataKeys::kOrigSize)));
   if (orig_size_it == map.end() || !orig_size_it->second.is_unsigned()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   return LargeBlobData(ciphertext_it->second.GetBytestring(),
                        base::make_span<kLargeBlobArrayNonceLength>(
@@ -236,13 +236,13 @@
          orig_size_ == other.orig_size_;
 }
 
-absl::optional<LargeBlob> LargeBlobData::Decrypt(LargeBlobKey key) const {
+std::optional<LargeBlob> LargeBlobData::Decrypt(LargeBlobKey key) const {
   crypto::Aead aead(crypto::Aead::AeadAlgorithm::AES_256_GCM);
   aead.Init(key);
-  absl::optional<std::vector<uint8_t>> compressed_data = aead.Open(
+  std::optional<std::vector<uint8_t>> compressed_data = aead.Open(
       ciphertext_, nonce_, GenerateLargeBlobAdditionalData(orig_size_));
   if (!compressed_data) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   return LargeBlob(*compressed_data, orig_size_);
 }
@@ -263,16 +263,16 @@
   bytes_.insert(bytes_.end(), fragment.begin(), fragment.end());
 }
 
-absl::optional<cbor::Value::ArrayValue> LargeBlobArrayReader::Materialize() {
+std::optional<cbor::Value::ArrayValue> LargeBlobArrayReader::Materialize() {
   if (!VerifyLargeBlobArrayIntegrity(bytes_)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   base::span<const uint8_t> cbor_bytes =
       base::span(bytes_).first(bytes_.size() - kTruncatedHashBytes);
-  absl::optional<cbor::Value> cbor = cbor::Reader::Read(cbor_bytes);
+  std::optional<cbor::Value> cbor = cbor::Reader::Read(cbor_bytes);
   if (!cbor || !cbor->is_array()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   cbor::Value::ArrayValue large_blob_array;
diff --git a/device/fido/large_blob.h b/device/fido/large_blob.h
index 076f1f817..b7fe81a 100644
--- a/device/fido/large_blob.h
+++ b/device/fido/large_blob.h
@@ -7,12 +7,12 @@
 
 #include <cstdint>
 #include <cstdlib>
+#include <optional>
 #include <vector>
 
 #include "base/component_export.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/pin.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -89,18 +89,18 @@
 
   void SetPinParam(const pin::TokenResponse& pin_uv_auth_token);
 
-  friend std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+  friend std::pair<CtapRequestCommand, std::optional<cbor::Value>>
   AsCTAPRequestValuePair(const LargeBlobsRequest& request);
 
  private:
   LargeBlobsRequest();
 
-  absl::optional<int64_t> get_;
-  absl::optional<std::vector<uint8_t>> set_;
+  std::optional<int64_t> get_;
+  std::optional<std::vector<uint8_t>> set_;
   int64_t offset_ = 0;
-  absl::optional<int64_t> length_;
-  absl::optional<std::vector<uint8_t>> pin_uv_auth_param_;
-  absl::optional<PINUVAuthProtocol> pin_uv_auth_protocol_;
+  std::optional<int64_t> length_;
+  std::optional<std::vector<uint8_t>> pin_uv_auth_param_;
+  std::optional<PINUVAuthProtocol> pin_uv_auth_protocol_;
 };
 
 class LargeBlobsResponse {
@@ -111,26 +111,26 @@
   LargeBlobsResponse& operator=(LargeBlobsResponse&&);
   ~LargeBlobsResponse();
 
-  static absl::optional<LargeBlobsResponse> ParseForRead(
+  static std::optional<LargeBlobsResponse> ParseForRead(
       size_t bytes_to_read,
-      const absl::optional<cbor::Value>& cbor_response);
-  static absl::optional<LargeBlobsResponse> ParseForWrite(
-      const absl::optional<cbor::Value>& cbor_response);
+      const std::optional<cbor::Value>& cbor_response);
+  static std::optional<LargeBlobsResponse> ParseForWrite(
+      const std::optional<cbor::Value>& cbor_response);
 
-  absl::optional<std::vector<uint8_t>> config() { return config_; }
+  std::optional<std::vector<uint8_t>> config() { return config_; }
 
  private:
   explicit LargeBlobsResponse(
-      absl::optional<std::vector<uint8_t>> config = absl::nullopt);
+      std::optional<std::vector<uint8_t>> config = std::nullopt);
 
-  absl::optional<std::vector<uint8_t>> config_;
+  std::optional<std::vector<uint8_t>> config_;
 };
 
 // Represents the large-blob map structure
 // https://drafts.fidoalliance.org/fido-2/stable-links-to-latest/fido-client-to-authenticator-protocol.html#large-blob
 class COMPONENT_EXPORT(DEVICE_FIDO) LargeBlobData {
  public:
-  static absl::optional<LargeBlobData> Parse(const cbor::Value& cbor_response);
+  static std::optional<LargeBlobData> Parse(const cbor::Value& cbor_response);
 
   LargeBlobData(LargeBlobKey key, LargeBlob large_blob);
   LargeBlobData(const LargeBlobData&) = delete;
@@ -140,7 +140,7 @@
   ~LargeBlobData();
   bool operator==(const LargeBlobData&) const;
 
-  absl::optional<LargeBlob> Decrypt(LargeBlobKey key) const;
+  std::optional<LargeBlob> Decrypt(LargeBlobKey key) const;
   cbor::Value AsCBOR() const;
 
  private:
@@ -168,7 +168,7 @@
   // Verifies the integrity of the large blob array. This should be called after
   // all fragments have been |Append|ed.
   // If successful, parses and returns the array.
-  absl::optional<cbor::Value::ArrayValue> Materialize();
+  std::optional<cbor::Value::ArrayValue> Materialize();
 
   // Returns the current size of the array fragments.
   size_t size() const { return bytes_.size(); }
@@ -207,7 +207,7 @@
   size_t offset_ = 0;
 };
 
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const LargeBlobsRequest& request);
 
 }  // namespace device
diff --git a/device/fido/large_blob_unittest.cc b/device/fido/large_blob_unittest.cc
index 0d1bd07..175cec55 100644
--- a/device/fido/large_blob_unittest.cc
+++ b/device/fido/large_blob_unittest.cc
@@ -4,9 +4,10 @@
 
 #include "device/fido/large_blob.h"
 
+#include <optional>
+
 #include "device/fido/fido_parsing_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
diff --git a/device/fido/mac/authenticator.h b/device/fido/mac/authenticator.h
index ef8f4f9..7550d2f2 100644
--- a/device/fido/mac/authenticator.h
+++ b/device/fido/mac/authenticator.h
@@ -64,7 +64,7 @@
   AuthenticatorType GetType() const override;
   std::string GetId() const override;
   const AuthenticatorSupportedOptions& Options() const override;
-  absl::optional<FidoTransportProtocol> AuthenticatorTransport() const override;
+  std::optional<FidoTransportProtocol> AuthenticatorTransport() const override;
   void GetTouch(base::OnceClosure callback) override;
   base::WeakPtr<FidoAuthenticator> GetWeakPtr() override;
 
diff --git a/device/fido/mac/authenticator.mm b/device/fido/mac/authenticator.mm
index fa31034..9680717 100644
--- a/device/fido/mac/authenticator.mm
+++ b/device/fido/mac/authenticator.mm
@@ -5,6 +5,7 @@
 #include "device/fido/mac/authenticator.h"
 
 #include <algorithm>
+#include <optional>
 
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
@@ -27,7 +28,6 @@
 #include "device/fido/mac/make_credential_operation.h"
 #include "device/fido/mac/util.h"
 #include "device/fido/public_key_credential_user_entity.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device::fido::mac {
 
@@ -57,7 +57,7 @@
     const CtapGetAssertionRequest& request,
     const CtapGetAssertionOptions& options,
     GetPlatformCredentialInfoForRequestCallback callback) {
-  absl::optional<std::list<Credential>> credentials =
+  std::optional<std::list<Credential>> credentials =
       request.allow_list.empty()
           ? credential_store_.FindResidentCredentials(request.rp_id)
           : credential_store_.FindCredentialsFromCredentialDescriptorList(
@@ -122,7 +122,7 @@
   return "TouchIdAuthenticator";
 }
 
-absl::optional<FidoTransportProtocol>
+std::optional<FidoTransportProtocol>
 TouchIdAuthenticator::AuthenticatorTransport() const {
   return FidoTransportProtocol::kInternal;
 }
diff --git a/device/fido/mac/browsing_data_deletion_unittest.mm b/device/fido/mac/browsing_data_deletion_unittest.mm
index 6824c46..bf43685 100644
--- a/device/fido/mac/browsing_data_deletion_unittest.mm
+++ b/device/fido/mac/browsing_data_deletion_unittest.mm
@@ -132,7 +132,7 @@
 
   bool MakeCredential(TouchIdAuthenticator* authenticator) {
     TestCallbackReceiver<CtapDeviceResponseCode,
-                         absl::optional<AuthenticatorMakeCredentialResponse>>
+                         std::optional<AuthenticatorMakeCredentialResponse>>
         callback_receiver;
     authenticator->MakeCredential(MakeRequest(), MakeCredentialOptions(),
                                   callback_receiver.callback());
diff --git a/device/fido/mac/credential_metadata.cc b/device/fido/mac/credential_metadata.cc
index 5b3f991..4d241dd 100644
--- a/device/fido/mac/credential_metadata.cc
+++ b/device/fido/mac/credential_metadata.cc
@@ -54,7 +54,7 @@
                             base::span<const uint8_t> plaintext,
                             base::span<const uint8_t> authenticated_data) const;
 
-  absl::optional<std::vector<uint8_t>> Unseal(
+  std::optional<std::vector<uint8_t>> Unseal(
       Algorithm alg,
       base::span<const uint8_t> nonce,
       base::span<const uint8_t> ciphertext,
@@ -63,7 +63,7 @@
   std::string HmacForStorage(std::string_view data) const;
 
  private:
-  static absl::optional<crypto::Aead::AeadAlgorithm> ToAeadAlgorithm(
+  static std::optional<crypto::Aead::AeadAlgorithm> ToAeadAlgorithm(
       Algorithm alg);
 
   // Derives an Algorithm-specific key from |secret_| to avoid using the same
@@ -90,7 +90,7 @@
   return aead.Seal(plaintext, nonce, authenticated_data);
 }
 
-absl::optional<std::vector<uint8_t>> Cryptor::Unseal(
+std::optional<std::vector<uint8_t>> Cryptor::Unseal(
     Algorithm algorithm,
     base::span<const uint8_t> nonce,
     base::span<const uint8_t> ciphertext,
@@ -115,7 +115,7 @@
 }
 
 // static
-absl::optional<crypto::Aead::AeadAlgorithm> Cryptor::ToAeadAlgorithm(
+std::optional<crypto::Aead::AeadAlgorithm> Cryptor::ToAeadAlgorithm(
     Algorithm alg) {
   switch (alg) {
     case Algorithm::kAes256Gcm:
@@ -124,7 +124,7 @@
       return crypto::Aead::AES_256_GCM_SIV;
     case Algorithm::kHmacSha256:
       NOTREACHED() << "invalid AEAD";
-      return absl::nullopt;
+      return std::nullopt;
   }
 }
 
@@ -247,7 +247,7 @@
   cbor_metadata.emplace_back(cbor::Value(metadata.is_resident));
   cbor_metadata.emplace_back(
       cbor::Value(static_cast<uint8_t>(metadata.sign_counter_type)));
-  absl::optional<std::vector<uint8_t>> pt =
+  std::optional<std::vector<uint8_t>> pt =
       cbor::Writer::Write(cbor::Value(std::move(cbor_metadata)));
   DCHECK(pt);
 
@@ -270,7 +270,7 @@
 // In these versions, the `version` field is not part of the AEAD pt. Version 0
 // also lacks the `is_resident` boolean inside the metadata (i.e. all V0
 // credentials are non-resident).
-static absl::optional<CredentialMetadata> UnsealLegacyCredentialId(
+static std::optional<CredentialMetadata> UnsealLegacyCredentialId(
     const std::string& secret,
     const std::string& rp_id,
     base::span<const uint8_t> credential_id) {
@@ -281,27 +281,27 @@
            static_cast<uint8_t>(CredentialMetadata::Version::kV0) &&
        credential_id[0] !=
            static_cast<uint8_t>(CredentialMetadata::Version::kV1))) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   auto version = static_cast<CredentialMetadata::Version>(credential_id[0]);
 
-  absl::optional<std::vector<uint8_t>> plaintext = Cryptor(secret).Unseal(
+  std::optional<std::vector<uint8_t>> plaintext = Cryptor(secret).Unseal(
       Cryptor::Algorithm::kAes256Gcm, credential_id.subspan(1, kNonceLength),
       credential_id.subspan(1 + kNonceLength), MakeAad(version, rp_id));
   if (!plaintext) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   // The recovered plaintext should decode into the CredentialMetadata struct.
-  absl::optional<cbor::Value> maybe_array = cbor::Reader::Read(*plaintext);
+  std::optional<cbor::Value> maybe_array = cbor::Reader::Read(*plaintext);
   if (!maybe_array || !maybe_array->is_array()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   const cbor::Value::ArrayValue& array = maybe_array->GetArray();
   if (array.size() < 3 || !array[0].is_bytestring() ||
       !array[1].is_bytestring() || !array[2].is_bytestring()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   auto user_id = array[0].GetBytestring();
   auto user_name = array[1].GetBytestringAsString();
@@ -311,11 +311,11 @@
   DCHECK(version == CredentialMetadata::Version::kV0 ||
          version == CredentialMetadata::Version::kV1);
   if (version == CredentialMetadata::Version::kV0 && array.size() != 3) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   if (version == CredentialMetadata::Version::kV1) {
     if (array.size() != 4 || !array[3].is_bool()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     is_resident = array[3].GetBool();
   }
@@ -334,37 +334,37 @@
 // version prefix. Since the metadata version is still part of the AEAD's
 // authenticated data, this is generally called iteratively for each potential
 // version. Returns nullopt if unsealing fails.
-static absl::optional<CredentialMetadata> UnsealV2OrLaterCredentialMetadata(
+static std::optional<CredentialMetadata> UnsealV2OrLaterCredentialMetadata(
     CredentialMetadata::Version version,
     const std::string& secret,
     const std::string& rp_id,
     base::span<const uint8_t> credential_id) {
   DCHECK_GE(version, CredentialMetadata::Version::kV2);
   if (credential_id.size() <= kNonceLength) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
-  absl::optional<std::vector<uint8_t>> plaintext = Cryptor(secret).Unseal(
+  std::optional<std::vector<uint8_t>> plaintext = Cryptor(secret).Unseal(
       Cryptor::Algorithm::kAes256Gcm, credential_id.first(kNonceLength),
       credential_id.subspan(kNonceLength), MakeAad(version, rp_id));
   if (!plaintext) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
-  absl::optional<cbor::Value> maybe_array = cbor::Reader::Read(base::make_span(
+  std::optional<cbor::Value> maybe_array = cbor::Reader::Read(base::make_span(
       reinterpret_cast<const uint8_t*>(plaintext->data()), plaintext->size()));
   if (!maybe_array || !maybe_array->is_array()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   const cbor::Value::ArrayValue& array = maybe_array->GetArray();
   if (array.size() < 4 || !array[0].is_bytestring() ||
       !array[1].is_bytestring() || !array[2].is_bytestring() ||
       !array[3].is_bool()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   if (version == CredentialMetadata::Version::kV2) {
     if (array.size() != 4) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     return CredentialMetadata(
         CredentialMetadata::Version::kV2, array[0].GetBytestring(),
@@ -381,12 +381,12 @@
       "versions");
   DCHECK_GE(version, CredentialMetadata::Version::kV3);
   if (array.size() != 5) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   // Decode SignCounter enum:
   const int64_t counter_type = array[4].GetUnsigned();
   if (counter_type < 1) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   return CredentialMetadata(version, array[0].GetBytestring(),
                             std::string(array[1].GetBytestringAsString()),
@@ -395,12 +395,12 @@
                             CredentialMetadata::SignCounter(counter_type));
 }
 
-absl::optional<CredentialMetadata> UnsealMetadataFromLegacyCredentialId(
+std::optional<CredentialMetadata> UnsealMetadataFromLegacyCredentialId(
     const std::string& secret,
     const std::string& rp_id,
     base::span<const uint8_t> credential_id) {
   // Trial decrypt under V2 first, and if that fails try again with V0/V1.
-  absl::optional<CredentialMetadata> credential_metadata =
+  std::optional<CredentialMetadata> credential_metadata =
       UnsealV2OrLaterCredentialMetadata(CredentialMetadata::Version::kV2,
                                         secret, rp_id, credential_id);
   if (credential_metadata) {
@@ -409,7 +409,7 @@
   return UnsealLegacyCredentialId(secret, rp_id, credential_id);
 }
 
-absl::optional<CredentialMetadata> UnsealMetadataFromApplicationTag(
+std::optional<CredentialMetadata> UnsealMetadataFromApplicationTag(
     const std::string& secret,
     const std::string& rp_id,
     base::span<const uint8_t> application_tag) {
@@ -423,13 +423,13 @@
   // decryption because the version is part of the AEAD authententication tag.
   for (const auto version :
        {CredentialMetadata::Version::kV3, CredentialMetadata::Version::kV4}) {
-    if (absl::optional<CredentialMetadata> metadata =
+    if (std::optional<CredentialMetadata> metadata =
             UnsealV2OrLaterCredentialMetadata(version, secret, rp_id,
                                               application_tag)) {
       return metadata;
     }
   }
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 std::string EncodeRpIdAndUserIdDeprecated(const std::string& secret,
@@ -460,18 +460,18 @@
   return base::HexEncode(ct);
 }
 
-absl::optional<std::string> DecodeRpId(const std::string& secret,
-                                       const std::string& ciphertext) {
+std::optional<std::string> DecodeRpId(const std::string& secret,
+                                      const std::string& ciphertext) {
   std::vector<uint8_t> ct;
   if (!base::HexStringToBytes(ciphertext, &ct)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   static constexpr std::array<uint8_t, kNonceLength> fixed_zero_nonce = {};
-  absl::optional<std::vector<uint8_t>> pt = Cryptor(secret).Unseal(
+  std::optional<std::vector<uint8_t>> pt = Cryptor(secret).Unseal(
       Cryptor::Algorithm::kAes256GcmSiv, fixed_zero_nonce, ct,
       /*authenticated_data=*/{});
   if (!pt) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   return std::string(pt->begin(), pt->end());
 }
@@ -507,7 +507,7 @@
   if (version > CredentialMetadata::Version::kV0) {
     cbor_metadata.emplace_back(cbor::Value(is_resident));
   }
-  absl::optional<std::vector<uint8_t>> pt =
+  std::optional<std::vector<uint8_t>> pt =
       cbor::Writer::Write(cbor::Value(std::move(cbor_metadata)));
   DCHECK(pt);
 
diff --git a/device/fido/mac/credential_metadata.h b/device/fido/mac/credential_metadata.h
index 7396ecd4..38668b6 100644
--- a/device/fido/mac/credential_metadata.h
+++ b/device/fido/mac/credential_metadata.h
@@ -9,6 +9,7 @@
 #define DEVICE_FIDO_MAC_CREDENTIAL_METADATA_H_
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -19,7 +20,6 @@
 #include "crypto/hmac.h"
 #include "crypto/symmetric_key.h"
 #include "device/fido/features.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -141,7 +141,7 @@
 // UnsealCredentialId attempts to decrypt a CredentialMetadata from a credential
 // id for version <= kV2.
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<CredentialMetadata> UnsealMetadataFromLegacyCredentialId(
+std::optional<CredentialMetadata> UnsealMetadataFromLegacyCredentialId(
     const std::string& secret,
     const std::string& rp_id,
     base::span<const uint8_t> credential_id);
@@ -149,7 +149,7 @@
 // UnsealMetadataFromApplicationTag attempts to decrypt CredentialMetadata from
 // an kSecAttrApplicationTag attribute for version >= kV3.
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<CredentialMetadata> UnsealMetadataFromApplicationTag(
+std::optional<CredentialMetadata> UnsealMetadataFromApplicationTag(
     const std::string& secret,
     const std::string& rp_id,
     base::span<const uint8_t> application_tag);
@@ -180,8 +180,8 @@
 // under the given secret without knowing the RP ID (which would be required to
 // unseal a credential ID).
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<std::string> DecodeRpId(const std::string& secret,
-                                       const std::string& ciphertext);
+std::optional<std::string> DecodeRpId(const std::string& secret,
+                                      const std::string& ciphertext);
 
 // Seals a legacy V0, V1 or V2 credential ID.
 COMPONENT_EXPORT(DEVICE_FIDO)
diff --git a/device/fido/mac/credential_store.h b/device/fido/mac/credential_store.h
index 4fd1c7c..55e9e9b 100644
--- a/device/fido/mac/credential_store.h
+++ b/device/fido/mac/credential_store.h
@@ -7,6 +7,7 @@
 
 #include <list>
 #include <memory>
+#include <optional>
 #include <set>
 #include <string>
 #include <utility>
@@ -21,7 +22,6 @@
 #include "device/fido/platform_credential_store.h"
 #include "device/fido/public_key_credential_descriptor.h"
 #include "device/fido/public_key_credential_user_entity.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if defined(__OBJC__)
 @class LAContext;
@@ -84,9 +84,9 @@
 #endif  // __OBJC__
 
   // CreateCredential inserts a new credential into the keychain. It returns
-  // the new credential and its public key, or absl::nullopt if an error
+  // the new credential and its public key, or std::nullopt if an error
   // occurred.
-  absl::optional<std::pair<Credential, base::apple::ScopedCFTypeRef<SecKeyRef>>>
+  std::optional<std::pair<Credential, base::apple::ScopedCFTypeRef<SecKeyRef>>>
   CreateCredential(const std::string& rp_id,
                    const PublicKeyCredentialUserEntity& user,
                    Discoverable discoverable) const;
@@ -94,7 +94,7 @@
   // CreateCredentialLegacyCredentialForTesting inserts a credential for an old
   // `CredentialMetadata::Version`. Such credentials can't be created anymore,
   // but they still exist and we need to be able to exercise them.
-  absl::optional<std::pair<Credential, base::apple::ScopedCFTypeRef<SecKeyRef>>>
+  std::optional<std::pair<Credential, base::apple::ScopedCFTypeRef<SecKeyRef>>>
   CreateCredentialLegacyCredentialForTesting(
       CredentialMetadata::Version version,
       const std::string& rp_id,
@@ -106,8 +106,8 @@
   // matches a credential if its transports() set is either empty or contains
   // FidoTransportProtocol::kInternal, and if its id() is the credential ID.
   // The returned credentials may be discoverable or non-discoverable. If any
-  // unexpected keychain API error occurs, absl::nullopt is returned instead.
-  absl::optional<std::list<Credential>>
+  // unexpected keychain API error occurs, std::nullopt is returned instead.
+  std::optional<std::list<Credential>>
   FindCredentialsFromCredentialDescriptorList(
       const std::string& rp_id,
       const std::vector<PublicKeyCredentialDescriptor>& descriptors) const;
@@ -115,8 +115,8 @@
   // FindResidentCredentials returns the client-side discoverable credentials
   // for the given |rp_id|. If |rp_id| is not specified, all resident
   // credentials are returned. nullopt is returned if an error occurred.
-  absl::optional<std::list<Credential>> FindResidentCredentials(
-      const absl::optional<std::string>& rp_id) const;
+  std::optional<std::list<Credential>> FindResidentCredentials(
+      const std::optional<std::string>& rp_id) const;
 
   // DeleteCredentialsForUserId deletes all credentials for the given RP and
   // user ID. Returns true if deleting succeeded or no matching credential
@@ -150,8 +150,8 @@
       std::string rp_id);
 
  private:
-  absl::optional<std::list<Credential>> FindCredentialsImpl(
-      const absl::optional<std::string>& rp_id,
+  std::optional<std::list<Credential>> FindCredentialsImpl(
+      const std::optional<std::string>& rp_id,
       const std::set<std::vector<uint8_t>>& credential_ids) const;
 
   AuthenticatorConfig config_;
diff --git a/device/fido/mac/credential_store.mm b/device/fido/mac/credential_store.mm
index 26eaea4..5d99db9 100644
--- a/device/fido/mac/credential_store.mm
+++ b/device/fido/mac/credential_store.mm
@@ -9,6 +9,8 @@
 #import <LocalAuthentication/LocalAuthentication.h>
 #import <Security/Security.h>
 
+#include <optional>
+
 #include "base/apple/bridging.h"
 #include "base/apple/foundation_util.h"
 #include "base/apple/osstatus_logging.h"
@@ -26,7 +28,6 @@
 #include "device/fido/mac/credential_metadata.h"
 #include "device/fido/mac/keychain.h"
 #include "device/fido/mac/touch_id_context.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device::fido::mac {
 
@@ -38,7 +39,7 @@
 // the query.
 base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> DefaultKeychainQuery(
     const AuthenticatorConfig& config,
-    absl::optional<std::string> rp_id) {
+    std::optional<std::string> rp_id) {
   base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> query(
       CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
                                 &kCFTypeDictionaryKeyCallBacks,
@@ -81,7 +82,7 @@
       });
 }
 
-absl::optional<std::vector<base::apple::ScopedCFTypeRef<CFDictionaryRef>>>
+std::optional<std::vector<base::apple::ScopedCFTypeRef<CFDictionaryRef>>>
 QueryKeychainItemsForProfile(const std::string& keychain_access_group,
                              const std::string& metadata_secret,
                              base::Time created_not_before,
@@ -112,11 +113,11 @@
         reinterpret_cast<CFTypeRef*>(keychain_items.InitializeInto()));
     if (status == errSecItemNotFound) {
       DVLOG(1) << "no credentials found";
-      return absl::nullopt;
+      return std::nullopt;
     }
     if (status != errSecSuccess) {
       OSSTATUS_DLOG(ERROR, status) << "SecItemCopyMatching failed";
-      return absl::nullopt;
+      return std::nullopt;
     }
   }
 
@@ -125,7 +126,7 @@
         CFArrayGetValueAtIndex(keychain_items.get(), i));
     if (!attributes) {
       DLOG(ERROR) << "unexpected result type";
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     // Skip items that don't belong to the correct keychain access group
@@ -149,7 +150,7 @@
       DLOG(ERROR) << "missing label";
       continue;
     }
-    absl::optional<std::string> opt_rp_id =
+    std::optional<std::string> opt_rp_id =
         DecodeRpId(metadata_secret, base::SysCFStringRefToUTF8(sec_attr_label));
     if (!opt_rp_id) {
       DVLOG(1) << "key doesn't belong to this profile";
@@ -215,7 +216,7 @@
   objc_storage_->authentication_context = authentication_context;
 }
 
-absl::optional<std::pair<Credential, base::apple::ScopedCFTypeRef<SecKeyRef>>>
+std::optional<std::pair<Credential, base::apple::ScopedCFTypeRef<SecKeyRef>>>
 TouchIdCredentialStore::CreateCredential(
     const std::string& rp_id,
     const PublicKeyCredentialUserEntity& user,
@@ -281,13 +282,13 @@
                                                  cferr.InitializeInto());
   if (!private_key) {
     FIDO_LOG(ERROR) << "SecKeyCreateRandomKey failed: " << cferr.get();
-    return absl::nullopt;
+    return std::nullopt;
   }
   base::apple::ScopedCFTypeRef<SecKeyRef> public_key(
       Keychain::GetInstance().KeyCopyPublicKey(private_key.get()));
   if (!public_key) {
     FIDO_LOG(ERROR) << "SecKeyCopyPublicKey failed";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return std::make_pair(
@@ -296,7 +297,7 @@
       std::move(public_key));
 }
 
-absl::optional<std::pair<Credential, base::apple::ScopedCFTypeRef<SecKeyRef>>>
+std::optional<std::pair<Credential, base::apple::ScopedCFTypeRef<SecKeyRef>>>
 TouchIdCredentialStore::CreateCredentialLegacyCredentialForTesting(
     CredentialMetadata::Version version,
     const std::string& rp_id,
@@ -309,7 +310,7 @@
   std::vector<uint8_t> credential_id = SealLegacyCredentialIdForTestingOnly(
       version, config_.metadata_secret, rp_id, user.id, user.name.value_or(""),
       user.display_name.value_or(""), is_discoverable);
-  absl::optional<CredentialMetadata> metadata =
+  std::optional<CredentialMetadata> metadata =
       UnsealMetadataFromLegacyCredentialId(config_.metadata_secret, rp_id,
                                            credential_id);
   DCHECK(metadata);
@@ -371,13 +372,13 @@
                                                  cferr.InitializeInto());
   if (!private_key) {
     FIDO_LOG(ERROR) << "SecKeyCreateRandomKey failed: " << cferr.get();
-    return absl::nullopt;
+    return std::nullopt;
   }
   base::apple::ScopedCFTypeRef<SecKeyRef> public_key(
       Keychain::GetInstance().KeyCopyPublicKey(private_key.get()));
   if (!public_key) {
     FIDO_LOG(ERROR) << "SecKeyCopyPublicKey failed";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return std::make_pair(
@@ -386,7 +387,7 @@
       std::move(public_key));
 }
 
-absl::optional<std::list<Credential>>
+std::optional<std::list<Credential>>
 TouchIdCredentialStore::FindCredentialsFromCredentialDescriptorList(
     const std::string& rp_id,
     const std::vector<PublicKeyCredentialDescriptor>& descriptors) const {
@@ -407,13 +408,13 @@
   return FindCredentialsImpl(rp_id, credential_ids);
 }
 
-absl::optional<std::list<Credential>>
+std::optional<std::list<Credential>>
 TouchIdCredentialStore::FindResidentCredentials(
-    const absl::optional<std::string>& rp_id) const {
-  absl::optional<std::list<Credential>> credentials =
+    const std::optional<std::string>& rp_id) const {
+  std::optional<std::list<Credential>> credentials =
       FindCredentialsImpl(rp_id, /*credential_ids=*/{});
   if (!credentials) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   credentials->remove_if([](const Credential& credential) {
     return !credential.metadata.is_resident;
@@ -424,7 +425,7 @@
 bool TouchIdCredentialStore::DeleteCredentialsForUserId(
     const std::string& rp_id,
     const std::vector<uint8_t>& user_id) const {
-  absl::optional<std::list<Credential>> credentials =
+  std::optional<std::list<Credential>> credentials =
       FindCredentialsImpl(rp_id, /*credential_ids=*/{});
   if (!credentials) {
     return false;
@@ -458,7 +459,7 @@
 bool TouchIdCredentialStore::DeleteCredentialsSync(
     base::Time created_not_before,
     base::Time created_not_after) {
-  absl::optional<std::vector<base::apple::ScopedCFTypeRef<CFDictionaryRef>>>
+  std::optional<std::vector<base::apple::ScopedCFTypeRef<CFDictionaryRef>>>
       keychain_items = QueryKeychainItemsForProfile(
           config_.keychain_access_group, config_.metadata_secret,
           created_not_before, created_not_after);
@@ -490,7 +491,7 @@
 size_t TouchIdCredentialStore::CountCredentialsSync(
     base::Time created_not_before,
     base::Time created_not_after) {
-  absl::optional<std::vector<base::apple::ScopedCFTypeRef<CFDictionaryRef>>>
+  std::optional<std::vector<base::apple::ScopedCFTypeRef<CFDictionaryRef>>>
       keychain_items = QueryKeychainItemsForProfile(
           config_.keychain_access_group, config_.metadata_secret,
           created_not_before, created_not_after);
@@ -506,7 +507,7 @@
     AuthenticatorConfig config,
     std::string rp_id) {
   TouchIdCredentialStore store(std::move(config));
-  absl::optional<std::list<Credential>> credentials =
+  std::optional<std::list<Credential>> credentials =
       store.FindCredentialsImpl(rp_id, /*credential_ids=*/{});
   DCHECK(credentials) << "FindCredentialsImpl shouldn't fail in tests";
   std::vector<Credential> result;
@@ -516,9 +517,9 @@
   return result;
 }
 
-absl::optional<std::list<Credential>>
+std::optional<std::list<Credential>>
 TouchIdCredentialStore::FindCredentialsImpl(
-    const absl::optional<std::string>& rp_id,
+    const std::optional<std::string>& rp_id,
     const std::set<std::vector<uint8_t>>& credential_ids) const {
   // Query all credentials for the RP. Filtering for `rp_id` here ensures we
   // don't retrieve credentials for other profiles, because their
@@ -544,7 +545,7 @@
   if (status != errSecSuccess) {
     FIDO_LOG(ERROR) << "SecItemCopyMatching failed: "
                     << logging::DescriptionFromOSStatus(status);
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   // Filter credentials for the RP down to |credential_ids|, unless it's
@@ -555,7 +556,7 @@
         CFArrayGetValueAtIndex(keychain_items.get(), i));
     if (!attributes) {
       FIDO_LOG(ERROR) << "credential with missing attributes";
-      return absl::nullopt;
+      return std::nullopt;
     }
     // Skip items that don't belong to the correct keychain access group
     // because the kSecAttrAccessGroup filter is broken.
@@ -574,7 +575,7 @@
         FIDO_LOG(ERROR) << "credential with missing kSecAttrLabel_data";
         continue;
       }
-      absl::optional<std::string> opt_rp_id = DecodeRpId(
+      std::optional<std::string> opt_rp_id = DecodeRpId(
           config_.metadata_secret, base::SysCFStringRefToUTF8(sec_attr_label));
       if (!opt_rp_id) {
         FIDO_LOG(ERROR) << "could not decode RP ID";
@@ -589,7 +590,7 @@
             attributes, kSecAttrApplicationLabel);
     if (!application_label) {
       FIDO_LOG(ERROR) << "credential with missing application label";
-      return absl::nullopt;
+      return std::nullopt;
     }
     std::vector<uint8_t> credential_id(CFDataGetBytePtr(application_label),
                                        CFDataGetBytePtr(application_label) +
@@ -601,7 +602,7 @@
 
     // Decode `CredentialMetadata` from the `kSecAttrApplicationTag` attribute
     // for V3 credentials, or from the credential ID for version <= 2.
-    absl::optional<CredentialMetadata> metadata;
+    std::optional<CredentialMetadata> metadata;
     CFDataRef application_tag_ref =
         base::apple::GetValueFromDictionary<CFDataRef>(attributes,
                                                        kSecAttrApplicationTag);
@@ -620,14 +621,14 @@
     }
     if (!metadata) {
       FIDO_LOG(ERROR) << "credential with invalid metadata";
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     SecKeyRef key = base::apple::GetValueFromDictionary<SecKeyRef>(
         attributes, kSecValueRef);
     if (!key) {
       FIDO_LOG(ERROR) << "credential with missing value ref";
-      return absl::nullopt;
+      return std::nullopt;
     }
     base::apple::ScopedCFTypeRef<SecKeyRef> private_key(
         key, base::scoped_policy::RETAIN);
@@ -683,8 +684,8 @@
   std::vector<uint8_t> credential_id =
       fido_parsing_utils::Materialize(credential_id_span);
 
-  absl::optional<std::list<Credential>> credentials = FindCredentialsImpl(
-      /*rp_id=*/absl::nullopt, {credential_id});
+  std::optional<std::list<Credential>> credentials = FindCredentialsImpl(
+      /*rp_id=*/std::nullopt, {credential_id});
   if (!credentials) {
     FIDO_LOG(ERROR) << "no credentials found";
     return false;
diff --git a/device/fido/mac/credential_store_unittest.mm b/device/fido/mac/credential_store_unittest.mm
index 180ad6ad..19fb60b 100644
--- a/device/fido/mac/credential_store_unittest.mm
+++ b/device/fido/mac/credential_store_unittest.mm
@@ -2,17 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "device/fido/mac/credential_store.h"
+
 #include <Foundation/Foundation.h>
 #include <Security/Security.h>
 
+#include <optional>
+
 #include "base/apple/foundation_util.h"
 #include "device/fido/mac/authenticator_config.h"
-#include "device/fido/mac/credential_store.h"
 #include "device/fido/mac/fake_keychain.h"
 #include "device/fido/public_key_credential_user_entity.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device::fido::mac {
 namespace {
@@ -94,7 +96,7 @@
 TEST_F(CredentialStoreTest, FindCredentialsFromCredentialDescriptorList_Basic) {
   std::vector<Credential> credentials = InsertCredentials(3);
   InsertCredentialsForRp("foo.com", 3);
-  absl::optional<std::list<Credential>> found =
+  std::optional<std::list<Credential>> found =
       store_.FindCredentialsFromCredentialDescriptorList(
           kRpId, AsDescriptors(credentials));
   ASSERT_TRUE(found);
@@ -116,7 +118,7 @@
 TEST_F(CredentialStoreTest,
        FindCredentialsFromCredentialDescriptorList_ReturnEmpty) {
   std::vector<Credential> credentials = InsertCredentials(3);
-  absl::optional<std::list<Credential>> found =
+  std::optional<std::list<Credential>> found =
       store_.FindCredentialsFromCredentialDescriptorList(
           kRpId, std::vector<PublicKeyCredentialDescriptor>());
   EXPECT_TRUE(found && found->empty());
@@ -135,7 +137,7 @@
         /*user_id=*/std::vector<uint8_t>({static_cast<uint8_t>(version)})));
   }
 
-  absl::optional<std::list<Credential>> found =
+  std::optional<std::list<Credential>> found =
       store_.FindCredentialsFromCredentialDescriptorList(
           kRpId, AsDescriptors(credentials));
   ASSERT_TRUE(found);
@@ -146,7 +148,7 @@
 TEST_F(CredentialStoreTest, FindResidentCredentials) {
   ASSERT_TRUE(store_.CreateCredential(
       kRpId, kUser, TouchIdCredentialStore::kNonDiscoverable));
-  absl::optional<std::list<Credential>> found =
+  std::optional<std::list<Credential>> found =
       store_.FindResidentCredentials(kRpId);
   ASSERT_TRUE(found);
   EXPECT_EQ(found->size(), 0u);
@@ -161,7 +163,7 @@
   auto credential = store_.CreateCredential(
       kRpId, kUser, TouchIdCredentialStore::kNonDiscoverable);
   ASSERT_TRUE(credential);
-  absl::optional<std::list<Credential>> found =
+  std::optional<std::list<Credential>> found =
       store_.FindResidentCredentials(kRpId);
   ASSERT_TRUE(found);
   EXPECT_EQ(found->size(), 0u);
diff --git a/device/fido/mac/fake_icloud_keychain_sys.h b/device/fido/mac/fake_icloud_keychain_sys.h
index 5ba6692..f78bf8c4 100644
--- a/device/fido/mac/fake_icloud_keychain_sys.h
+++ b/device/fido/mac/fake_icloud_keychain_sys.h
@@ -9,12 +9,12 @@
 #error "This header is only for Objective C++ compilation units"
 #endif
 
+#include <optional>
 #include <vector>
 
 #include "base/memory/ref_counted.h"
 #include "device/fido/discoverable_credential_metadata.h"
 #include "device/fido/mac/icloud_keychain_sys.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 @class NSWindow;
 
@@ -94,18 +94,17 @@
   ~FakeSystemInterface() override;
 
   AuthState auth_state_ = kAuthAuthorized;
-  absl::optional<AuthState> next_auth_state_;
+  std::optional<AuthState> next_auth_state_;
 
-  absl::optional<int> make_credential_error_code_;
-  absl::optional<std::vector<uint8_t>>
-      make_credential_attestation_object_bytes_;
-  absl::optional<std::vector<uint8_t>> make_credential_credential_id_;
+  std::optional<int> make_credential_error_code_;
+  std::optional<std::vector<uint8_t>> make_credential_attestation_object_bytes_;
+  std::optional<std::vector<uint8_t>> make_credential_credential_id_;
 
-  absl::optional<std::pair<int, std::string>> get_assertion_error_;
-  absl::optional<std::vector<uint8_t>> get_assertion_authenticator_data_;
-  absl::optional<std::vector<uint8_t>> get_assertion_signature_;
-  absl::optional<std::vector<uint8_t>> get_assertion_user_id_;
-  absl::optional<std::vector<uint8_t>> get_assertion_credential_id_;
+  std::optional<std::pair<int, std::string>> get_assertion_error_;
+  std::optional<std::vector<uint8_t>> get_assertion_authenticator_data_;
+  std::optional<std::vector<uint8_t>> get_assertion_signature_;
+  std::optional<std::vector<uint8_t>> get_assertion_user_id_;
+  std::optional<std::vector<uint8_t>> get_assertion_credential_id_;
 
   unsigned cancel_count_ = 0;
 
diff --git a/device/fido/mac/get_assertion_operation.h b/device/fido/mac/get_assertion_operation.h
index 05c3b86..958e7d4 100644
--- a/device/fido/mac/get_assertion_operation.h
+++ b/device/fido/mac/get_assertion_operation.h
@@ -48,7 +48,7 @@
  private:
   void PromptTouchIdDone(bool success);
   void GenerateResponses(std::list<Credential> credentials, bool has_uv);
-  absl::optional<AuthenticatorGetAssertionResponse> ResponseForCredential(
+  std::optional<AuthenticatorGetAssertionResponse> ResponseForCredential(
       const Credential& credential,
       bool has_uv);
 
diff --git a/device/fido/mac/get_assertion_operation.mm b/device/fido/mac/get_assertion_operation.mm
index c1e9242..8bd5abd 100644
--- a/device/fido/mac/get_assertion_operation.mm
+++ b/device/fido/mac/get_assertion_operation.mm
@@ -42,7 +42,7 @@
 
 void GetAssertionOperation::Run() {
   const bool empty_allow_list = request_.allow_list.empty();
-  absl::optional<std::list<Credential>> credentials =
+  std::optional<std::list<Credential>> credentials =
       empty_allow_list
           ? credential_store_->FindResidentCredentials(request_.rp_id)
           : credential_store_->FindCredentialsFromCredentialDescriptorList(
@@ -96,7 +96,7 @@
   credential_store_->SetAuthenticationContext(
       touch_id_context_->authentication_context());
 
-  absl::optional<std::list<Credential>> credentials =
+  std::optional<std::list<Credential>> credentials =
       request_.allow_list.empty()
           ? credential_store_->FindResidentCredentials(request_.rp_id)
           : credential_store_->FindCredentialsFromCredentialDescriptorList(
@@ -121,7 +121,7 @@
 
   std::vector<AuthenticatorGetAssertionResponse> responses;
   for (const Credential& credential : credentials) {
-    absl::optional<AuthenticatorGetAssertionResponse> response =
+    std::optional<AuthenticatorGetAssertionResponse> response =
         ResponseForCredential(credential, has_uv);
     if (!response) {
       FIDO_LOG(ERROR) << "Could not generate response for credential, skipping";
@@ -139,18 +139,18 @@
                            std::move(responses));
 }
 
-absl::optional<AuthenticatorGetAssertionResponse>
+std::optional<AuthenticatorGetAssertionResponse>
 GetAssertionOperation::ResponseForCredential(const Credential& credential,
                                              bool has_uv) {
   AuthenticatorData authenticator_data = MakeAuthenticatorData(
       credential.metadata.sign_counter_type, request_.rp_id,
-      /*attested_credential_data=*/absl::nullopt, has_uv);
-  absl::optional<std::vector<uint8_t>> signature =
+      /*attested_credential_data=*/std::nullopt, has_uv);
+  std::optional<std::vector<uint8_t>> signature =
       GenerateSignature(authenticator_data, request_.client_data_hash,
                         credential.private_key.get());
   if (!signature) {
     FIDO_LOG(ERROR) << "GenerateSignature failed";
-    return absl::nullopt;
+    return std::nullopt;
   }
   AuthenticatorGetAssertionResponse response(std::move(authenticator_data),
                                              std::move(*signature),
diff --git a/device/fido/mac/get_assertion_operation_unittest_mac.mm b/device/fido/mac/get_assertion_operation_unittest_mac.mm
index 50750e67..ed259be 100644
--- a/device/fido/mac/get_assertion_operation_unittest_mac.mm
+++ b/device/fido/mac/get_assertion_operation_unittest_mac.mm
@@ -36,7 +36,7 @@
 
 bool MakeCredential() {
   TestCallbackReceiver<CtapDeviceResponseCode,
-                       absl::optional<AuthenticatorMakeCredentialResponse>>
+                       std::optional<AuthenticatorMakeCredentialResponse>>
       callback_receiver;
   auto request = CtapMakeCredentialRequest(
       test_data::kClientDataJson, PublicKeyCredentialRpEntity(kRpId),
diff --git a/device/fido/mac/icloud_keychain.mm b/device/fido/mac/icloud_keychain.mm
index 9dec5b0..925bd3f 100644
--- a/device/fido/mac/icloud_keychain.mm
+++ b/device/fido/mac/icloud_keychain.mm
@@ -7,6 +7,8 @@
 #import <AuthenticationServices/AuthenticationServices.h>
 #import <Foundation/Foundation.h>
 
+#include <optional>
+
 #include "base/containers/contains.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
@@ -33,7 +35,6 @@
 #include "device/fido/fido_parsing_utils.h"
 #include "device/fido/fido_transport_protocol.h"
 #include "device/fido/mac/icloud_keychain_sys.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device::fido::icloud_keychain {
 
@@ -231,7 +232,7 @@
                              ToVector(cred.userHandle), cred.name.UTF8String,
                              /* iCloud Keychain does not store
                                 a displayName for passkeys */
-                             absl::nullopt));
+                             std::nullopt));
       }
       const auto has_credentials =
           ret.empty() ? FidoRequestHandlerBase::RecognizedCredential::
@@ -264,8 +265,7 @@
     return *options;
   }
 
-  absl::optional<FidoTransportProtocol> AuthenticatorTransport()
-      const override {
+  std::optional<FidoTransportProtocol> AuthenticatorTransport() const override {
     return FidoTransportProtocol::kInternal;
   }
 
@@ -293,14 +293,14 @@
                       << " msg: " << error.localizedDescription.UTF8String;
       if (domain == "WKErrorDomain" && error.code == 8) {
         std::move(callback).Run(
-            CtapDeviceResponseCode::kCtap2ErrCredentialExcluded, absl::nullopt);
+            CtapDeviceResponseCode::kCtap2ErrCredentialExcluded, std::nullopt);
       } else {
         // All other errors are currently mapped to `kCtap2ErrOperationDenied`
         // because it's not obvious that we want to differentiate them:
         // https://developer.apple.com/documentation/authenticationservices/asauthorizationerror?language=objc
         //
         std::move(callback).Run(
-            CtapDeviceResponseCode::kCtap2ErrOperationDenied, absl::nullopt);
+            CtapDeviceResponseCode::kCtap2ErrOperationDenied, std::nullopt);
       }
       return;
     }
@@ -313,21 +313,21 @@
         (id<ASAuthorizationPublicKeyCredentialRegistration>)
             authorization.credential;
 
-    absl::optional<cbor::Value> attestation_object_value =
+    std::optional<cbor::Value> attestation_object_value =
         cbor::Reader::Read(ToSpan(result.rawAttestationObject));
     if (!attestation_object_value || !attestation_object_value->is_map()) {
       FIDO_LOG(ERROR) << "iCKC: failed to parse attestation CBOR";
       std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrOther,
-                              absl::nullopt);
+                              std::nullopt);
       return;
     }
 
-    absl::optional<AttestationObject> attestation_object =
+    std::optional<AttestationObject> attestation_object =
         AttestationObject::Parse(*attestation_object_value);
     if (!attestation_object) {
       FIDO_LOG(ERROR) << "iCKC: failed to parse attestation object";
       std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrOther,
-                              absl::nullopt);
+                              std::nullopt);
       return;
     }
 
@@ -342,7 +342,7 @@
                       << base::HexEncode(credential_id_from_auth_data) << " vs "
                       << base::HexEncode(credential_id);
       std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrOther,
-                              absl::nullopt);
+                              std::nullopt);
       return;
     }
 
@@ -398,7 +398,7 @@
         (id<ASAuthorizationPublicKeyCredentialAssertion>)
             authorization.credential;
 
-    absl::optional<AuthenticatorData> authenticator_data =
+    std::optional<AuthenticatorData> authenticator_data =
         AuthenticatorData::DecodeAuthenticatorData(
             ToSpan(result.rawAuthenticatorData));
     if (!authenticator_data) {
diff --git a/device/fido/mac/icloud_keychain_sys.mm b/device/fido/mac/icloud_keychain_sys.mm
index 9044707..82c3a9c0 100644
--- a/device/fido/mac/icloud_keychain_sys.mm
+++ b/device/fido/mac/icloud_keychain_sys.mm
@@ -90,7 +90,7 @@
 @end
 
 @implementation ICloudKeychainCreateController {
-  absl::optional<device::CtapMakeCredentialRequest> request_;
+  std::optional<device::CtapMakeCredentialRequest> request_;
 }
 
 - (void)setRequest:(device::CtapMakeCredentialRequest)request {
@@ -156,7 +156,7 @@
 @end
 
 @implementation ICloudKeychainGetController {
-  absl::optional<device::CtapGetAssertionRequest> request_;
+  std::optional<device::CtapGetAssertionRequest> request_;
 }
 
 - (void)setRequest:(device::CtapGetAssertionRequest)request {
diff --git a/device/fido/mac/icloud_keychain_unittest.mm b/device/fido/mac/icloud_keychain_unittest.mm
index 025edc03..2010b34 100644
--- a/device/fido/mac/icloud_keychain_unittest.mm
+++ b/device/fido/mac/icloud_keychain_unittest.mm
@@ -4,6 +4,8 @@
 
 #include "device/fido/mac/icloud_keychain.h"
 
+#include <optional>
+
 #include "base/logging.h"
 #include "base/memory/raw_ptr.h"
 #include "base/metrics/histogram_base.h"
@@ -18,7 +20,6 @@
 #include "device/fido/mac/fake_icloud_keychain_sys.h"
 #include "device/fido/test_callback_receiver.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device::fido::icloud_keychain {
 
@@ -203,7 +204,7 @@
         if (is_make_credential) {
           test::TestCallbackReceiver<
               CtapDeviceResponseCode,
-              absl::optional<AuthenticatorMakeCredentialResponse>>
+              std::optional<AuthenticatorMakeCredentialResponse>>
               callback;
           authenticator_->MakeCredential(make_credential_request,
                                          make_credential_options,
@@ -242,10 +243,10 @@
 
     auto make_credential = [this, &request, &options]()
         -> std::tuple<CtapDeviceResponseCode,
-                      absl::optional<AuthenticatorMakeCredentialResponse>> {
+                      std::optional<AuthenticatorMakeCredentialResponse>> {
       test::TestCallbackReceiver<
           CtapDeviceResponseCode,
-          absl::optional<AuthenticatorMakeCredentialResponse>>
+          std::optional<AuthenticatorMakeCredentialResponse>>
           callback;
       authenticator_->MakeCredential(request, options, callback.callback());
       callback.WaitForCallback();
@@ -496,7 +497,7 @@
         {AuthenticatorType::kICloudKeychain,
          "example.com",
          {1, 2, 3, 4},
-         {{4, 3, 2, 1}, "name", absl::nullopt}}};
+         {{4, 3, 2, 1}, "name", std::nullopt}}};
     fake_->SetCredentials(creds);
     test::TestCallbackReceiver<std::vector<DiscoverableCredentialMetadata>,
                                FidoRequestHandlerBase::RecognizedCredential>
@@ -523,11 +524,11 @@
         {AuthenticatorType::kICloudKeychain,
          "example.com",
          {1, 2, 3, 4},
-         {{4, 3, 2, 1}, "name", absl::nullopt}},
+         {{4, 3, 2, 1}, "name", std::nullopt}},
         {AuthenticatorType::kICloudKeychain,
          "example.com",
          {1, 2, 3, 5},
-         {{4, 3, 2, 2}, "name", absl::nullopt}},
+         {{4, 3, 2, 2}, "name", std::nullopt}},
     };
     fake_->SetCredentials(creds);
     test::TestCallbackReceiver<std::vector<DiscoverableCredentialMetadata>,
diff --git a/device/fido/mac/make_credential_operation.h b/device/fido/mac/make_credential_operation.h
index 03a995f..4266bcd 100644
--- a/device/fido/mac/make_credential_operation.h
+++ b/device/fido/mac/make_credential_operation.h
@@ -27,7 +27,7 @@
  public:
   using Callback = base::OnceCallback<void(
       CtapDeviceResponseCode,
-      absl::optional<AuthenticatorMakeCredentialResponse>)>;
+      std::optional<AuthenticatorMakeCredentialResponse>)>;
 
   MakeCredentialOperation(CtapMakeCredentialRequest request,
                           TouchIdCredentialStore* credential_store,
diff --git a/device/fido/mac/make_credential_operation.mm b/device/fido/mac/make_credential_operation.mm
index e017f77..5723f90 100644
--- a/device/fido/mac/make_credential_operation.mm
+++ b/device/fido/mac/make_credential_operation.mm
@@ -49,7 +49,7 @@
           &PublicKeyCredentialParams::CredentialInfo::algorithm)) {
     FIDO_LOG(ERROR) << "No supported algorithm found";
     std::move(callback_).Run(
-        CtapDeviceResponseCode::kCtap2ErrUnsupportedAlgorithm, absl::nullopt);
+        CtapDeviceResponseCode::kCtap2ErrUnsupportedAlgorithm, std::nullopt);
     return;
   }
 
@@ -71,7 +71,7 @@
 void MakeCredentialOperation::PromptTouchIdDone(bool success) {
   if (!success) {
     std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOperationDenied,
-                             absl::nullopt);
+                             std::nullopt);
     return;
   }
 
@@ -86,18 +86,18 @@
 
 void MakeCredentialOperation::CreateCredential(bool has_uv) {
   if (!request_.exclude_list.empty()) {
-    absl::optional<std::list<Credential>> credentials =
+    std::optional<std::list<Credential>> credentials =
         credential_store_->FindCredentialsFromCredentialDescriptorList(
             request_.rp.id, request_.exclude_list);
     if (!credentials) {
       FIDO_LOG(ERROR) << "Failed to check for excluded credentials";
       std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
-                               absl::nullopt);
+                               std::nullopt);
       return;
     }
     if (!credentials->empty()) {
       std::move(callback_).Run(
-          CtapDeviceResponseCode::kCtap2ErrCredentialExcluded, absl::nullopt);
+          CtapDeviceResponseCode::kCtap2ErrCredentialExcluded, std::nullopt);
       return;
     }
   }
@@ -107,7 +107,7 @@
                                                      request_.user.id)) {
     FIDO_LOG(ERROR) << "DeleteCredentialsForUserId() failed";
     std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
-                             absl::nullopt);
+                             std::nullopt);
     return;
   }
 
@@ -115,38 +115,38 @@
   //
   // New credentials are always discoverable. But older non-discoverable
   // credentials may exist.
-  absl::optional<std::pair<Credential, base::apple::ScopedCFTypeRef<SecKeyRef>>>
+  std::optional<std::pair<Credential, base::apple::ScopedCFTypeRef<SecKeyRef>>>
       credential_result = credential_store_->CreateCredential(
           request_.rp.id, request_.user, TouchIdCredentialStore::kDiscoverable);
   if (!credential_result) {
     FIDO_LOG(ERROR) << "CreateCredential() failed";
     std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
-                             absl::nullopt);
+                             std::nullopt);
     return;
   }
   auto [credential, sec_key_ref] = std::move(*credential_result);
 
   // Create attestation object. There is no separate attestation key pair, so
   // we perform self-attestation.
-  absl::optional<AttestedCredentialData> attested_credential_data =
+  std::optional<AttestedCredentialData> attested_credential_data =
       MakeAttestedCredentialData(credential.credential_id,
                                  SecKeyRefToECPublicKey(sec_key_ref.get()));
   if (!attested_credential_data) {
     FIDO_LOG(ERROR) << "MakeAttestedCredentialData failed";
     std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
-                             absl::nullopt);
+                             std::nullopt);
     return;
   }
   AuthenticatorData authenticator_data = MakeAuthenticatorData(
       credential.metadata.sign_counter_type, request_.rp.id,
       std::move(*attested_credential_data), has_uv);
-  absl::optional<std::vector<uint8_t>> signature =
+  std::optional<std::vector<uint8_t>> signature =
       GenerateSignature(authenticator_data, request_.client_data_hash,
                         credential.private_key.get());
   if (!signature) {
     FIDO_LOG(ERROR) << "MakeSignature failed";
     std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
-                             absl::nullopt);
+                             std::nullopt);
     return;
   }
   AuthenticatorMakeCredentialResponse response(
diff --git a/device/fido/mac/make_credential_operation_unittest_mac.mm b/device/fido/mac/make_credential_operation_unittest_mac.mm
index a990567..26a0c10 100644
--- a/device/fido/mac/make_credential_operation_unittest_mac.mm
+++ b/device/fido/mac/make_credential_operation_unittest_mac.mm
@@ -43,7 +43,7 @@
 TEST(MakeCredentialOperationTest, DISABLED_TestRun) {
   base::test::TaskEnvironment task_environment;
   TestCallbackReceiver<CtapDeviceResponseCode,
-                       absl::optional<AuthenticatorMakeCredentialResponse>>
+                       std::optional<AuthenticatorMakeCredentialResponse>>
       callback_receiver;
   auto request = MakeTestRequest();
   TouchIdCredentialStore credential_store(
diff --git a/device/fido/mac/util.h b/device/fido/mac/util.h
index c2358ae..eb5c96c 100644
--- a/device/fido/mac/util.h
+++ b/device/fido/mac/util.h
@@ -9,6 +9,7 @@
 #include <os/availability.h>
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -20,33 +21,32 @@
 #include "device/fido/fido_constants.h"
 #include "device/fido/mac/credential_metadata.h"
 #include "device/fido/p256_public_key.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device::fido::mac {
 
 // MakeAttestedCredentialData returns an AttestedCredentialData instance for
-// the Touch ID authenticator credential ID and public key or |absl::nullopt|
+// the Touch ID authenticator credential ID and public key or |std::nullopt|
 // on failure.
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<AttestedCredentialData> MakeAttestedCredentialData(
+std::optional<AttestedCredentialData> MakeAttestedCredentialData(
     std::vector<uint8_t> credential_id,
     std::unique_ptr<PublicKey> public_key);
 
 // MakeAuthenticatorData returns an AuthenticatorData instance for the Touch ID
 // authenticator with the given Relying Party ID and AttestedCredentialData,
-// which may be |absl::nullopt| in GetAssertion operations.
+// which may be |std::nullopt| in GetAssertion operations.
 COMPONENT_EXPORT(DEVICE_FIDO)
 AuthenticatorData MakeAuthenticatorData(
     CredentialMetadata::SignCounter counter_type,
     const std::string& rp_id,
-    absl::optional<AttestedCredentialData> attested_credential_data,
+    std::optional<AttestedCredentialData> attested_credential_data,
     bool has_uv);
 
 // GenerateSignature signs the concatenation of the serialization of the given
 // authenticator data and the given client data hash, as required for
-// (self-)attestation and assertion. Returns |absl::nullopt| if the operation
+// (self-)attestation and assertion. Returns |std::nullopt| if the operation
 // fails.
-absl::optional<std::vector<uint8_t>> GenerateSignature(
+std::optional<std::vector<uint8_t>> GenerateSignature(
     const AuthenticatorData& authenticator_data,
     base::span<const uint8_t, kClientDataHashLength> client_data_hash,
     SecKeyRef private_key);
diff --git a/device/fido/mac/util.mm b/device/fido/mac/util.mm
index c895566..7220c48 100644
--- a/device/fido/mac/util.mm
+++ b/device/fido/mac/util.mm
@@ -71,16 +71,16 @@
 }  // namespace
 
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<AttestedCredentialData> MakeAttestedCredentialData(
+std::optional<AttestedCredentialData> MakeAttestedCredentialData(
     std::vector<uint8_t> credential_id,
     std::unique_ptr<PublicKey> public_key) {
   if (credential_id.empty() || credential_id.size() > 255) {
     LOG(ERROR) << "invalid credential id: " << base::HexEncode(credential_id);
-    return absl::nullopt;
+    return std::nullopt;
   }
   if (!public_key) {
     LOG(ERROR) << "no public key";
-    return absl::nullopt;
+    return std::nullopt;
   }
   std::array<uint8_t, 2> encoded_credential_id_length = {
       0, static_cast<uint8_t>(credential_id.size())};
@@ -92,7 +92,7 @@
 AuthenticatorData MakeAuthenticatorData(
     CredentialMetadata::SignCounter counter_type,
     const std::string& rp_id,
-    absl::optional<AttestedCredentialData> attested_credential_data,
+    std::optional<AttestedCredentialData> attested_credential_data,
     bool has_uv) {
   uint8_t flags =
       static_cast<uint8_t>(AuthenticatorData::Flag::kTestOfUserPresence);
@@ -108,7 +108,7 @@
                            std::move(attested_credential_data));
 }
 
-absl::optional<std::vector<uint8_t>> GenerateSignature(
+std::optional<std::vector<uint8_t>> GenerateSignature(
     const AuthenticatorData& authenticator_data,
     base::span<const uint8_t, kClientDataHashLength> client_data_hash,
     SecKeyRef private_key) {
@@ -129,7 +129,7 @@
           sig_input.get(), err.InitializeInto()));
   if (!sig_data) {
     LOG(ERROR) << "SecKeyCreateSignature failed: " << err.get();
-    return absl::nullopt;
+    return std::nullopt;
   }
   return std::vector<uint8_t>(
       CFDataGetBytePtr(sig_data.get()),
diff --git a/device/fido/mac/util_unittest.cc b/device/fido/mac/util_unittest.cc
index 1058679c..66aa051 100644
--- a/device/fido/mac/util_unittest.cc
+++ b/device/fido/mac/util_unittest.cc
@@ -29,11 +29,11 @@
       []() { return g_fake_now; }, nullptr, nullptr);
 
   EXPECT_THAT(MakeAuthenticatorData(CredentialMetadata::SignCounter::kTimestamp,
-                                    kRpId, absl::nullopt, /*has_uv=*/true)
+                                    kRpId, std::nullopt, /*has_uv=*/true)
                   .counter(),
               ElementsAre(0xff, 0xce, 0xdd, 0x80));
   EXPECT_THAT(MakeAuthenticatorData(CredentialMetadata::SignCounter::kZero,
-                                    kRpId, absl::nullopt, /*has_uv=*/true)
+                                    kRpId, std::nullopt, /*has_uv=*/true)
                   .counter(),
               ElementsAre(0x00, 0x00, 0x00, 0x00));
 }
@@ -41,7 +41,7 @@
 TEST(MakeAuthenticatorDataTest, UvBit) {
   for (const bool has_uv : {false, true}) {
     AuthenticatorData data = MakeAuthenticatorData(
-        CredentialMetadata::SignCounter::kZero, kRpId, absl::nullopt, has_uv);
+        CredentialMetadata::SignCounter::kZero, kRpId, std::nullopt, has_uv);
     EXPECT_TRUE(data.obtained_user_presence());
     EXPECT_EQ(data.obtained_user_verification(), has_uv);
   }
diff --git a/device/fido/make_credential_handler_unittest.cc b/device/fido/make_credential_handler_unittest.cc
index 0beb422..78fe4fa5 100644
--- a/device/fido/make_credential_handler_unittest.cc
+++ b/device/fido/make_credential_handler_unittest.cc
@@ -55,7 +55,7 @@
 
 using TestMakeCredentialRequestCallback = test::StatusAndValuesCallbackReceiver<
     MakeCredentialStatus,
-    absl::optional<AuthenticatorMakeCredentialResponse>,
+    std::optional<AuthenticatorMakeCredentialResponse>,
     const FidoAuthenticator*>;
 
 }  // namespace
@@ -83,7 +83,7 @@
     PublicKeyCredentialRpEntity rp(test_data::kRelyingPartyId);
     PublicKeyCredentialUserEntity user(
         fido_parsing_utils::Materialize(test_data::kUserId), "nia",
-        absl::nullopt);
+        std::nullopt);
     PublicKeyCredentialParams credential_params(
         std::vector<PublicKeyCredentialParams::CredentialInfo>(1));
 
@@ -434,7 +434,7 @@
   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
       FROM_HERE,
       base::BindOnce(
-          [](base::OnceCallback<void(absl::optional<std::vector<uint8_t>>)>
+          [](base::OnceCallback<void(std::optional<std::vector<uint8_t>>)>
                  callback,
              std::vector<uint8_t> reply_bytes) {
             std::move(callback).Run(std::move(reply_bytes));
diff --git a/device/fido/make_credential_request_handler.cc b/device/fido/make_credential_request_handler.cc
index b5c1453..2fd820a8 100644
--- a/device/fido/make_credential_request_handler.cc
+++ b/device/fido/make_credential_request_handler.cc
@@ -60,7 +60,7 @@
   return permissions;
 }
 
-absl::optional<MakeCredentialStatus> ConvertDeviceResponseCode(
+std::optional<MakeCredentialStatus> ConvertDeviceResponseCode(
     CtapDeviceResponseCode device_response_code) {
   switch (device_response_code) {
     case CtapDeviceResponseCode::kSuccess:
@@ -88,7 +88,7 @@
     // For all other errors, the authenticator will be dropped, and other
     // authenticators may continue.
     default:
-      return absl::nullopt;
+      return std::nullopt;
   }
 }
 
@@ -173,7 +173,7 @@
     return MakeCredentialStatus::kAuthenticatorMissingUserVerification;
   }
 
-  absl::optional<base::span<const int32_t>> supported_algorithms(
+  std::optional<base::span<const int32_t>> supported_algorithms(
       authenticator->GetAlgorithms());
   if (supported_algorithms) {
     // Substitution of defaults should have happened by this point.
@@ -230,7 +230,7 @@
 }
 
 void ReportMakeCredentialResponseTransport(
-    absl::optional<FidoTransportProtocol> transport) {
+    std::optional<FidoTransportProtocol> transport) {
   if (transport) {
     base::UmaHistogramEnumeration(
         "WebAuthentication.MakeCredentialResponseTransport", *transport);
@@ -334,7 +334,7 @@
     return false;
   }
 
-  const absl::optional<cbor::Value>& extensions =
+  const std::optional<cbor::Value>& extensions =
       response.attestation_object.authenticator_data().extensions();
   if (extensions && !ValidateResponseExtensions(request, options, authenticator,
                                                 response, *extensions)) {
@@ -509,7 +509,7 @@
   } else {
     DispatchRequestAfterAppIdExclude(std::move(request), authenticator,
                                      CtapDeviceResponseCode::kSuccess,
-                                     absl::nullopt);
+                                     std::nullopt);
   }
 }
 
@@ -517,7 +517,7 @@
     std::unique_ptr<CtapMakeCredentialRequest> request,
     FidoAuthenticator* authenticator,
     CtapDeviceResponseCode status,
-    absl::optional<bool> unused) {
+    std::optional<bool> unused) {
   if (state_ != State::kWaitingForTouch) {
     return;
   }
@@ -537,7 +537,7 @@
     default:
       std::move(completion_callback_)
           .Run(MakeCredentialStatus::kAuthenticatorResponseInvalid,
-               absl::nullopt, authenticator);
+               std::nullopt, authenticator);
       return;
   }
 
@@ -587,7 +587,7 @@
       state_ = State::kFinished;
       std::move(completion_callback_)
           .Run(MakeCredentialStatus::kAuthenticatorRemovedDuringPINEntry,
-               absl::nullopt, nullptr);
+               std::nullopt, nullptr);
     }
   }
 }
@@ -636,8 +636,8 @@
 void MakeCredentialRequestHandler::HavePINUVAuthTokenResultForAuthenticator(
     FidoAuthenticator* authenticator,
     AuthTokenRequester::Result result,
-    absl::optional<pin::TokenResponse> token_response) {
-  absl::optional<MakeCredentialStatus> error;
+    std::optional<pin::TokenResponse> token_response) {
+  std::optional<MakeCredentialStatus> error;
   switch (result) {
     case AuthTokenRequester::Result::kPreTouchUnsatisfiableRequest:
     case AuthTokenRequester::Result::kPreTouchAuthenticatorResponseInvalid:
@@ -669,7 +669,7 @@
   DCHECK_EQ(selected_authenticator_for_pin_uv_auth_token_, authenticator);
   if (error) {
     state_ = State::kFinished;
-    std::move(completion_callback_).Run(*error, absl::nullopt, authenticator);
+    std::move(completion_callback_).Run(*error, std::nullopt, authenticator);
     return;
   }
 
@@ -722,7 +722,7 @@
     std::unique_ptr<CtapMakeCredentialRequest> request,
     base::ElapsedTimer request_timer,
     CtapDeviceResponseCode status,
-    absl::optional<AuthenticatorMakeCredentialResponse> response) {
+    std::optional<AuthenticatorMakeCredentialResponse> response) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
 
   if (state_ != State::kWaitingForTouch &&
@@ -747,7 +747,7 @@
     if (status != CtapDeviceResponseCode::kSuccess) {
       std::move(completion_callback_)
           .Run(WinCtapDeviceResponseCodeToMakeCredentialStatus(status),
-               absl::nullopt, authenticator);
+               std::nullopt, authenticator);
       return;
     }
     if (!response ||
@@ -756,7 +756,7 @@
           << "Failing make credential request due to bad response from "
           << authenticator->GetDisplayName();
       std::move(completion_callback_)
-          .Run(MakeCredentialStatus::kWinNotAllowedError, absl::nullopt,
+          .Run(MakeCredentialStatus::kWinNotAllowedError, std::nullopt,
                authenticator);
       return;
     }
@@ -819,13 +819,13 @@
     return;
   }
 
-  const absl::optional<MakeCredentialStatus> maybe_result =
+  const std::optional<MakeCredentialStatus> maybe_result =
       ConvertDeviceResponseCode(status);
   if (!maybe_result) {
     if (state_ == State::kWaitingForResponseWithToken) {
       std::move(completion_callback_)
           .Run(MakeCredentialStatus::kAuthenticatorResponseInvalid,
-               absl::nullopt, authenticator);
+               std::nullopt, authenticator);
     } else if (authenticator->GetType() == AuthenticatorType::kPhone ||
                authenticator->GetType() == AuthenticatorType::kEnclave) {
       FIDO_LOG(ERROR) << "Status " << static_cast<int>(status) << " from "
@@ -835,7 +835,7 @@
           .Run(authenticator->GetType() == AuthenticatorType::kPhone
                    ? MakeCredentialStatus::kHybridTransportError
                    : MakeCredentialStatus::kEnclaveError,
-               absl::nullopt, authenticator);
+               std::nullopt, authenticator);
     } else {
       FIDO_LOG(ERROR) << "Ignoring status " << static_cast<int>(status)
                       << " from " << authenticator->GetDisplayName();
@@ -850,7 +850,7 @@
     FIDO_LOG(ERROR) << "Failing make credential request due to status "
                     << status << " from " << authenticator->GetDisplayName();
     std::move(completion_callback_)
-        .Run(*maybe_result, absl::nullopt, authenticator);
+        .Run(*maybe_result, std::nullopt, authenticator);
     return;
   }
 
@@ -860,7 +860,7 @@
         << "Failing make credential request due to bad response from "
         << authenticator->GetDisplayName();
     std::move(completion_callback_)
-        .Run(MakeCredentialStatus::kAuthenticatorResponseInvalid, absl::nullopt,
+        .Run(MakeCredentialStatus::kAuthenticatorResponseInvalid, std::nullopt,
              authenticator);
     return;
   }
@@ -879,7 +879,7 @@
   CancelActiveAuthenticators(authenticator->GetId());
   std::move(completion_callback_)
       .Run(MakeCredentialStatus::kUserConsentButCredentialExcluded,
-           absl::nullopt, nullptr);
+           std::nullopt, nullptr);
 }
 
 void MakeCredentialRequestHandler::HandleInapplicableAuthenticator(
@@ -890,7 +890,7 @@
 
   state_ = State::kFinished;
   CancelActiveAuthenticators(authenticator->GetId());
-  std::move(completion_callback_).Run(status, absl::nullopt, nullptr);
+  std::move(completion_callback_).Run(status, std::nullopt, nullptr);
 }
 
 void MakeCredentialRequestHandler::OnSampleCollected(
@@ -900,7 +900,7 @@
 }
 
 void MakeCredentialRequestHandler::OnEnrollmentDone(
-    absl::optional<std::vector<uint8_t>> template_id) {
+    std::optional<std::vector<uint8_t>> template_id) {
   state_ = State::kBioEnrollmentDone;
 
   bio_enrollment_complete_barrier_->Run();
@@ -911,7 +911,7 @@
   bio_enroller_.reset();
   state_ = State::kFinished;
   std::move(completion_callback_)
-      .Run(MakeCredentialStatus::kAuthenticatorResponseInvalid, absl::nullopt,
+      .Run(MakeCredentialStatus::kAuthenticatorResponseInvalid, std::nullopt,
            nullptr);
 }
 
diff --git a/device/fido/make_credential_request_handler.h b/device/fido/make_credential_request_handler.h
index 8b1f128..b81397f 100644
--- a/device/fido/make_credential_request_handler.h
+++ b/device/fido/make_credential_request_handler.h
@@ -6,6 +6,7 @@
 #define DEVICE_FIDO_MAKE_CREDENTIAL_REQUEST_HANDLER_H_
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -25,7 +26,6 @@
 #include "device/fido/fido_transport_protocol.h"
 #include "device/fido/fido_types.h"
 #include "device/fido/pin.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
 class ElapsedTimer;
@@ -70,7 +70,7 @@
  public:
   using CompletionCallback = base::OnceCallback<void(
       MakeCredentialStatus,
-      absl::optional<AuthenticatorMakeCredentialResponse>,
+      std::optional<AuthenticatorMakeCredentialResponse>,
       const FidoAuthenticator*)>;
 
   MakeCredentialRequestHandler(
@@ -113,13 +113,13 @@
   void HavePINUVAuthTokenResultForAuthenticator(
       FidoAuthenticator* authenticator,
       AuthTokenRequester::Result result,
-      absl::optional<pin::TokenResponse> response) override;
+      std::optional<pin::TokenResponse> response) override;
 
   // BioEnroller::Delegate:
   void OnSampleCollected(BioEnrollmentSampleStatus status,
                          int samples_remaining) override;
   void OnEnrollmentDone(
-      absl::optional<std::vector<uint8_t>> template_id) override;
+      std::optional<std::vector<uint8_t>> template_id) override;
   void OnEnrollmentError(CtapDeviceResponseCode status) override;
 
   void ObtainPINUVAuthToken(FidoAuthenticator* authenticator,
@@ -130,13 +130,13 @@
       std::unique_ptr<CtapMakeCredentialRequest> request,
       FidoAuthenticator* authenticator,
       CtapDeviceResponseCode status,
-      absl::optional<bool> unused);
+      std::optional<bool> unused);
   void HandleResponse(
       FidoAuthenticator* authenticator,
       std::unique_ptr<CtapMakeCredentialRequest> request,
       base::ElapsedTimer request_timer,
       CtapDeviceResponseCode response_code,
-      absl::optional<AuthenticatorMakeCredentialResponse> response);
+      std::optional<AuthenticatorMakeCredentialResponse> response);
   void HandleExcludedAuthenticator(FidoAuthenticator* authenticator);
   void HandleInapplicableAuthenticator(FidoAuthenticator* authenticator,
                                        MakeCredentialStatus status);
@@ -155,7 +155,7 @@
   State state_ = State::kWaitingForTouch;
   bool suppress_attestation_ = false;
   CtapMakeCredentialRequest request_;
-  absl::optional<base::RepeatingClosure> bio_enrollment_complete_barrier_;
+  std::optional<base::RepeatingClosure> bio_enrollment_complete_barrier_;
   const MakeCredentialOptions options_;
 
   std::map<FidoAuthenticator*, std::unique_ptr<AuthTokenRequester>>
@@ -167,7 +167,7 @@
   // object and this pointer is cleared if it's removed during processing.
   raw_ptr<FidoAuthenticator> selected_authenticator_for_pin_uv_auth_token_ =
       nullptr;
-  absl::optional<pin::TokenResponse> token_;
+  std::optional<pin::TokenResponse> token_;
   std::unique_ptr<BioEnroller> bio_enroller_;
 
   // On ChromeOS, non-U2F cross-platform requests may be dispatched to the
diff --git a/device/fido/make_credential_task.cc b/device/fido/make_credential_task.cc
index 31509f6..27ac331 100644
--- a/device/fido/make_credential_task.cc
+++ b/device/fido/make_credential_task.cc
@@ -63,17 +63,17 @@
 // given CTAP response message in |cbor|. It wraps
 // ReadCTAPMakeCredentialResponse() and in addition fills in |is_resident_key|,
 // which requires looking at the request and device.
-absl::optional<AuthenticatorMakeCredentialResponse> ConvertCTAPResponse(
+std::optional<AuthenticatorMakeCredentialResponse> ConvertCTAPResponse(
     FidoDevice* device,
     bool resident_key_required,
-    const absl::optional<cbor::Value>& cbor) {
+    const std::optional<cbor::Value>& cbor) {
   DCHECK_EQ(device->supported_protocol(), ProtocolVersion::kCtap2);
   DCHECK(device->device_info());
 
-  absl::optional<AuthenticatorMakeCredentialResponse> response =
+  std::optional<AuthenticatorMakeCredentialResponse> response =
       ReadCTAPMakeCredentialResponse(device->DeviceTransport(), cbor);
   if (!response) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   // Fill in whether the created credential is client-side discoverable
@@ -258,7 +258,7 @@
 
 void MakeCredentialTask::HandleResponseToSilentSignRequest(
     CtapDeviceResponseCode response_code,
-    absl::optional<AuthenticatorGetAssertionResponse> response_data) {
+    std::optional<AuthenticatorGetAssertionResponse> response_data) {
   if (canceled_) {
     return;
   }
@@ -328,15 +328,15 @@
 
 void MakeCredentialTask::HandleResponseToDummyTouch(
     CtapDeviceResponseCode response_code,
-    absl::optional<AuthenticatorMakeCredentialResponse> response_data) {
+    std::optional<AuthenticatorMakeCredentialResponse> response_data) {
   std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
-                           absl::nullopt);
+                           std::nullopt);
 }
 
 void MakeCredentialTask::U2fRegister() {
   if (!IsConvertibleToU2fRegisterCommand(request_)) {
     std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
-                             absl::nullopt);
+                             std::nullopt);
     return;
   }
 
@@ -350,7 +350,7 @@
 
 void MakeCredentialTask::MaybeRevertU2fFallback(
     CtapDeviceResponseCode status,
-    absl::optional<AuthenticatorMakeCredentialResponse> response) {
+    std::optional<AuthenticatorMakeCredentialResponse> response) {
   DCHECK_EQ(ProtocolVersion::kU2f, device()->supported_protocol());
   if (device()->device_info()) {
     // This was actually a CTAP2 device, but the protocol version was set to U2F
diff --git a/device/fido/make_credential_task.h b/device/fido/make_credential_task.h
index 1b42dfd..7d2d4c82 100644
--- a/device/fido/make_credential_task.h
+++ b/device/fido/make_credential_task.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "base/component_export.h"
@@ -20,7 +21,6 @@
 #include "device/fido/device_operation.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_task.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -30,7 +30,7 @@
  public:
   using MakeCredentialTaskCallback = base::OnceCallback<void(
       CtapDeviceResponseCode,
-      absl::optional<AuthenticatorMakeCredentialResponse>)>;
+      std::optional<AuthenticatorMakeCredentialResponse>)>;
   using SignOperation = DeviceOperation<CtapGetAssertionRequest,
                                         AuthenticatorGetAssertionResponse>;
   using RegisterOperation =
@@ -68,15 +68,15 @@
   CtapGetAssertionRequest NextSilentRequest();
   void HandleResponseToSilentSignRequest(
       CtapDeviceResponseCode response_code,
-      absl::optional<AuthenticatorGetAssertionResponse> response_data);
+      std::optional<AuthenticatorGetAssertionResponse> response_data);
   void HandleResponseToDummyTouch(
       CtapDeviceResponseCode response_code,
-      absl::optional<AuthenticatorMakeCredentialResponse> response_data);
+      std::optional<AuthenticatorMakeCredentialResponse> response_data);
 
   void U2fRegister();
   void MaybeRevertU2fFallback(
       CtapDeviceResponseCode status,
-      absl::optional<AuthenticatorMakeCredentialResponse> response);
+      std::optional<AuthenticatorMakeCredentialResponse> response);
 
   CtapMakeCredentialRequest request_;
   MakeCredentialOptions options_;
diff --git a/device/fido/make_credential_task_unittest.cc b/device/fido/make_credential_task_unittest.cc
index 18fc294..4ed3681 100644
--- a/device/fido/make_credential_task_unittest.cc
+++ b/device/fido/make_credential_task_unittest.cc
@@ -37,7 +37,7 @@
 using TestMakeCredentialTaskCallback =
     ::device::test::StatusAndValueCallbackReceiver<
         CtapDeviceResponseCode,
-        absl::optional<AuthenticatorMakeCredentialResponse>>;
+        std::optional<AuthenticatorMakeCredentialResponse>>;
 
 class FidoMakeCredentialTaskTest : public testing::Test {
  public:
@@ -149,7 +149,7 @@
 
   auto device = MockFidoDevice::MakeCtap(std::move(device_info));
   device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorMakeCredential, absl::nullopt);
+      CtapRequestCommand::kAuthenticatorMakeCredential, std::nullopt);
 
   PublicKeyCredentialRpEntity rp(test_data::kRelyingPartyId);
   PublicKeyCredentialUserEntity user(
diff --git a/device/fido/mock_fido_device.cc b/device/fido/mock_fido_device.cc
index 6c5a070..bbdc16b 100644
--- a/device/fido/mock_fido_device.cc
+++ b/device/fido/mock_fido_device.cc
@@ -27,12 +27,12 @@
 
 // static
 std::unique_ptr<MockFidoDevice> MockFidoDevice::MakeU2f() {
-  return std::make_unique<MockFidoDevice>(ProtocolVersion::kU2f, absl::nullopt);
+  return std::make_unique<MockFidoDevice>(ProtocolVersion::kU2f, std::nullopt);
 }
 
 // static
 std::unique_ptr<MockFidoDevice> MockFidoDevice::MakeCtap(
-    absl::optional<AuthenticatorGetInfoResponse> device_info) {
+    std::optional<AuthenticatorGetInfoResponse> device_info) {
   if (!device_info) {
     device_info = DefaultAuthenticatorInfo();
   }
@@ -50,13 +50,13 @@
   device->StubGetDisplayName();
   device->ExpectWinkedAtLeastOnce();
   device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo, absl::nullopt);
+      CtapRequestCommand::kAuthenticatorGetInfo, std::nullopt);
   return device;
 }
 
 // static
 std::unique_ptr<MockFidoDevice> MockFidoDevice::MakeCtapWithGetInfoExpectation(
-    absl::optional<base::span<const uint8_t>> get_info_response) {
+    std::optional<base::span<const uint8_t>> get_info_response) {
   if (!get_info_response) {
     get_info_response = test_data::kTestAuthenticatorGetInfoResponse;
   }
@@ -72,11 +72,11 @@
 }
 
 std::vector<uint8_t> MockFidoDevice::EncodeCBORRequest(
-    std::pair<CtapRequestCommand, absl::optional<cbor::Value>> request) {
+    std::pair<CtapRequestCommand, std::optional<cbor::Value>> request) {
   std::vector<uint8_t> request_bytes;
 
   if (request.second) {
-    absl::optional<std::vector<uint8_t>> cbor_bytes =
+    std::optional<std::vector<uint8_t>> cbor_bytes =
         cbor::Writer::Write(*request.second);
     DCHECK(cbor_bytes);
     request_bytes = std::move(*cbor_bytes);
@@ -94,7 +94,7 @@
 MockFidoDevice::MockFidoDevice() = default;
 MockFidoDevice::MockFidoDevice(
     ProtocolVersion protocol_version,
-    absl::optional<AuthenticatorGetInfoResponse> device_info)
+    std::optional<AuthenticatorGetInfoResponse> device_info)
     : MockFidoDevice() {
   set_supported_protocol(protocol_version);
   if (device_info) {
@@ -137,7 +137,7 @@
 
 void MockFidoDevice::ExpectCtap2CommandAndRespondWith(
     CtapRequestCommand command,
-    absl::optional<base::span<const uint8_t>> response,
+    std::optional<base::span<const uint8_t>> response,
     base::TimeDelta delay,
     testing::Matcher<base::span<const uint8_t>> request_matcher) {
   auto data = fido_parsing_utils::MaterializeOrNull(response);
@@ -164,7 +164,7 @@
 
 void MockFidoDevice::ExpectRequestAndRespondWith(
     base::span<const uint8_t> request,
-    absl::optional<base::span<const uint8_t>> response,
+    std::optional<base::span<const uint8_t>> response,
     base::TimeDelta delay) {
   auto data = fido_parsing_utils::MaterializeOrNull(response);
   auto send_response = [data(std::move(data)), delay](DeviceCallback& cb) {
diff --git a/device/fido/mock_fido_device.h b/device/fido/mock_fido_device.h
index 5a03132..ea96f418 100644
--- a/device/fido/mock_fido_device.h
+++ b/device/fido/mock_fido_device.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -18,7 +19,6 @@
 #include "device/fido/fido_device.h"
 #include "device/fido/fido_transport_protocol.h"
 #include "testing/gmock/include/gmock/gmock.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace cbor {
 class Value;
@@ -36,7 +36,7 @@
   // state after |DiscoverSupportedProtocolAndDeviceInfo| has been called by
   // the FidoDeviceDiscovery.
   static std::unique_ptr<MockFidoDevice> MakeCtap(
-      absl::optional<AuthenticatorGetInfoResponse> device_info = absl::nullopt);
+      std::optional<AuthenticatorGetInfoResponse> device_info = std::nullopt);
   // MakeU2fWithDeviceInfoExpectation returns a uninitialized U2F device
   // suitable for injecting into a FidoDeviceDiscovery, which will determine its
   // protocol version by invoking |DiscoverSupportedProtocolAndDeviceInfo|.
@@ -47,16 +47,16 @@
   // response is supplied, the mock will use that to reply; otherwise it will
   // use |test_data::kTestAuthenticatorGetInfoResponse|.
   static std::unique_ptr<MockFidoDevice> MakeCtapWithGetInfoExpectation(
-      absl::optional<base::span<const uint8_t>> get_info_response =
-          absl::nullopt);
+      std::optional<base::span<const uint8_t>> get_info_response =
+          std::nullopt);
   // EncodeCBORRequest is a helper function for use with the |Expect*|
   // functions, below, that take a serialised request.
   static std::vector<uint8_t> EncodeCBORRequest(
-      std::pair<CtapRequestCommand, absl::optional<cbor::Value>> request);
+      std::pair<CtapRequestCommand, std::optional<cbor::Value>> request);
 
   MockFidoDevice();
   MockFidoDevice(ProtocolVersion protocol_version,
-                 absl::optional<AuthenticatorGetInfoResponse> device_info);
+                 std::optional<AuthenticatorGetInfoResponse> device_info);
 
   MockFidoDevice(const MockFidoDevice&) = delete;
   MockFidoDevice& operator=(const MockFidoDevice&) = delete;
@@ -87,7 +87,7 @@
   void ExpectWinkedAtLeastOnce();
   void ExpectCtap2CommandAndRespondWith(
       CtapRequestCommand command,
-      absl::optional<base::span<const uint8_t>> response,
+      std::optional<base::span<const uint8_t>> response,
       base::TimeDelta delay = base::TimeDelta(),
       testing::Matcher<base::span<const uint8_t>> request_matcher =
           testing::A<base::span<const uint8_t>>());
@@ -97,7 +97,7 @@
       base::TimeDelta delay = base::TimeDelta());
   void ExpectRequestAndRespondWith(
       base::span<const uint8_t> request,
-      absl::optional<base::span<const uint8_t>> response,
+      std::optional<base::span<const uint8_t>> response,
       base::TimeDelta delay = base::TimeDelta());
   void ExpectCtap2CommandAndDoNotRespond(CtapRequestCommand command);
   void ExpectRequestAndDoNotRespond(base::span<const uint8_t> request);
diff --git a/device/fido/opaque_attestation_statement.cc b/device/fido/opaque_attestation_statement.cc
index 4c09d40..31308b4fc 100644
--- a/device/fido/opaque_attestation_statement.cc
+++ b/device/fido/opaque_attestation_statement.cc
@@ -51,19 +51,19 @@
   return false;
 }
 
-absl::optional<base::span<const uint8_t>>
+std::optional<base::span<const uint8_t>>
 OpaqueAttestationStatement::GetLeafCertificate() const {
   DCHECK(attestation_statement_map_.is_map());
   const Value::MapValue& m(attestation_statement_map_.GetMap());
   const Value x5c("x5c");
   const auto it = m.find(x5c);
   if (it == m.end() || !it->second.is_array()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   const Value::ArrayValue& certs = it->second.GetArray();
   if (certs.empty() || !certs[0].is_bytestring()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return certs[0].GetBytestring();
diff --git a/device/fido/opaque_attestation_statement.h b/device/fido/opaque_attestation_statement.h
index 52a8551..0610b403 100644
--- a/device/fido/opaque_attestation_statement.h
+++ b/device/fido/opaque_attestation_statement.h
@@ -31,7 +31,7 @@
   bool IsNoneAttestation() const override;
   bool IsSelfAttestation() const override;
   bool IsAttestationCertificateInappropriatelyIdentifying() const override;
-  absl::optional<base::span<const uint8_t>> GetLeafCertificate() const override;
+  std::optional<base::span<const uint8_t>> GetLeafCertificate() const override;
 
  private:
   cbor::Value attestation_statement_map_;
diff --git a/device/fido/pin.cc b/device/fido/pin.cc
index 741e33b..7af6243 100644
--- a/device/fido/pin.cc
+++ b/device/fido/pin.cc
@@ -45,7 +45,7 @@
 
 PINEntryError ValidatePIN(const std::string& pin,
                           uint32_t min_pin_length,
-                          absl::optional<std::string> current_pin) {
+                          std::optional<std::string> current_pin) {
   if (pin.size() < min_pin_length) {
     return PINEntryError::kTooShort;
   }
@@ -63,7 +63,7 @@
 
 PINEntryError ValidatePIN(const std::u16string& pin16,
                           uint32_t min_pin_length,
-                          absl::optional<std::string> current_pin) {
+                          std::optional<std::string> current_pin) {
   std::string pin;
   if (!base::UTF16ToUTF8(pin16.c_str(), pin16.size(), &pin)) {
     return pin::PINEntryError::kInvalidCharacters;
@@ -74,7 +74,7 @@
 // EncodePINCommand returns a CTAP2 PIN command for the operation |subcommand|.
 // Additional elements of the top-level CBOR map can be added with the optional
 // |add_additional| callback.
-static std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+static std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 EncodePINCommand(
     PINUVAuthProtocol protocol_version,
     Subcommand subcommand,
@@ -96,36 +96,36 @@
 RetriesResponse::RetriesResponse() = default;
 
 // static
-absl::optional<RetriesResponse> RetriesResponse::ParsePinRetries(
-    const absl::optional<cbor::Value>& cbor) {
+std::optional<RetriesResponse> RetriesResponse::ParsePinRetries(
+    const std::optional<cbor::Value>& cbor) {
   return RetriesResponse::Parse(std::move(cbor),
                                 static_cast<int>(ResponseKey::kRetries));
 }
 
 // static
-absl::optional<RetriesResponse> RetriesResponse::ParseUvRetries(
-    const absl::optional<cbor::Value>& cbor) {
+std::optional<RetriesResponse> RetriesResponse::ParseUvRetries(
+    const std::optional<cbor::Value>& cbor) {
   return RetriesResponse::Parse(std::move(cbor),
                                 static_cast<int>(ResponseKey::kUvRetries));
 }
 
 // static
-absl::optional<RetriesResponse> RetriesResponse::Parse(
-    const absl::optional<cbor::Value>& cbor,
+std::optional<RetriesResponse> RetriesResponse::Parse(
+    const std::optional<cbor::Value>& cbor,
     const int retries_key) {
   if (!cbor || !cbor->is_map()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   const auto& response_map = cbor->GetMap();
 
   auto it = response_map.find(cbor::Value(retries_key));
   if (it == response_map.end() || !it->second.is_unsigned()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   const int64_t retries = it->second.GetUnsigned();
   if (retries > INT_MAX) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   RetriesResponse ret;
@@ -136,10 +136,10 @@
 KeyAgreementResponse::KeyAgreementResponse() = default;
 
 // static
-absl::optional<KeyAgreementResponse> KeyAgreementResponse::Parse(
-    const absl::optional<cbor::Value>& cbor) {
+std::optional<KeyAgreementResponse> KeyAgreementResponse::Parse(
+    const std::optional<cbor::Value>& cbor) {
   if (!cbor || !cbor->is_map()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   const auto& response_map = cbor->GetMap();
 
@@ -147,7 +147,7 @@
   auto it = response_map.find(
       cbor::Value(static_cast<int>(ResponseKey::kKeyAgreement)));
   if (it == response_map.end() || !it->second.is_map()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   const auto& cose_key = it->second.GetMap();
 
@@ -155,7 +155,7 @@
 }
 
 // static
-absl::optional<KeyAgreementResponse> KeyAgreementResponse::ParseFromCOSE(
+std::optional<KeyAgreementResponse> KeyAgreementResponse::ParseFromCOSE(
     const cbor::Value::MapValue& cose_key) {
   // The COSE key must be a P-256 point. See
   // https://tools.ietf.org/html/rfc8152#section-7.1
@@ -167,7 +167,7 @@
     auto it = cose_key.find(cbor::Value(pair.first));
     if (it == cose_key.end() || !it->second.is_integer() ||
         it->second.GetInteger() != pair.second) {
-      return absl::nullopt;
+      return std::nullopt;
     }
   }
 
@@ -176,14 +176,14 @@
   const auto& y_it = cose_key.find(cbor::Value(-3));
   if (x_it == cose_key.end() || y_it == cose_key.end() ||
       !x_it->second.is_bytestring() || !y_it->second.is_bytestring()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   const auto& x = x_it->second.GetBytestring();
   const auto& y = y_it->second.GetBytestring();
   KeyAgreementResponse ret;
   if (x.size() != sizeof(ret.x) || y.size() != sizeof(ret.y)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   memcpy(ret.x, x.data(), sizeof(ret.x));
   memcpy(ret.y, y.data(), sizeof(ret.y));
@@ -194,7 +194,7 @@
   // Check that the point is on the curve.
   auto point = PointFromKeyAgreementResponse(group.get(), ret);
   if (!point) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return ret;
@@ -248,14 +248,14 @@
 }
 
 // static
-absl::optional<EmptyResponse> EmptyResponse::Parse(
-    const absl::optional<cbor::Value>& cbor) {
+std::optional<EmptyResponse> EmptyResponse::Parse(
+    const std::optional<cbor::Value>& cbor) {
   // Yubikeys can return just the status byte, and no CBOR bytes, for the empty
   // response, which will end up here with |cbor| being |nullopt|. This seems
   // wrong, but is handled. (The response should, instead, encode an empty CBOR
   // map.)
   if (cbor && (!cbor->is_map() || !cbor->GetMap().empty())) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   EmptyResponse ret;
@@ -268,23 +268,23 @@
 TokenResponse::TokenResponse(const TokenResponse&) = default;
 TokenResponse& TokenResponse::operator=(const TokenResponse&) = default;
 
-absl::optional<TokenResponse> TokenResponse::Parse(
+std::optional<TokenResponse> TokenResponse::Parse(
     PINUVAuthProtocol protocol,
     base::span<const uint8_t> shared_key,
-    const absl::optional<cbor::Value>& cbor) {
+    const std::optional<cbor::Value>& cbor) {
   if (!cbor || !cbor->is_map()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   const auto& response_map = cbor->GetMap();
 
   auto it =
       response_map.find(cbor::Value(static_cast<int>(ResponseKey::kPINToken)));
   if (it == response_map.end() || !it->second.is_bytestring()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   const auto& encrypted_token = it->second.GetBytestring();
   if (encrypted_token.size() % AES_BLOCK_SIZE != 0) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   std::vector<uint8_t> token =
@@ -297,12 +297,12 @@
       // may be any multiple of 16 bytes. We don't know the CTAP version, so
       // only enforce the latter.
       if (token.empty() || token.size() % AES_BLOCK_SIZE != 0) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       break;
     case PINUVAuthProtocol::kV2:
       if (token.size() != 32u) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       break;
   }
@@ -319,25 +319,25 @@
 }
 
 // static
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const PinRetriesRequest& request) {
   return EncodePINCommand(request.protocol, Subcommand::kGetRetries);
 }
 
 // static
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const UvRetriesRequest& request) {
   return EncodePINCommand(request.protocol, Subcommand::kGetUvRetries);
 }
 
 // static
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const KeyAgreementRequest& request) {
   return EncodePINCommand(request.protocol, Subcommand::kGetKeyAgreement);
 }
 
 // static
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const SetRequest& request) {
   // See
   // https://fidoalliance.org/specs/fido-v2.0-rd-20180702/fido-client-to-authenticator-protocol-v2.0-rd-20180702.html#settingNewPin
@@ -367,7 +367,7 @@
 }
 
 // static
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const ChangeRequest& request) {
   // See
   // https://fidoalliance.org/specs/fido-v2.0-rd-20180702/fido-client-to-authenticator-protocol-v2.0-rd-20180702.html#changingExistingPin
@@ -411,9 +411,9 @@
 }
 
 // static
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const ResetRequest&) {
-  return std::make_pair(CtapRequestCommand::kAuthenticatorReset, absl::nullopt);
+  return std::make_pair(CtapRequestCommand::kAuthenticatorReset, std::nullopt);
 }
 
 TokenRequest::TokenRequest(PINUVAuthProtocol protocol,
@@ -444,7 +444,7 @@
 PinTokenRequest::PinTokenRequest(PinTokenRequest&& other) = default;
 
 // static
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const PinTokenRequest& request) {
   static_assert((sizeof(request.pin_hash_) % AES_BLOCK_SIZE) == 0,
                 "pin_hash_ is not a multiple of the AES block size");
@@ -467,13 +467,13 @@
     const std::string& pin,
     const KeyAgreementResponse& peer_key,
     base::span<const pin::Permissions> permissions,
-    const absl::optional<std::string> rp_id)
+    const std::optional<std::string> rp_id)
     : PinTokenRequest(protocol, pin, peer_key),
       permissions_(PermissionsToByte(permissions)),
       rp_id_(rp_id) {}
 
 // static
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const PinTokenWithPermissionsRequest& request) {
   std::vector<uint8_t> encrypted_pin =
       ProtocolVersion(request.protocol_)
@@ -502,7 +502,7 @@
 
 UvTokenRequest::UvTokenRequest(PINUVAuthProtocol protocol,
                                const KeyAgreementResponse& peer_key,
-                               absl::optional<std::string> rp_id,
+                               std::optional<std::string> rp_id,
                                base::span<const pin::Permissions> permissions)
     : TokenRequest(protocol, peer_key),
       rp_id_(rp_id),
@@ -513,7 +513,7 @@
 UvTokenRequest::UvTokenRequest(UvTokenRequest&& other) = default;
 
 // static
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const UvTokenRequest& request) {
   return EncodePINCommand(
       request.protocol_, Subcommand::kGetUvToken,
@@ -531,7 +531,7 @@
 
 static std::vector<uint8_t> ConcatSalts(
     base::span<const uint8_t, 32> salt1,
-    const absl::optional<std::array<uint8_t, 32>>& salt2) {
+    const std::optional<std::array<uint8_t, 32>>& salt2) {
   const size_t salts_size =
       salt1.size() + (salt2.has_value() ? salt2->size() : 0);
   std::vector<uint8_t> salts(salts_size);
@@ -548,7 +548,7 @@
     PINUVAuthProtocol protocol,
     const KeyAgreementResponse& peer_key,
     base::span<const uint8_t, 32> salt1,
-    const absl::optional<std::array<uint8_t, 32>>& salt2)
+    const std::optional<std::array<uint8_t, 32>>& salt2)
     : protocol_(protocol),
       have_two_salts_(salt2.has_value()),
       public_key_x962(
@@ -563,14 +563,14 @@
 
 HMACSecretRequest::HMACSecretRequest(const HMACSecretRequest& other) = default;
 
-absl::optional<std::vector<uint8_t>> HMACSecretRequest::Decrypt(
+std::optional<std::vector<uint8_t>> HMACSecretRequest::Decrypt(
     base::span<const uint8_t> ciphertext) {
-  const absl::optional<std::vector<uint8_t>> plaintext =
+  const std::optional<std::vector<uint8_t>> plaintext =
       pin::ProtocolVersion(protocol_).Decrypt(shared_key_, ciphertext);
 
   const unsigned num_salts = have_two_salts_ ? 2 : 1;
   if (plaintext && plaintext->size() != 32 * num_salts) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return plaintext;
diff --git a/device/fido/pin.h b/device/fido/pin.h
index 64cd05c..fb9a187e 100644
--- a/device/fido/pin.h
+++ b/device/fido/pin.h
@@ -12,6 +12,7 @@
 #include <stdint.h>
 
 #include <array>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -19,7 +20,6 @@
 #include "base/containers/span.h"
 #include "components/cbor/values.h"
 #include "device/fido/fido_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 namespace pin {
@@ -80,14 +80,14 @@
 PINEntryError ValidatePIN(
     const std::string& pin,
     uint32_t min_pin_length = kMinPinLength,
-    absl::optional<std::string> current_pin = absl::nullopt);
+    std::optional<std::string> current_pin = std::nullopt);
 
 // Like |ValidatePIN| above but takes a wide string.
 COMPONENT_EXPORT(DEVICE_FIDO)
 PINEntryError ValidatePIN(
     const std::u16string& pin16,
     uint32_t min_pin_length = kMinPinLength,
-    absl::optional<std::string> current_pin = absl::nullopt);
+    std::optional<std::string> current_pin = std::nullopt);
 
 // kMinBytes is the minimum number of *bytes* of PIN data that a CTAP2 device
 // will accept. Since the PIN is UTF-8 encoded, this could be a single code
@@ -118,19 +118,19 @@
 // RetriesResponse reflects an authenticator's response to a |PinRetriesRequest|
 // or a |UvRetriesRequest|.
 struct RetriesResponse {
-  static absl::optional<RetriesResponse> ParsePinRetries(
-      const absl::optional<cbor::Value>& cbor);
+  static std::optional<RetriesResponse> ParsePinRetries(
+      const std::optional<cbor::Value>& cbor);
 
-  static absl::optional<RetriesResponse> ParseUvRetries(
-      const absl::optional<cbor::Value>& cbor);
+  static std::optional<RetriesResponse> ParseUvRetries(
+      const std::optional<cbor::Value>& cbor);
 
   // retries is the number of PIN attempts remaining before the authenticator
   // locks.
   int retries;
 
  private:
-  static absl::optional<RetriesResponse> Parse(
-      const absl::optional<cbor::Value>& cbor,
+  static std::optional<RetriesResponse> Parse(
+      const std::optional<cbor::Value>& cbor,
       const int retries_key);
 
   RetriesResponse();
@@ -146,9 +146,9 @@
 // |KeyAgreementRequest| and is also used as representation of the
 // authenticator's ephemeral key.
 struct COMPONENT_EXPORT(DEVICE_FIDO) KeyAgreementResponse {
-  static absl::optional<KeyAgreementResponse> Parse(
-      const absl::optional<cbor::Value>& cbor);
-  static absl::optional<KeyAgreementResponse> ParseFromCOSE(
+  static std::optional<KeyAgreementResponse> Parse(
+      const std::optional<cbor::Value>& cbor);
+  static std::optional<KeyAgreementResponse> ParseFromCOSE(
       const cbor::Value::MapValue& cose_key);
 
   // X962 returns the public key from the response in X9.62 form.
@@ -171,7 +171,7 @@
              const std::string& pin,
              const KeyAgreementResponse& peer_key);
 
-  friend std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+  friend std::pair<CtapRequestCommand, std::optional<cbor::Value>>
   AsCTAPRequestValuePair(const SetRequest&);
 
  private:
@@ -181,8 +181,8 @@
 };
 
 struct EmptyResponse {
-  static absl::optional<EmptyResponse> Parse(
-      const absl::optional<cbor::Value>& cbor);
+  static std::optional<EmptyResponse> Parse(
+      const std::optional<cbor::Value>& cbor);
 };
 
 // ChangeRequest changes the PIN on an authenticator that already has a PIN set.
@@ -195,7 +195,7 @@
                 const std::string& new_pin,
                 const KeyAgreementResponse& peer_key);
 
-  friend std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+  friend std::pair<CtapRequestCommand, std::optional<cbor::Value>>
   AsCTAPRequestValuePair(const ChangeRequest&);
 
  private:
@@ -244,7 +244,7 @@
   PinTokenRequest(const PinTokenRequest&) = delete;
   virtual ~PinTokenRequest();
 
-  friend std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+  friend std::pair<CtapRequestCommand, std::optional<cbor::Value>>
   AsCTAPRequestValuePair(const PinTokenRequest&);
 
  protected:
@@ -257,35 +257,35 @@
                                  const std::string& pin,
                                  const KeyAgreementResponse& peer_key,
                                  base::span<const pin::Permissions> permissions,
-                                 const absl::optional<std::string> rp_id);
+                                 const std::optional<std::string> rp_id);
   PinTokenWithPermissionsRequest(PinTokenWithPermissionsRequest&&);
   PinTokenWithPermissionsRequest(const PinTokenWithPermissionsRequest&) =
       delete;
   ~PinTokenWithPermissionsRequest() override;
 
-  friend std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+  friend std::pair<CtapRequestCommand, std::optional<cbor::Value>>
   AsCTAPRequestValuePair(const PinTokenWithPermissionsRequest&);
 
  private:
   uint8_t permissions_;
-  absl::optional<std::string> rp_id_;
+  std::optional<std::string> rp_id_;
 };
 
 class UvTokenRequest : public TokenRequest {
  public:
   UvTokenRequest(PINUVAuthProtocol protocol,
                  const KeyAgreementResponse& peer_key,
-                 absl::optional<std::string> rp_id,
+                 std::optional<std::string> rp_id,
                  base::span<const pin::Permissions> permissions);
   UvTokenRequest(UvTokenRequest&&);
   UvTokenRequest(const UvTokenRequest&) = delete;
   virtual ~UvTokenRequest();
 
-  friend std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+  friend std::pair<CtapRequestCommand, std::optional<cbor::Value>>
   AsCTAPRequestValuePair(const UvTokenRequest&);
 
  private:
-  absl::optional<std::string> rp_id_;
+  std::optional<std::string> rp_id_;
   uint8_t permissions_;
 };
 
@@ -294,12 +294,12 @@
   HMACSecretRequest(PINUVAuthProtocol protocol,
                     const KeyAgreementResponse& peer_key,
                     base::span<const uint8_t, 32> salt1,
-                    const absl::optional<std::array<uint8_t, 32>>& salt2);
+                    const std::optional<std::array<uint8_t, 32>>& salt2);
   HMACSecretRequest(const HMACSecretRequest&);
   ~HMACSecretRequest();
   HMACSecretRequest& operator=(const HMACSecretRequest&);
 
-  absl::optional<std::vector<uint8_t>> Decrypt(
+  std::optional<std::vector<uint8_t>> Decrypt(
       base::span<const uint8_t> ciphertext);
 
  private:
@@ -323,10 +323,10 @@
   TokenResponse(const TokenResponse&);
   TokenResponse& operator=(const TokenResponse&);
 
-  static absl::optional<TokenResponse> Parse(
+  static std::optional<TokenResponse> Parse(
       PINUVAuthProtocol protocol,
       base::span<const uint8_t> shared_key,
-      const absl::optional<cbor::Value>& cbor);
+      const std::optional<cbor::Value>& cbor);
 
   std::pair<PINUVAuthProtocol, std::vector<uint8_t>> PinAuth(
       base::span<const uint8_t> client_data_hash) const;
@@ -341,25 +341,25 @@
   std::vector<uint8_t> token_;
 };
 
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const PinRetriesRequest&);
 
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const UvRetriesRequest&);
 
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const KeyAgreementRequest&);
 
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const SetRequest&);
 
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const ChangeRequest&);
 
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const ResetRequest&);
 
-std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
+std::pair<CtapRequestCommand, std::optional<cbor::Value>>
 AsCTAPRequestValuePair(const TokenRequest&);
 
 }  // namespace pin
diff --git a/device/fido/pin_internal.cc b/device/fido/pin_internal.cc
index 3981c46..550456b 100644
--- a/device/fido/pin_internal.cc
+++ b/device/fido/pin_internal.cc
@@ -25,7 +25,7 @@
 namespace device {
 namespace pin {
 
-absl::optional<bssl::UniquePtr<EC_POINT>> PointFromKeyAgreementResponse(
+std::optional<bssl::UniquePtr<EC_POINT>> PointFromKeyAgreementResponse(
     const EC_GROUP* group,
     const KeyAgreementResponse& response) {
   bssl::UniquePtr<EC_POINT> ret(EC_POINT_new(group));
@@ -38,7 +38,7 @@
                                           y_bn.get(), nullptr /* ctx */) == 1;
 
   if (!on_curve) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return ret;
@@ -55,7 +55,7 @@
       std::vector<uint8_t>* out_shared_key) const override {
     bssl::UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
     CHECK(EC_KEY_generate_key(key.get()));
-    absl::optional<bssl::UniquePtr<EC_POINT>> peers_point =
+    std::optional<bssl::UniquePtr<EC_POINT>> peers_point =
         PointFromKeyAgreementResponse(EC_KEY_get0_group(key.get()), peers_key);
     *out_shared_key = CalculateSharedKey(key.get(), peers_point->get());
     // KeyAgreementResponse parsing ensures that the point is on the curve.
diff --git a/device/fido/pin_internal.h b/device/fido/pin_internal.h
index d6d916a..423e1ea 100644
--- a/device/fido/pin_internal.h
+++ b/device/fido/pin_internal.h
@@ -12,6 +12,7 @@
 #include <stdint.h>
 
 #include <array>
+#include <optional>
 #include <vector>
 
 #include "base/component_export.h"
@@ -19,7 +20,6 @@
 #include "components/cbor/values.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/pin.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/boringssl/src/include/openssl/base.h"
 
 namespace device {
@@ -67,7 +67,7 @@
 // PointFromKeyAgreementResponse returns an |EC_POINT| that represents the same
 // P-256 point as |response|. It returns |nullopt| if |response| encodes an
 // invalid point.
-absl::optional<bssl::UniquePtr<EC_POINT>> PointFromKeyAgreementResponse(
+std::optional<bssl::UniquePtr<EC_POINT>> PointFromKeyAgreementResponse(
     const EC_GROUP* group,
     const KeyAgreementResponse& response);
 
diff --git a/device/fido/pin_unittest.cc b/device/fido/pin_unittest.cc
index 7ad313d..3f503bb 100644
--- a/device/fido/pin_unittest.cc
+++ b/device/fido/pin_unittest.cc
@@ -39,7 +39,7 @@
                                 POINT_CONVERSION_UNCOMPRESSED, peer_x962.data(),
                                 peer_x962.size(), nullptr /* BN_CTX */),
              peer_x962.size());
-    const absl::optional<pin::KeyAgreementResponse> peer_response =
+    const std::optional<pin::KeyAgreementResponse> peer_response =
         pin::KeyAgreementResponse::ParseFromCOSE(
             pin::EncodeCOSEPublicKey(peer_x962));
     CHECK(peer_response);
diff --git a/device/fido/prf_input.cc b/device/fido/prf_input.cc
index 637949d..aea5bc9 100644
--- a/device/fido/prf_input.cc
+++ b/device/fido/prf_input.cc
@@ -28,26 +28,26 @@
 PRFInput::~PRFInput() = default;
 
 // static
-absl::optional<PRFInput> PRFInput::FromCBOR(const cbor::Value& v) {
+std::optional<PRFInput> PRFInput::FromCBOR(const cbor::Value& v) {
   if (!v.is_map()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   const cbor::Value::MapValue& map = v.GetMap();
   const auto first_it = map.find(cbor::Value(kExtensionPRFFirst));
   if (first_it == map.end()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   PRFInput ret;
   if (!CBORToPRFValue(first_it->second, &ret.salt1)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   const auto second_it = map.find(cbor::Value(kExtensionPRFSecond));
   if (second_it != map.end()) {
     ret.salt2.emplace();
     if (!CBORToPRFValue(second_it->second, &ret.salt2.value())) {
-      return absl::nullopt;
+      return std::nullopt;
     }
   }
   return ret;
diff --git a/device/fido/prf_input.h b/device/fido/prf_input.h
index 2a8c62c..27a65436 100644
--- a/device/fido/prf_input.h
+++ b/device/fido/prf_input.h
@@ -8,11 +8,11 @@
 #include <stdint.h>
 
 #include <array>
+#include <optional>
 #include <vector>
 
 #include "base/component_export.h"
 #include "components/cbor/values.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -25,13 +25,13 @@
   PRFInput& operator=(const PRFInput&);
   ~PRFInput();
 
-  static absl::optional<PRFInput> FromCBOR(const cbor::Value& v);
+  static std::optional<PRFInput> FromCBOR(const cbor::Value& v);
 
   cbor::Value::MapValue ToCBOR() const;
 
-  absl::optional<std::vector<uint8_t>> credential_id;
+  std::optional<std::vector<uint8_t>> credential_id;
   std::array<uint8_t, 32> salt1;
-  absl::optional<std::array<uint8_t, 32>> salt2;
+  std::optional<std::array<uint8_t, 32>> salt2;
 };
 
 }  // namespace device
diff --git a/device/fido/public_key.cc b/device/fido/public_key.cc
index e242be8..e4bb76e9 100644
--- a/device/fido/public_key.cc
+++ b/device/fido/public_key.cc
@@ -4,16 +4,17 @@
 
 #include "device/fido/public_key.h"
 
+#include <optional>
 #include <vector>
+
 #include "base/containers/span.h"
 #include "device/fido/fido_parsing_utils.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
 PublicKey::PublicKey(int32_t in_algorithm,
                      base::span<const uint8_t> in_cose_key_bytes,
-                     absl::optional<std::vector<uint8_t>> in_der_bytes)
+                     std::optional<std::vector<uint8_t>> in_der_bytes)
     : algorithm(in_algorithm),
       cose_key_bytes(fido_parsing_utils::Materialize(in_cose_key_bytes)),
       der_bytes(std::move(in_der_bytes)) {}
diff --git a/device/fido/public_key.h b/device/fido/public_key.h
index afe4d8ef..2be6056 100644
--- a/device/fido/public_key.h
+++ b/device/fido/public_key.h
@@ -6,11 +6,12 @@
 #define DEVICE_FIDO_PUBLIC_KEY_H_
 
 #include <stdint.h>
+
+#include <optional>
 #include <vector>
 
 #include "base/component_export.h"
 #include "base/containers/span.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -18,7 +19,7 @@
 struct COMPONENT_EXPORT(DEVICE_FIDO) PublicKey {
   PublicKey(int32_t algorithm,
             base::span<const uint8_t> cbor_bytes,
-            absl::optional<std::vector<uint8_t>> der_bytes);
+            std::optional<std::vector<uint8_t>> der_bytes);
 
   PublicKey(const PublicKey&) = delete;
   PublicKey& operator=(const PublicKey&) = delete;
@@ -36,7 +37,7 @@
   // public key, if possible. (WebAuthn can negotiate the use of unknown
   // public-key algorithms so not all public keys can be transformed into SPKI
   // form.)
-  const absl::optional<std::vector<uint8_t>> der_bytes;
+  const std::optional<std::vector<uint8_t>> der_bytes;
 };
 
 }  // namespace device
diff --git a/device/fido/public_key_credential_descriptor.cc b/device/fido/public_key_credential_descriptor.cc
index 3e248d2..69df585 100644
--- a/device/fido/public_key_credential_descriptor.cc
+++ b/device/fido/public_key_credential_descriptor.cc
@@ -18,21 +18,21 @@
 }  // namespace
 
 // static
-absl::optional<PublicKeyCredentialDescriptor>
+std::optional<PublicKeyCredentialDescriptor>
 PublicKeyCredentialDescriptor::CreateFromCBORValue(const cbor::Value& cbor) {
   if (!cbor.is_map()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   const cbor::Value::MapValue& map = cbor.GetMap();
   auto type = map.find(cbor::Value(kCredentialTypeKey));
   if (type == map.end() || !type->second.is_string() ||
       type->second.GetString() != kPublicKey)
-    return absl::nullopt;
+    return std::nullopt;
 
   auto id = map.find(cbor::Value(kCredentialIdKey));
   if (id == map.end() || !id->second.is_bytestring())
-    return absl::nullopt;
+    return std::nullopt;
 
   auto ret = PublicKeyCredentialDescriptor(CredentialType::kPublicKey,
                                            id->second.GetBytestring());
diff --git a/device/fido/public_key_credential_descriptor.h b/device/fido/public_key_credential_descriptor.h
index d6b40f93..2de9b849 100644
--- a/device/fido/public_key_credential_descriptor.h
+++ b/device/fido/public_key_credential_descriptor.h
@@ -6,6 +6,8 @@
 #define DEVICE_FIDO_PUBLIC_KEY_CREDENTIAL_DESCRIPTOR_H_
 
 #include <stdint.h>
+
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -14,7 +16,6 @@
 #include "components/cbor/values.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_transport_protocol.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -24,7 +25,7 @@
 // AuthenticatorGetAssertion command.
 class COMPONENT_EXPORT(DEVICE_FIDO) PublicKeyCredentialDescriptor {
  public:
-  static absl::optional<PublicKeyCredentialDescriptor> CreateFromCBORValue(
+  static std::optional<PublicKeyCredentialDescriptor> CreateFromCBORValue(
       const cbor::Value& cbor);
 
   PublicKeyCredentialDescriptor();
diff --git a/device/fido/public_key_credential_params.cc b/device/fido/public_key_credential_params.cc
index 744b552..89e8588 100644
--- a/device/fido/public_key_credential_params.cc
+++ b/device/fido/public_key_credential_params.cc
@@ -16,15 +16,15 @@
 }
 
 // static
-absl::optional<PublicKeyCredentialParams>
+std::optional<PublicKeyCredentialParams>
 PublicKeyCredentialParams::CreateFromCBORValue(const cbor::Value& cbor_value) {
   if (!cbor_value.is_array())
-    return absl::nullopt;
+    return std::nullopt;
 
   std::vector<PublicKeyCredentialParams::CredentialInfo> credential_params;
   for (const auto& credential : cbor_value.GetArray()) {
     if (!credential.is_map() || credential.GetMap().size() != 2)
-      return absl::nullopt;
+      return std::nullopt;
 
     const auto& credential_map = credential.GetMap();
     const auto credential_type_it =
@@ -39,7 +39,7 @@
         !algorithm_type_it->second.is_integer() ||
         !base::IsValueInRangeForNumericType<int32_t>(
             algorithm_type_it->second.GetInteger())) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     credential_params.push_back(PublicKeyCredentialParams::CredentialInfo{
diff --git a/device/fido/public_key_credential_params.h b/device/fido/public_key_credential_params.h
index fe0c3b0..983baf7 100644
--- a/device/fido/public_key_credential_params.h
+++ b/device/fido/public_key_credential_params.h
@@ -5,6 +5,7 @@
 #ifndef DEVICE_FIDO_PUBLIC_KEY_CREDENTIAL_PARAMS_H_
 #define DEVICE_FIDO_PUBLIC_KEY_CREDENTIAL_PARAMS_H_
 
+#include <optional>
 #include <string>
 #include <tuple>
 #include <vector>
@@ -13,7 +14,6 @@
 #include "base/numerics/safe_conversions.h"
 #include "components/cbor/values.h"
 #include "device/fido/fido_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -30,7 +30,7 @@
         base::strict_cast<int32_t>(CoseAlgorithmIdentifier::kEs256);
   };
 
-  static absl::optional<PublicKeyCredentialParams> CreateFromCBORValue(
+  static std::optional<PublicKeyCredentialParams> CreateFromCBORValue(
       const cbor::Value& cbor_value);
 
   explicit PublicKeyCredentialParams(
diff --git a/device/fido/public_key_credential_rp_entity.cc b/device/fido/public_key_credential_rp_entity.cc
index 6e15ccaa..a9f2854 100644
--- a/device/fido/public_key_credential_rp_entity.cc
+++ b/device/fido/public_key_credential_rp_entity.cc
@@ -12,24 +12,24 @@
 namespace device {
 
 // static
-absl::optional<PublicKeyCredentialRpEntity>
+std::optional<PublicKeyCredentialRpEntity>
 PublicKeyCredentialRpEntity::CreateFromCBORValue(const cbor::Value& cbor) {
   if (!cbor.is_map()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   const cbor::Value::MapValue& rp_map = cbor.GetMap();
   for (const auto& element : rp_map) {
     if (!element.first.is_string() || !element.second.is_string()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     const std::string& key = element.first.GetString();
     if (key != kEntityIdMapKey && key != kEntityNameMapKey) {
-      return absl::nullopt;
+      return std::nullopt;
     }
   }
   const auto id_it = rp_map.find(cbor::Value(kEntityIdMapKey));
   if (id_it == rp_map.end()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   PublicKeyCredentialRpEntity rp(id_it->second.GetString());
   const auto name_it = rp_map.find(cbor::Value(kEntityNameMapKey));
@@ -46,7 +46,7 @@
 
 PublicKeyCredentialRpEntity::PublicKeyCredentialRpEntity(
     std::string id_,
-    absl::optional<std::string> name_)
+    std::optional<std::string> name_)
     : id(std::move(id_)), name(std::move(name_)) {}
 
 PublicKeyCredentialRpEntity::PublicKeyCredentialRpEntity(
diff --git a/device/fido/public_key_credential_rp_entity.h b/device/fido/public_key_credential_rp_entity.h
index d59250fd..22fcf24d 100644
--- a/device/fido/public_key_credential_rp_entity.h
+++ b/device/fido/public_key_credential_rp_entity.h
@@ -5,12 +5,12 @@
 #ifndef DEVICE_FIDO_PUBLIC_KEY_CREDENTIAL_RP_ENTITY_H_
 #define DEVICE_FIDO_PUBLIC_KEY_CREDENTIAL_RP_ENTITY_H_
 
+#include <optional>
 #include <string>
 #include <vector>
 
 #include "base/component_export.h"
 #include "components/cbor/values.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -20,12 +20,12 @@
 // https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialrpentity
 struct COMPONENT_EXPORT(DEVICE_FIDO) PublicKeyCredentialRpEntity {
  public:
-  static absl::optional<PublicKeyCredentialRpEntity> CreateFromCBORValue(
+  static std::optional<PublicKeyCredentialRpEntity> CreateFromCBORValue(
       const cbor::Value& cbor);
 
   PublicKeyCredentialRpEntity();
   explicit PublicKeyCredentialRpEntity(std::string id);
-  PublicKeyCredentialRpEntity(std::string id, absl::optional<std::string> name);
+  PublicKeyCredentialRpEntity(std::string id, std::optional<std::string> name);
   PublicKeyCredentialRpEntity(const PublicKeyCredentialRpEntity& other);
   PublicKeyCredentialRpEntity(PublicKeyCredentialRpEntity&& other);
   PublicKeyCredentialRpEntity& operator=(
@@ -35,7 +35,7 @@
   ~PublicKeyCredentialRpEntity();
 
   std::string id;
-  absl::optional<std::string> name;
+  std::optional<std::string> name;
 };
 
 cbor::Value AsCBOR(const PublicKeyCredentialRpEntity&);
diff --git a/device/fido/public_key_credential_user_entity.cc b/device/fido/public_key_credential_user_entity.cc
index 0eca1d8..a3b8774 100644
--- a/device/fido/public_key_credential_user_entity.cc
+++ b/device/fido/public_key_credential_user_entity.cc
@@ -12,16 +12,16 @@
 namespace device {
 
 // static
-absl::optional<PublicKeyCredentialUserEntity>
+std::optional<PublicKeyCredentialUserEntity>
 PublicKeyCredentialUserEntity::CreateFromCBORValue(const cbor::Value& cbor) {
   if (!cbor.is_map())
-    return absl::nullopt;
+    return std::nullopt;
 
   const cbor::Value::MapValue& cbor_map = cbor.GetMap();
 
   auto id_it = cbor_map.find(cbor::Value(kEntityIdMapKey));
   if (id_it == cbor_map.end() || !id_it->second.is_bytestring())
-    return absl::nullopt;
+    return std::nullopt;
 
   PublicKeyCredentialUserEntity user(id_it->second.GetBytestring());
 
@@ -31,7 +31,7 @@
   auto name_it = cbor_map.find(cbor::Value(kEntityNameMapKey));
   if (name_it != cbor_map.end()) {
     if (!name_it->second.is_string()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     user.name = name_it->second.GetString();
   }
@@ -39,7 +39,7 @@
   auto display_name_it = cbor_map.find(cbor::Value(kDisplayNameMapKey));
   if (display_name_it != cbor_map.end()) {
     if (!display_name_it->second.is_string()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     user.display_name = display_name_it->second.GetString();
   }
@@ -55,8 +55,8 @@
 
 PublicKeyCredentialUserEntity::PublicKeyCredentialUserEntity(
     std::vector<uint8_t> id_,
-    absl::optional<std::string> name_,
-    absl::optional<std::string> display_name_)
+    std::optional<std::string> name_,
+    std::optional<std::string> display_name_)
     : id(std::move(id_)),
       name(std::move(name_)),
       display_name(std::move(display_name_)) {}
diff --git a/device/fido/public_key_credential_user_entity.h b/device/fido/public_key_credential_user_entity.h
index 88e55474..d9bbddc 100644
--- a/device/fido/public_key_credential_user_entity.h
+++ b/device/fido/public_key_credential_user_entity.h
@@ -7,12 +7,12 @@
 
 #include <stdint.h>
 
+#include <optional>
 #include <string>
 #include <vector>
 
 #include "base/component_export.h"
 #include "components/cbor/values.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -22,14 +22,14 @@
 // request.
 class COMPONENT_EXPORT(DEVICE_FIDO) PublicKeyCredentialUserEntity {
  public:
-  static absl::optional<PublicKeyCredentialUserEntity> CreateFromCBORValue(
+  static std::optional<PublicKeyCredentialUserEntity> CreateFromCBORValue(
       const cbor::Value& cbor);
 
   PublicKeyCredentialUserEntity();
   explicit PublicKeyCredentialUserEntity(std::vector<uint8_t> id);
   PublicKeyCredentialUserEntity(std::vector<uint8_t> id,
-                                absl::optional<std::string> name,
-                                absl::optional<std::string> display_name);
+                                std::optional<std::string> name,
+                                std::optional<std::string> display_name);
   PublicKeyCredentialUserEntity(const PublicKeyCredentialUserEntity& other);
   PublicKeyCredentialUserEntity(PublicKeyCredentialUserEntity&& other);
   PublicKeyCredentialUserEntity& operator=(
@@ -40,8 +40,8 @@
   ~PublicKeyCredentialUserEntity();
 
   std::vector<uint8_t> id;
-  absl::optional<std::string> name;
-  absl::optional<std::string> display_name;
+  std::optional<std::string> name;
+  std::optional<std::string> display_name;
 };
 
 cbor::Value AsCBOR(const PublicKeyCredentialUserEntity&);
diff --git a/device/fido/reset_request_handler.cc b/device/fido/reset_request_handler.cc
index c81bf77..20384f3 100644
--- a/device/fido/reset_request_handler.cc
+++ b/device/fido/reset_request_handler.cc
@@ -61,7 +61,7 @@
 
 void ResetRequestHandler::OnResetComplete(
     CtapDeviceResponseCode status,
-    absl::optional<pin::EmptyResponse> response) {
+    std::optional<pin::EmptyResponse> response) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
   DCHECK(processed_touch_);
 
diff --git a/device/fido/reset_request_handler.h b/device/fido/reset_request_handler.h
index 2a76c89..0839490 100644
--- a/device/fido/reset_request_handler.h
+++ b/device/fido/reset_request_handler.h
@@ -58,7 +58,7 @@
 
   void OnTouch(FidoAuthenticator* authenticator);
   void OnResetComplete(CtapDeviceResponseCode status,
-                       absl::optional<pin::EmptyResponse> response);
+                       std::optional<pin::EmptyResponse> response);
 
   ResetSentCallback reset_sent_callback_;
   FinishedCallback finished_callback_;
diff --git a/device/fido/set_pin_request_handler.cc b/device/fido/set_pin_request_handler.cc
index f77dc67..1d12b1d 100644
--- a/device/fido/set_pin_request_handler.cc
+++ b/device/fido/set_pin_request_handler.cc
@@ -109,14 +109,14 @@
       CancelActiveAuthenticators(authenticator->GetId());
       std::move(get_pin_callback_)
           .Run(authenticator->CurrentMinPINLength(),
-               authenticator->NewMinPINLength(), absl::nullopt);
+               authenticator->NewMinPINLength(), std::nullopt);
       break;
   }
 }
 
 void SetPINRequestHandler::OnRetriesResponse(
     CtapDeviceResponseCode status,
-    absl::optional<pin::RetriesResponse> response) {
+    std::optional<pin::RetriesResponse> response) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
   DCHECK_EQ(state_, State::kGettingRetries);
 
@@ -134,7 +134,7 @@
 
 void SetPINRequestHandler::OnSetPINComplete(
     CtapDeviceResponseCode status,
-    absl::optional<pin::EmptyResponse> response) {
+    std::optional<pin::EmptyResponse> response) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
   DCHECK_EQ(state_, State::kSettingPIN);
 
diff --git a/device/fido/set_pin_request_handler.h b/device/fido/set_pin_request_handler.h
index 28495c3..7cb0474 100644
--- a/device/fido/set_pin_request_handler.h
+++ b/device/fido/set_pin_request_handler.h
@@ -44,7 +44,7 @@
   using GetPINCallback =
       base::OnceCallback<void(uint32_t current_min_pin_length,
                               uint32_t new_min_pin_length,
-                              absl::optional<int64_t> attempts)>;
+                              std::optional<int64_t> attempts)>;
 
   // FinishedCallback is called multiple times once an attempt has completed.
   // This can be called prior to |GetPINCallback| if the touched authenticator
@@ -95,10 +95,10 @@
   void OnTouch(FidoAuthenticator* authenticator);
   void RequestRetries();
   void OnRetriesResponse(CtapDeviceResponseCode status,
-                         absl::optional<pin::RetriesResponse> response);
+                         std::optional<pin::RetriesResponse> response);
 
   void OnSetPINComplete(CtapDeviceResponseCode status,
-                        absl::optional<pin::EmptyResponse> response);
+                        std::optional<pin::EmptyResponse> response);
 
   State state_ = State::kWaitingForTouch;
   GetPINCallback get_pin_callback_;
diff --git a/device/fido/test_callback_receiver.h b/device/fido/test_callback_receiver.h
index 9773db7..1b1a720 100644
--- a/device/fido/test_callback_receiver.h
+++ b/device/fido/test_callback_receiver.h
@@ -5,6 +5,7 @@
 #ifndef DEVICE_FIDO_TEST_CALLBACK_RECEIVER_H_
 #define DEVICE_FIDO_TEST_CALLBACK_RECEIVER_H_
 
+#include <optional>
 #include <tuple>
 #include <type_traits>
 #include <utility>
@@ -13,7 +14,6 @@
 #include "base/functional/callback.h"
 #include "base/functional/callback_helpers.h"
 #include "base/run_loop.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 namespace test {
@@ -53,7 +53,7 @@
 
   // The result, which is non-null exactly if the callback was already invoked
   // and the result has not yet been taken with TakeResult().
-  const absl::optional<TupleOfNonReferenceArgs>& result() const {
+  const std::optional<TupleOfNonReferenceArgs>& result() const {
     return result_;
   }
 
@@ -91,7 +91,7 @@
 
   bool was_called_ = false;
   base::RunLoop wait_for_callback_loop_;
-  absl::optional<TupleOfNonReferenceArgs> result_;
+  std::optional<TupleOfNonReferenceArgs> result_;
 };
 
 template <class Value>
diff --git a/device/fido/u2f_command_constructor.cc b/device/fido/u2f_command_constructor.cc
index 9bc1ca45..a7e4197c 100644
--- a/device/fido/u2f_command_constructor.cc
+++ b/device/fido/u2f_command_constructor.cc
@@ -41,10 +41,10 @@
          !request.allow_list.empty();
 }
 
-absl::optional<std::vector<uint8_t>> ConvertToU2fRegisterCommand(
+std::optional<std::vector<uint8_t>> ConvertToU2fRegisterCommand(
     const CtapMakeCredentialRequest& request) {
   if (!IsConvertibleToU2fRegisterCommand(request))
-    return absl::nullopt;
+    return std::nullopt;
 
   if (request.pin_auth && request.pin_auth->size() == 0) {
     // An empty pin_auth in CTAP2 indicates that the device should just wait
@@ -60,7 +60,7 @@
       request.client_data_hash, is_individual_attestation);
 }
 
-absl::optional<std::vector<uint8_t>> ConvertToU2fSignCommandWithBogusChallenge(
+std::optional<std::vector<uint8_t>> ConvertToU2fSignCommandWithBogusChallenge(
     const CtapMakeCredentialRequest& request,
     base::span<const uint8_t> key_handle) {
   return ConstructU2fSignCommand(
@@ -68,12 +68,12 @@
       kBogusChallenge, key_handle);
 }
 
-absl::optional<std::vector<uint8_t>> ConvertToU2fSignCommand(
+std::optional<std::vector<uint8_t>> ConvertToU2fSignCommand(
     const CtapGetAssertionRequest& request,
     ApplicationParameterType application_parameter_type,
     base::span<const uint8_t> key_handle) {
   if (!IsConvertibleToU2fSignCommand(request))
-    return absl::nullopt;
+    return std::nullopt;
 
   const auto& application_parameter =
       application_parameter_type == ApplicationParameterType::kPrimary
@@ -103,12 +103,12 @@
   return command.GetEncodedCommand();
 }
 
-absl::optional<std::vector<uint8_t>> ConstructU2fSignCommand(
+std::optional<std::vector<uint8_t>> ConstructU2fSignCommand(
     base::span<const uint8_t, kU2fApplicationParamLength> application_parameter,
     base::span<const uint8_t, kU2fChallengeParamLength> challenge_parameter,
     base::span<const uint8_t> key_handle) {
   if (key_handle.size() > kMaxKeyHandleLength) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   std::vector<uint8_t> data;
diff --git a/device/fido/u2f_command_constructor.h b/device/fido/u2f_command_constructor.h
index 68cf832..b49d17e 100644
--- a/device/fido/u2f_command_constructor.h
+++ b/device/fido/u2f_command_constructor.h
@@ -7,13 +7,13 @@
 
 #include <stdint.h>
 
+#include <optional>
 #include <vector>
 
 #include "base/component_export.h"
 #include "base/containers/span.h"
 #include "device/fido/ctap_get_assertion_request.h"
 #include "device/fido/ctap_make_credential_request.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -41,19 +41,19 @@
 
 // Extracts APDU encoded U2F register command from CtapMakeCredentialRequest.
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<std::vector<uint8_t>> ConvertToU2fRegisterCommand(
+std::optional<std::vector<uint8_t>> ConvertToU2fRegisterCommand(
     const CtapMakeCredentialRequest& request);
 
 // Turns a CtapMakeCredentialRequest into an APDU encoded U2F sign command
 // for the same RP and key handle, but a bogus challenge.
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<std::vector<uint8_t>> ConvertToU2fSignCommandWithBogusChallenge(
+std::optional<std::vector<uint8_t>> ConvertToU2fSignCommandWithBogusChallenge(
     const CtapMakeCredentialRequest& request,
     base::span<const uint8_t> key_handle);
 
 // Extracts APDU encoded U2F sign command from CtapGetAssertionRequest.
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<std::vector<uint8_t>> ConvertToU2fSignCommand(
+std::optional<std::vector<uint8_t>> ConvertToU2fSignCommand(
     const CtapGetAssertionRequest& request,
     ApplicationParameterType application_parameter_type,
     base::span<const uint8_t> key_handle);
@@ -69,7 +69,7 @@
 // TODO(hongjunchoi): Move this logic inside ConvertToU2fSignCommand() once
 // U2fSign is deleted.
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<std::vector<uint8_t>> ConstructU2fSignCommand(
+std::optional<std::vector<uint8_t>> ConstructU2fSignCommand(
     base::span<const uint8_t, kU2fApplicationParamLength> application_parameter,
     base::span<const uint8_t, kU2fChallengeParamLength> challenge_parameter,
     base::span<const uint8_t> key_handle);
diff --git a/device/fido/u2f_register_operation.cc b/device/fido/u2f_register_operation.cc
index 1748c430..02f40b0b 100644
--- a/device/fido/u2f_register_operation.cc
+++ b/device/fido/u2f_register_operation.cc
@@ -52,7 +52,7 @@
 }
 
 void U2fRegisterOperation::TrySign() {
-  absl::optional<std::vector<uint8_t>> sign_command;
+  std::optional<std::vector<uint8_t>> sign_command;
   if (probing_alternative_rp_id_) {
     CtapMakeCredentialRequest sign_request(request());
     sign_request.rp.id = *request().app_id_exclude;
@@ -70,7 +70,7 @@
 }
 
 void U2fRegisterOperation::OnCheckForExcludedKeyHandle(
-    absl::optional<std::vector<uint8_t>> device_response) {
+    std::optional<std::vector<uint8_t>> device_response) {
   if (canceled_) {
     return;
   }
@@ -97,7 +97,7 @@
       // user-presence.
       std::move(callback())
           .Run(CtapDeviceResponseCode::kCtap2ErrCredentialExcluded,
-               absl::nullopt);
+               std::nullopt);
       break;
 
     case apdu::ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED:
@@ -135,7 +135,7 @@
       FIDO_LOG(ERROR) << "Unexpected status " << static_cast<int>(result)
                       << " from U2F device";
       std::move(callback())
-          .Run(CtapDeviceResponseCode::kCtap2ErrOther, absl::nullopt);
+          .Run(CtapDeviceResponseCode::kCtap2ErrOther, std::nullopt);
       break;
   }
 }
@@ -153,7 +153,7 @@
 }
 
 void U2fRegisterOperation::OnRegisterResponseReceived(
-    absl::optional<std::vector<uint8_t>> device_response) {
+    std::optional<std::vector<uint8_t>> device_response) {
   if (canceled_) {
     return;
   }
@@ -162,7 +162,7 @@
   const auto apdu_response =
       device_response
           ? apdu::ApduResponse::CreateFromMessage(std::move(*device_response))
-          : absl::nullopt;
+          : std::nullopt;
   if (apdu_response) {
     result = apdu_response->status();
   }
@@ -196,7 +196,7 @@
       FIDO_LOG(ERROR) << "Unexpected status " << static_cast<int>(result)
                       << " from U2F device";
       std::move(callback())
-          .Run(CtapDeviceResponseCode::kCtap2ErrOther, absl::nullopt);
+          .Run(CtapDeviceResponseCode::kCtap2ErrOther, std::nullopt);
       break;
   }
 }
diff --git a/device/fido/u2f_register_operation.h b/device/fido/u2f_register_operation.h
index 88541d76..6a7a214d 100644
--- a/device/fido/u2f_register_operation.h
+++ b/device/fido/u2f_register_operation.h
@@ -7,13 +7,13 @@
 
 #include <stdint.h>
 
+#include <optional>
 #include <vector>
 
 #include "base/component_export.h"
 #include "base/memory/weak_ptr.h"
 #include "device/fido/ctap_make_credential_request.h"
 #include "device/fido/device_operation.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -50,11 +50,11 @@
   void WinkAndTrySign();
   void TrySign();
   void OnCheckForExcludedKeyHandle(
-      absl::optional<std::vector<uint8_t>> device_response);
+      std::optional<std::vector<uint8_t>> device_response);
   void WinkAndTryRegistration();
   void TryRegistration();
   void OnRegisterResponseReceived(
-      absl::optional<std::vector<uint8_t>> device_response);
+      std::optional<std::vector<uint8_t>> device_response);
   const std::vector<uint8_t>& excluded_key_handle() const;
 
   size_t current_key_handle_index_ = 0;
diff --git a/device/fido/u2f_register_operation_unittest.cc b/device/fido/u2f_register_operation_unittest.cc
index 9222956..2afb05a4 100644
--- a/device/fido/u2f_register_operation_unittest.cc
+++ b/device/fido/u2f_register_operation_unittest.cc
@@ -56,7 +56,7 @@
 
 using TestRegisterCallback = ::device::test::StatusAndValueCallbackReceiver<
     CtapDeviceResponseCode,
-    absl::optional<AuthenticatorMakeCredentialResponse>>;
+    std::optional<AuthenticatorMakeCredentialResponse>>;
 
 }  // namespace
 
diff --git a/device/fido/u2f_sign_operation.cc b/device/fido/u2f_sign_operation.cc
index 6f274761..cef1a7ca 100644
--- a/device/fido/u2f_sign_operation.cc
+++ b/device/fido/u2f_sign_operation.cc
@@ -60,7 +60,7 @@
 }
 
 void U2fSignOperation::OnSignResponseReceived(
-    absl::optional<std::vector<uint8_t>> device_response) {
+    std::optional<std::vector<uint8_t>> device_response) {
   if (canceled_) {
     return;
   }
@@ -69,7 +69,7 @@
   const auto apdu_response =
       device_response
           ? apdu::ApduResponse::CreateFromMessage(std::move(*device_response))
-          : absl::nullopt;
+          : std::nullopt;
   if (apdu_response) {
     result = apdu_response->status();
   }
@@ -93,7 +93,7 @@
               key_handle(), device()->DeviceTransport());
       if (!sign_response) {
         std::move(callback())
-            .Run(CtapDeviceResponseCode::kCtap2ErrOther, absl::nullopt);
+            .Run(CtapDeviceResponseCode::kCtap2ErrOther, std::nullopt);
         return;
       }
 
@@ -137,7 +137,7 @@
     default:
       // Some sort of failure occurred. Abandon this device and move on.
       std::move(callback())
-          .Run(CtapDeviceResponseCode::kCtap2ErrOther, absl::nullopt);
+          .Run(CtapDeviceResponseCode::kCtap2ErrOther, std::nullopt);
       return;
   }
 }
@@ -155,7 +155,7 @@
 }
 
 void U2fSignOperation::OnEnrollmentResponseReceived(
-    absl::optional<std::vector<uint8_t>> device_response) {
+    std::optional<std::vector<uint8_t>> device_response) {
   if (canceled_) {
     return;
   }
@@ -172,7 +172,7 @@
   switch (result) {
     case apdu::ApduResponse::Status::SW_NO_ERROR:
       std::move(callback())
-          .Run(CtapDeviceResponseCode::kCtap2ErrNoCredentials, absl::nullopt);
+          .Run(CtapDeviceResponseCode::kCtap2ErrNoCredentials, std::nullopt);
       break;
 
     case apdu::ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED:
@@ -187,7 +187,7 @@
     default:
       // Some sort of failure occurred. Abandon this device and move on.
       std::move(callback())
-          .Run(CtapDeviceResponseCode::kCtap2ErrOther, absl::nullopt);
+          .Run(CtapDeviceResponseCode::kCtap2ErrOther, std::nullopt);
       return;
   }
 }
diff --git a/device/fido/u2f_sign_operation.h b/device/fido/u2f_sign_operation.h
index f6ef2a9..208bd564 100644
--- a/device/fido/u2f_sign_operation.h
+++ b/device/fido/u2f_sign_operation.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "base/component_export.h"
@@ -16,7 +17,6 @@
 #include "device/fido/ctap_get_assertion_request.h"
 #include "device/fido/device_operation.h"
 #include "device/fido/fido_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -47,11 +47,11 @@
   void WinkAndTrySign();
   void TrySign();
   void OnSignResponseReceived(
-      absl::optional<std::vector<uint8_t>> device_response);
+      std::optional<std::vector<uint8_t>> device_response);
   void WinkAndTryFakeEnrollment();
   void TryFakeEnrollment();
   void OnEnrollmentResponseReceived(
-      absl::optional<std::vector<uint8_t>> device_response);
+      std::optional<std::vector<uint8_t>> device_response);
   const std::vector<uint8_t>& key_handle() const;
 
   size_t current_key_handle_index_ = 0;
diff --git a/device/fido/u2f_sign_operation_unittest.cc b/device/fido/u2f_sign_operation_unittest.cc
index 094854bb..f476457 100644
--- a/device/fido/u2f_sign_operation_unittest.cc
+++ b/device/fido/u2f_sign_operation_unittest.cc
@@ -29,7 +29,7 @@
 
 using TestSignCallback = ::device::test::StatusAndValueCallbackReceiver<
     CtapDeviceResponseCode,
-    absl::optional<AuthenticatorGetAssertionResponse>>;
+    std::optional<AuthenticatorGetAssertionResponse>>;
 
 }  // namespace
 
@@ -73,7 +73,7 @@
   sign_callback_receiver().WaitForCallback();
   EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
             sign_callback_receiver().status());
-  absl::optional<AuthenticatorGetAssertionResponse> response =
+  std::optional<AuthenticatorGetAssertionResponse> response =
       sign_callback_receiver().TakeValue();
   ASSERT_TRUE(response);
   EXPECT_THAT(response->signature,
diff --git a/device/fido/value_response_conversions.cc b/device/fido/value_response_conversions.cc
index 91852e8..3a1c0255 100644
--- a/device/fido/value_response_conversions.cc
+++ b/device/fido/value_response_conversions.cc
@@ -21,19 +21,19 @@
 
 // Base64url-decodes the value of `key` from `dict`. Returns `nullopt` if the
 // key isn't present or decoding failed.
-absl::optional<std::string> Base64UrlDecodeStringKey(
+std::optional<std::string> Base64UrlDecodeStringKey(
     const base::Value::Dict& dict,
     const std::string& key) {
   const std::string* b64url_data = dict.FindString(key);
   if (!b64url_data) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   std::string decoded;
   if (!base::Base64UrlDecode(*b64url_data,
                              base::Base64UrlDecodePolicy::DISALLOW_PADDING,
                              &decoded)) {
     FIDO_LOG(ERROR) << "Failed to decode key " << key;
-    return absl::nullopt;
+    return std::nullopt;
   }
   return decoded;
 }
@@ -42,19 +42,19 @@
 // which is true when the field is present and correctly parsed, or when the
 // field is absent. The boolean is false when the field is present but does
 // not correctly parse.
-std::tuple<bool, absl::optional<std::string>> Base64UrlDecodeOptionalStringKey(
+std::tuple<bool, std::optional<std::string>> Base64UrlDecodeOptionalStringKey(
     const base::Value::Dict& dict,
     const std::string& key) {
   const base::Value* value = dict.Find(key);
   if (!value) {
-    return {true, absl::nullopt};
+    return {true, std::nullopt};
   }
   std::string decoded;
   if (!value->is_string() ||
       !base::Base64UrlDecode(value->GetString(),
                              base::Base64UrlDecodePolicy::DISALLOW_PADDING,
                              &decoded)) {
-    return {false, absl::nullopt};
+    return {false, std::nullopt};
   }
   return {true, decoded};
 }
@@ -64,13 +64,13 @@
   return std::vector<uint8_t>(in_ptr, in_ptr + in.size());
 }
 
-absl::optional<AuthenticatorData> ReadAuthenticatorData(
+std::optional<AuthenticatorData> ReadAuthenticatorData(
     const base::Value::Dict& dict) {
-  absl::optional<std::string> authenticator_data_opt =
+  std::optional<std::string> authenticator_data_opt =
       Base64UrlDecodeStringKey(dict, "authenticatorData");
   if (!authenticator_data_opt) {
     FIDO_LOG(ERROR) << "Response missing required authenticatorData field.";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   std::vector<uint8_t> authenticator_data_bytes =
@@ -79,18 +79,18 @@
       AuthenticatorData::DecodeAuthenticatorData(authenticator_data_bytes);
   if (!authenticator_data) {
     FIDO_LOG(ERROR) << "Response contained invalid authenticatorData.";
-    return absl::nullopt;
+    return std::nullopt;
   }
   return authenticator_data;
 }
 
 }  // namespace
 
-absl::optional<AuthenticatorGetAssertionResponse>
+std::optional<AuthenticatorGetAssertionResponse>
 AuthenticatorGetAssertionResponseFromValue(const base::Value& value) {
   if (!value.is_dict()) {
     FIDO_LOG(ERROR) << "Assertion response value is not a dict.";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   const base::Value::Dict& response_dict = value.GetDict();
@@ -101,14 +101,14 @@
   // 'attestationObject' is optional and also ignored.
   auto authenticator_data = ReadAuthenticatorData(response_dict);
   if (!authenticator_data) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
-  absl::optional<std::string> signature_opt =
+  std::optional<std::string> signature_opt =
       Base64UrlDecodeStringKey(response_dict, "signature");
   if (!signature_opt) {
     FIDO_LOG(ERROR) << "Assertion response missing required signature field.";
-    return absl::nullopt;
+    return std::nullopt;
   }
   std::vector<uint8_t> signature = ToByteVector(*signature_opt);
 
@@ -116,12 +116,12 @@
       Base64UrlDecodeOptionalStringKey(response_dict, "userHandle");
   if (!success) {
     FIDO_LOG(ERROR) << "Assertion response contained invalid user handle.";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   AuthenticatorGetAssertionResponse response(std::move(*authenticator_data),
                                              std::move(signature),
-                                             /*transport_used=*/absl::nullopt);
+                                             /*transport_used=*/std::nullopt);
   if (user_handle_opt) {
     std::vector<uint8_t> user_handle = ToByteVector(*user_handle_opt);
     response.user_entity =
diff --git a/device/fido/value_response_conversions.h b/device/fido/value_response_conversions.h
index 694caa9..2c833afd 100644
--- a/device/fido/value_response_conversions.h
+++ b/device/fido/value_response_conversions.h
@@ -5,10 +5,11 @@
 #ifndef DEVICE_FIDO_VALUE_RESPONSE_CONVERSIONS_H_
 #define DEVICE_FIDO_VALUE_RESPONSE_CONVERSIONS_H_
 
+#include <optional>
+
 #include "base/component_export.h"
 #include "base/values.h"
 #include "device/fido/authenticator_get_assertion_response.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -16,10 +17,10 @@
 // based on the specified JSON format. It only parses a subset of the fields,
 // since they are creating CTAP-level objects and the JSON is defined for
 // WebAuthn responses.
-// Returns absl::nullopt on error.
+// Returns std::nullopt on error.
 // https://w3c.github.io/webauthn/#dictdef-authenticatorassertionresponsejson
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<AuthenticatorGetAssertionResponse>
+std::optional<AuthenticatorGetAssertionResponse>
 AuthenticatorGetAssertionResponseFromValue(const base::Value& value);
 
 }  // namespace device
diff --git a/device/fido/value_response_conversions_unittest.cc b/device/fido/value_response_conversions_unittest.cc
index 0272361..fcce2ac 100644
--- a/device/fido/value_response_conversions_unittest.cc
+++ b/device/fido/value_response_conversions_unittest.cc
@@ -45,7 +45,7 @@
       deserializer.Deserialize(/*error_code=*/nullptr, &deserialize_error);
   ASSERT_TRUE(value) << deserialize_error;
 
-  absl::optional<AuthenticatorGetAssertionResponse> response =
+  std::optional<AuthenticatorGetAssertionResponse> response =
       AuthenticatorGetAssertionResponseFromValue(*value);
   ASSERT_TRUE(response);
 
@@ -74,7 +74,7 @@
       deserializer.Deserialize(/*error_code=*/nullptr, &deserialize_error);
   ASSERT_TRUE(value) << deserialize_error;
 
-  absl::optional<AuthenticatorGetAssertionResponse> response =
+  std::optional<AuthenticatorGetAssertionResponse> response =
       AuthenticatorGetAssertionResponseFromValue(*value);
   ASSERT_TRUE(response);
 
@@ -104,7 +104,7 @@
         deserializer.Deserialize(/*error_code=*/nullptr, &deserialize_error);
     ASSERT_TRUE(value) << deserialize_error;
 
-    absl::optional<AuthenticatorGetAssertionResponse> response =
+    std::optional<AuthenticatorGetAssertionResponse> response =
         AuthenticatorGetAssertionResponseFromValue(*value);
     ASSERT_FALSE(response)
         << "Parsing incorrectly succeeded with no authenticatorData.";
@@ -117,7 +117,7 @@
         deserializer.Deserialize(/*error_code=*/nullptr, &deserialize_error);
     ASSERT_TRUE(value) << deserialize_error;
 
-    absl::optional<AuthenticatorGetAssertionResponse> response =
+    std::optional<AuthenticatorGetAssertionResponse> response =
         AuthenticatorGetAssertionResponseFromValue(*value);
     ASSERT_FALSE(response)
         << "Parsing incorrectly succeeded with no signature.";
diff --git a/device/fido/virtual_ctap2_device.cc b/device/fido/virtual_ctap2_device.cc
index 9084621..23d52f8 100644
--- a/device/fido/virtual_ctap2_device.cc
+++ b/device/fido/virtual_ctap2_device.cc
@@ -64,7 +64,7 @@
 
 struct PinUvAuthTokenPermissions {
   uint8_t permissions;
-  absl::optional<std::string> rp_id;
+  std::optional<std::string> rp_id;
 };
 
 uint8_t GetSupportedPermissionsMask(const VirtualCtap2Device::Config& config) {
@@ -135,7 +135,7 @@
 void ReturnCtap2Response(
     FidoDevice::DeviceCallback cb,
     CtapDeviceResponseCode response_code,
-    absl::optional<base::span<const uint8_t>> data = absl::nullopt) {
+    std::optional<base::span<const uint8_t>> data = std::nullopt) {
   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
       FROM_HERE,
       base::BindOnce(std::move(cb),
@@ -154,13 +154,13 @@
 }
 
 std::vector<uint8_t> ConstructMakeCredentialResponse(
-    const absl::optional<std::vector<uint8_t>> attestation_certificate,
+    const std::optional<std::vector<uint8_t>> attestation_certificate,
     base::span<const uint8_t> signature,
     AuthenticatorData authenticator_data,
     bool enterprise_attestation_requested,
-    absl::optional<LargeBlobSupportType> large_blob_type,
+    std::optional<LargeBlobSupportType> large_blob_type,
     bool prf_enabled,
-    absl::optional<std::vector<uint8_t>> prf_results) {
+    std::optional<std::vector<uint8_t>> prf_results) {
   std::unique_ptr<OpaqueAttestationStatement> attestation_statement;
   if (!signature.empty()) {
     cbor::Value::MapValue attestation_map;
@@ -192,27 +192,27 @@
   return AsCTAPStyleCBORBytes(make_credential_response);
 }
 
-absl::optional<std::vector<uint8_t>> GetPINBytestring(
+std::optional<std::vector<uint8_t>> GetPINBytestring(
     const cbor::Value::MapValue& request,
     pin::RequestKey key) {
   const auto it = request.find(cbor::Value(static_cast<int>(key)));
   if (it == request.end() || !it->second.is_bytestring()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   return it->second.GetBytestring();
 }
 
-absl::optional<bssl::UniquePtr<EC_POINT>> GetPINKey(
+std::optional<bssl::UniquePtr<EC_POINT>> GetPINKey(
     const cbor::Value::MapValue& request,
     pin::RequestKey map_key) {
   const auto it = request.find(cbor::Value(static_cast<int>(map_key)));
   if (it == request.end() || !it->second.is_map()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   const auto& cose_key = it->second.GetMap();
   auto response = pin::KeyAgreementResponse::ParseFromCOSE(cose_key);
   if (!response) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   bssl::UniquePtr<EC_GROUP> group(
@@ -279,7 +279,7 @@
     const std::vector<uint8_t>& shared_key,
     const std::vector<uint8_t>& encrypted_pin,
     const std::vector<uint8_t>& pin_auth,
-    absl::optional<base::span<const uint8_t>> current_encrypted_pin_hash) {
+    std::optional<base::span<const uint8_t>> current_encrypted_pin_hash) {
   const pin::Protocol& pin_protocol = pin::ProtocolVersion(protocol);
   std::vector<uint8_t> pin_auth_bytes;
   pin_auth_bytes.insert(pin_auth_bytes.begin(), encrypted_pin.begin(),
@@ -347,7 +347,7 @@
       !pin_protocol_it->second.is_unsigned()) {
     return CtapDeviceResponseCode::kCtap2ErrCBORUnexpectedType;
   }
-  absl::optional<PINUVAuthProtocol> protocol =
+  std::optional<PINUVAuthProtocol> protocol =
       ToPINUVAuthProtocol(pin_protocol_it->second.GetUnsigned());
   if (!protocol ||
       !base::Contains(*authenticator_info.pin_protocols, *protocol)) {
@@ -366,9 +366,8 @@
 
 // Like AsCBOR(const PublicKeyCredentialRpEntity&), but optionally allows name
 // to be INVALID_UTF8.
-absl::optional<cbor::Value> RpEntityAsCBOR(
-    const PublicKeyCredentialRpEntity& rp,
-    bool allow_invalid_utf8) {
+std::optional<cbor::Value> RpEntityAsCBOR(const PublicKeyCredentialRpEntity& rp,
+                                          bool allow_invalid_utf8) {
   if (!allow_invalid_utf8) {
     return AsCBOR(rp);
   }
@@ -384,7 +383,7 @@
 
 // Like AsCBOR(const PublicKeyCredentialUserEntity&), but optionally allows name
 // or displayName to be INVALID_UTF8.
-absl::optional<cbor::Value> UserEntityAsCBOR(
+std::optional<cbor::Value> UserEntityAsCBOR(
     const PublicKeyCredentialUserEntity& user,
     bool user_verification,
     bool allow_invalid_utf8) {
@@ -516,7 +515,7 @@
 std::vector<uint8_t> EvaluateHMAC(
     base::span<const uint8_t> hmac_key,
     const std::array<uint8_t, 32>& hmac_salt1,
-    const absl::optional<std::array<uint8_t, 32>>& hmac_salt2) {
+    const std::optional<std::array<uint8_t, 32>>& hmac_salt2) {
   uint8_t hmac_result[SHA256_DIGEST_LENGTH];
   unsigned hmac_out_length;
   HMAC(EVP_sha256(), hmac_key.data(), hmac_key.size(), hmac_salt1.data(),
@@ -907,13 +906,12 @@
       std::move(versions), config_.ctap2_versions, kDeviceAaguid);
 }
 
-absl::optional<CtapDeviceResponseCode>
-VirtualCtap2Device::CheckUserVerification(
+std::optional<CtapDeviceResponseCode> VirtualCtap2Device::CheckUserVerification(
     CheckUserVerificationMode mode,
     const AuthenticatorGetInfoResponse& authenticator_info,
     const std::string& rp_id,
-    const absl::optional<std::vector<uint8_t>>& pin_auth,
-    const absl::optional<PINUVAuthProtocol>& pin_protocol,
+    const std::optional<std::vector<uint8_t>>& pin_auth,
+    const std::optional<PINUVAuthProtocol>& pin_protocol,
     base::span<const uint8_t> client_data_hash,
     UserVerificationRequirement user_verification,
     bool user_presence_required,
@@ -942,7 +940,7 @@
       AuthenticatorSupportedOptions::ClientPinAvailability::kNotSupported;
   if (supports_pin && pin_auth && pin_auth->empty()) {
     if (!SimulatePress()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     switch (options.client_pin_availability) {
@@ -956,7 +954,7 @@
         NOTREACHED();
     }
   }
-  const absl::optional<base::flat_set<PINUVAuthProtocol>>&
+  const std::optional<base::flat_set<PINUVAuthProtocol>>&
       supported_pin_protocols = authenticator_info.pin_protocols;
   DCHECK(!supports_pin ||
          (supported_pin_protocols && !supported_pin_protocols->empty()));
@@ -1031,7 +1029,7 @@
           AuthenticatorSupportedOptions::UserVerificationAvailability::
               kSupportedAndConfigured) {
         if (!SimulatePress()) {
-          return absl::nullopt;
+          return std::nullopt;
         }
 
         if (!config_.user_verification_succeeds) {
@@ -1100,7 +1098,7 @@
   return CtapDeviceResponseCode::kSuccess;
 }
 
-absl::optional<CtapDeviceResponseCode> VirtualCtap2Device::OnMakeCredential(
+std::optional<CtapDeviceResponseCode> VirtualCtap2Device::OnMakeCredential(
     base::span<const uint8_t> request_bytes,
     std::vector<uint8_t>* response) {
   request_state_.Reset();
@@ -1130,7 +1128,7 @@
        device_info_->options.make_cred_uv_not_required)
           ? CheckUserVerificationMode::kMakeCredentialUvNotRequired
           : CheckUserVerificationMode::kMakeCredential;
-  const absl::optional<CtapDeviceResponseCode> uv_error = CheckUserVerification(
+  const std::optional<CtapDeviceResponseCode> uv_error = CheckUserVerification(
       check_uv_mode, *device_info_, request.rp.id, request.pin_auth,
       request.pin_protocol, request.client_data_hash, request.user_verification,
       /*user_presence_required=*/true, &user_verified);
@@ -1166,7 +1164,7 @@
         continue;
       }
       if (!SimulatePress()) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       return CtapDeviceResponseCode::kCtap2ErrCredentialExcluded;
     }
@@ -1224,13 +1222,13 @@
   if ((!user_verified || request.user_verification ==
                              UserVerificationRequirement::kDiscouraged) &&
       !SimulatePress()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   // Our key handles are simple hashes of the public key.
   const auto key_handle = crypto::SHA256Hash(public_key->cose_key_bytes);
 
-  absl::optional<cbor::Value> extensions;
+  std::optional<cbor::Value> extensions;
   cbor::Value::MapValue extensions_map;
   if (request.hmac_secret) {
     if (!config_.hmac_secret_support) {
@@ -1261,7 +1259,7 @@
                            cbor::Value(static_cast<int64_t>(cred_protect)));
   }
 
-  absl::optional<LargeBlobSupportType> supports_large_blob;
+  std::optional<LargeBlobSupportType> supports_large_blob;
   if (request.large_blob_key) {
     if (!config_.large_blob_support) {
       DLOG(ERROR) << "Rejecting makeCredential due to unexpected largeBlobKey "
@@ -1350,7 +1348,7 @@
     }
   }
 
-  absl::optional<std::vector<uint8_t>> attestation_cert;
+  std::optional<std::vector<uint8_t>> attestation_cert;
   bool enterprise_attestation_requested = false;
   if (!config_.none_attestation && !mutable_state()->self_attestation) {
     if (config_.support_enterprise_attestation) {
@@ -1413,7 +1411,7 @@
   registration.protection = cred_protect;
   registration.cred_blob = std::move(request.cred_blob);
 
-  absl::optional<std::vector<uint8_t>> prf_results;
+  std::optional<std::vector<uint8_t>> prf_results;
   if (request.hmac_secret || prf_enabled) {
     registration.hmac_key.emplace();
     RAND_bytes(registration.hmac_key->first.data(),
@@ -1444,7 +1442,7 @@
   return CtapDeviceResponseCode::kSuccess;
 }
 
-absl::optional<CtapDeviceResponseCode> VirtualCtap2Device::OnGetAssertion(
+std::optional<CtapDeviceResponseCode> VirtualCtap2Device::OnGetAssertion(
     base::span<const uint8_t> request_bytes,
     std::vector<uint8_t>* response) {
   request_state_.Reset();
@@ -1470,7 +1468,7 @@
   mutable_state()->allow_list_history.push_back(request.allow_list);
 
   bool user_verified;
-  const absl::optional<CtapDeviceResponseCode> uv_error = CheckUserVerification(
+  const std::optional<CtapDeviceResponseCode> uv_error = CheckUserVerification(
       CheckUserVerificationMode::kGetAssertion, *device_info_, request.rp_id,
       request.pin_auth, request.pin_protocol, request.client_data_hash,
       request.user_verification, request.user_presence_required,
@@ -1568,7 +1566,7 @@
       (!user_verified || request.user_verification ==
                              UserVerificationRequirement::kDiscouraged) &&
       !SimulatePress()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   // Step 8.
@@ -1576,9 +1574,9 @@
     return CtapDeviceResponseCode::kCtap2ErrNoCredentials;
   }
 
-  absl::optional<std::vector<uint8_t>> hmac_shared_key;
-  absl::optional<std::array<uint8_t, 32>> hmac_salt1;
-  absl::optional<std::array<uint8_t, 32>> hmac_salt2;
+  std::optional<std::vector<uint8_t>> hmac_shared_key;
+  std::optional<std::array<uint8_t, 32>> hmac_salt1;
+  std::optional<std::array<uint8_t, 32>> hmac_salt2;
 
   if (request.hmac_secret) {
     if (!config_.hmac_secret_support) {
@@ -1663,7 +1661,7 @@
   for (const auto& registration : found_registrations) {
     registration.second->counter++;
 
-    absl::optional<AttestedCredentialData> opt_attested_cred_data;
+    std::optional<AttestedCredentialData> opt_attested_cred_data;
     if (config_.return_attested_cred_data_in_get_assertion_response) {
       opt_attested_cred_data.emplace(ConstructAttestedCredentialData(
           registration.first,
@@ -1697,7 +1695,7 @@
           registration.second->cred_blob.value_or(std::vector<uint8_t>()));
     }
 
-    absl::optional<cbor::Value> extensions;
+    std::optional<cbor::Value> extensions;
     if (!extensions_map.empty()) {
       extensions.emplace(std::move(extensions_map));
     }
@@ -1838,7 +1836,7 @@
   return CtapDeviceResponseCode::kSuccess;
 }
 
-absl::optional<CtapDeviceResponseCode> VirtualCtap2Device::OnPINCommand(
+std::optional<CtapDeviceResponseCode> VirtualCtap2Device::OnPINCommand(
     base::span<const uint8_t> request_bytes,
     std::vector<uint8_t>* response) {
   request_state_.Reset();
@@ -1854,7 +1852,7 @@
   if (protocol_it == request_map.end() || !protocol_it->second.is_unsigned()) {
     return CtapDeviceResponseCode::kCtap2ErrCBORUnexpectedType;
   }
-  absl::optional<PINUVAuthProtocol> pin_protocol =
+  std::optional<PINUVAuthProtocol> pin_protocol =
       ToPINUVAuthProtocol(protocol_it->second.GetUnsigned());
   if (!pin_protocol) {
     return CtapDeviceResponseCode::kCtap1ErrInvalidCommand;
@@ -1937,7 +1935,7 @@
 
       CtapDeviceResponseCode err =
           SetPIN(*pin_protocol, mutable_state(), shared_key, *encrypted_pin,
-                 *pin_auth, /*current_encrypted_pin_hash=*/absl::nullopt);
+                 *pin_auth, /*current_encrypted_pin_hash=*/std::nullopt);
       if (err != CtapDeviceResponseCode::kSuccess) {
         return err;
       }
@@ -2107,7 +2105,7 @@
 
       // Simulate internal UV.
       if (!SimulatePress()) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       if (!config_.user_verification_succeeds) {
         return mutable_state()->uv_retries > 0
@@ -2380,7 +2378,7 @@
       if (new_user_it == params.end() || !new_user_it->second.is_map()) {
         return CtapDeviceResponseCode::kCtap2ErrCBORUnexpectedType;
       }
-      absl::optional<PublicKeyCredentialUserEntity> new_user =
+      std::optional<PublicKeyCredentialUserEntity> new_user =
           PublicKeyCredentialUserEntity::CreateFromCBORValue(
               cbor::Value(new_user_it->second.GetMap()));
       if (!new_user) {
@@ -2453,8 +2451,8 @@
   }
 
   // Template id from subcommand parameters, if it exists.
-  absl::optional<uint8_t> template_id;
-  absl::optional<std::string> name;
+  std::optional<uint8_t> template_id;
+  std::optional<std::string> name;
   auto params_it = request_map.find(cbor::Value(
       static_cast<int>(BioEnrollmentRequestKey::kSubCommandParams)));
   if (params_it != request_map.end()) {
@@ -2567,7 +2565,7 @@
             base::StrCat(
                 {"Template", base::NumberToString(
                                  *mutable_state()->bio_current_template_id)});
-        mutable_state()->bio_current_template_id = absl::nullopt;
+        mutable_state()->bio_current_template_id = std::nullopt;
         mutable_state()->fingerprints_enrolled = true;
       }
       break;
@@ -2621,7 +2619,7 @@
       mutable_state()->bio_templates.erase(*template_id);
       return CtapDeviceResponseCode::kSuccess;
     case SubCmd::kCancelCurrentEnrollment:
-      mutable_state()->bio_current_template_id = absl::nullopt;
+      mutable_state()->bio_current_template_id = std::nullopt;
       return CtapDeviceResponseCode::kSuccess;
     default:
       // Handle all other commands as if they were unsupported (will change
@@ -2835,7 +2833,7 @@
           cbor::Value(cbor::Value(*registration.second.large_blob_key)));
     }
 
-    absl::optional<cbor::Value> cose_key = cbor::Reader::Read(
+    std::optional<cbor::Value> cose_key = cbor::Reader::Read(
         registration.second.private_key->GetPublicKey()->cose_key_bytes);
     response_map.emplace(
         static_cast<int>(CredentialManagementResponseKey::kPublicKey),
diff --git a/device/fido/virtual_ctap2_device.h b/device/fido/virtual_ctap2_device.h
index a74912c..d3736e9 100644
--- a/device/fido/virtual_ctap2_device.h
+++ b/device/fido/virtual_ctap2_device.h
@@ -9,6 +9,7 @@
 
 #include <list>
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "base/component_export.h"
@@ -24,7 +25,6 @@
 #include "device/fido/fido_types.h"
 #include "device/fido/large_blob.h"
 #include "device/fido/virtual_fido_device.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -79,7 +79,7 @@
     // present then the extension will be implement, but if it's present with
     // the value false then the authenticator will report that makeCredential
     // didn't enable a large blob.
-    absl::optional<bool> large_blob_extension_support;
+    std::optional<bool> large_blob_extension_support;
     // Support for setting a min PIN length and forcing pin change.
     bool min_pin_length_support = false;
     // min_pin_length_extension_support, if true, enables support for the
@@ -113,7 +113,7 @@
     // force_cred_protect, if set and if |cred_protect_support| is true, is a
     // credProtect level that will be forced for all registrations. This
     // overrides any level requested in the makeCredential.
-    absl::optional<device::CredProtect> force_cred_protect;
+    std::optional<device::CredProtect> force_cred_protect;
 
     // default_cred_protect, if |cred_protect_support| is true, is the
     // credProtect level that will be set for makeCredential requests that do
@@ -310,25 +310,25 @@
     kMakeCredential,
     kMakeCredentialUvNotRequired,
   };
-  absl::optional<CtapDeviceResponseCode> CheckUserVerification(
+  std::optional<CtapDeviceResponseCode> CheckUserVerification(
       CheckUserVerificationMode mode,
       const AuthenticatorGetInfoResponse& authenticator_info,
       const std::string& rp_id,
-      const absl::optional<std::vector<uint8_t>>& pin_auth,
-      const absl::optional<PINUVAuthProtocol>& pin_protocol,
+      const std::optional<std::vector<uint8_t>>& pin_auth,
+      const std::optional<PINUVAuthProtocol>& pin_protocol,
       base::span<const uint8_t> client_data_hash,
       UserVerificationRequirement user_verification,
       bool user_presence_required,
       bool* out_user_verified);
-  absl::optional<CtapDeviceResponseCode> OnMakeCredential(
+  std::optional<CtapDeviceResponseCode> OnMakeCredential(
       base::span<const uint8_t> request,
       std::vector<uint8_t>* response);
-  absl::optional<CtapDeviceResponseCode> OnGetAssertion(
+  std::optional<CtapDeviceResponseCode> OnGetAssertion(
       base::span<const uint8_t> request,
       std::vector<uint8_t>* response);
   CtapDeviceResponseCode OnGetNextAssertion(base::span<const uint8_t> request,
                                             std::vector<uint8_t>* response);
-  absl::optional<CtapDeviceResponseCode> OnPINCommand(
+  std::optional<CtapDeviceResponseCode> OnPINCommand(
       base::span<const uint8_t> request,
       std::vector<uint8_t>* response);
   CtapDeviceResponseCode OnCredentialManagement(
diff --git a/device/fido/virtual_ctap2_device_unittest.cc b/device/fido/virtual_ctap2_device_unittest.cc
index 60ef8ae..53e2f41 100644
--- a/device/fido/virtual_ctap2_device_unittest.cc
+++ b/device/fido/virtual_ctap2_device_unittest.cc
@@ -33,7 +33,7 @@
 namespace {
 
 using TestCallbackReceiver =
-    test::ValueCallbackReceiver<absl::optional<std::vector<uint8_t>>>;
+    test::ValueCallbackReceiver<std::optional<std::vector<uint8_t>>>;
 
 void SendCommand(VirtualCtap2Device* device,
                  base::span<const uint8_t> command,
@@ -44,18 +44,18 @@
 
 // DecodeCBOR parses a CBOR structure, ignoring the first byte of |in|, which is
 // assumed to be a CTAP2 status byte.
-absl::optional<cbor::Value> DecodeCBOR(base::span<const uint8_t> in) {
+std::optional<cbor::Value> DecodeCBOR(base::span<const uint8_t> in) {
   CHECK(!in.empty());
   return cbor::Reader::Read(in.subspan(1));
 }
 
 std::vector<uint8_t> ToCTAP2Command(
-    const std::pair<device::CtapRequestCommand, absl::optional<cbor::Value>>&
+    const std::pair<device::CtapRequestCommand, std::optional<cbor::Value>>&
         parts) {
   std::vector<uint8_t> ret;
 
   if (parts.second.has_value()) {
-    absl::optional<std::vector<uint8_t>> cbor_bytes =
+    std::optional<std::vector<uint8_t>> cbor_bytes =
         cbor::Writer::Write(std::move(*parts.second));
     ret.swap(*cbor_bytes);
   }
@@ -94,7 +94,7 @@
       base::make_span(test_data::kCtapMakeCredentialRequest).subspan(1));
   ASSERT_TRUE(cbor_request);
   ASSERT_TRUE(cbor_request->is_map());
-  const absl::optional<CtapMakeCredentialRequest> request =
+  const std::optional<CtapMakeCredentialRequest> request =
       CtapMakeCredentialRequest::Parse(cbor_request->GetMap());
   ASSERT_TRUE(request);
   EXPECT_THAT(request->client_data_hash,
@@ -143,7 +143,7 @@
   ASSERT_TRUE(cbor_request);
   ASSERT_TRUE(cbor_request->is_map());
 
-  const absl::optional<CtapGetAssertionRequest> request =
+  const std::optional<CtapGetAssertionRequest> request =
       CtapGetAssertionRequest::Parse(cbor_request->GetMap());
   EXPECT_THAT(request->client_data_hash,
               ::testing::ElementsAreArray(test_data::kClientDataHash));
@@ -180,9 +180,9 @@
               callback_receiver.callback());
   callback_receiver.WaitForCallback();
 
-  absl::optional<cbor::Value> cbor = DecodeCBOR(*callback_receiver.value());
+  std::optional<cbor::Value> cbor = DecodeCBOR(*callback_receiver.value());
   ASSERT_TRUE(cbor);
-  absl::optional<AuthenticatorMakeCredentialResponse> response =
+  std::optional<AuthenticatorMakeCredentialResponse> response =
       ReadCTAPMakeCredentialResponse(
           FidoTransportProtocol::kUsbHumanInterfaceDevice, std::move(cbor));
   ASSERT_TRUE(response);
@@ -244,7 +244,7 @@
     allow_list.emplace_back(std::move(cred));
     map.emplace(3, std::move(allow_list));
 
-    absl::optional<std::vector<uint8_t>> bytes =
+    std::optional<std::vector<uint8_t>> bytes =
         cbor::Writer::Write(cbor::Value(std::move(map)));
     ASSERT_TRUE(bytes.has_value());
 
@@ -292,10 +292,10 @@
       base::BindOnce(callback_receiver.callback()));
   callback_receiver.WaitForCallback();
 
-  absl::optional<cbor::Value> cbor = DecodeCBOR(*callback_receiver.value());
+  std::optional<cbor::Value> cbor = DecodeCBOR(*callback_receiver.value());
   ASSERT_TRUE(cbor);
 
-  absl::optional<AuthenticatorGetAssertionResponse> response =
+  std::optional<AuthenticatorGetAssertionResponse> response =
       ReadCTAPGetAssertionResponse(
           FidoTransportProtocol::kUsbHumanInterfaceDevice, std::move(cbor));
   ASSERT_TRUE(response);
@@ -311,9 +311,9 @@
   SendCommand(device_.get(), test_data::kCtapSimpleMakeCredentialRequest,
               callback_receiver.callback());
   callback_receiver.WaitForCallback();
-  absl::optional<cbor::Value> cbor = DecodeCBOR(*callback_receiver.value());
+  std::optional<cbor::Value> cbor = DecodeCBOR(*callback_receiver.value());
   ASSERT_TRUE(cbor);
-  absl::optional<AuthenticatorMakeCredentialResponse> response =
+  std::optional<AuthenticatorMakeCredentialResponse> response =
       ReadCTAPMakeCredentialResponse(
           FidoTransportProtocol::kUsbHumanInterfaceDevice, std::move(cbor));
   const AttestationStatement& attestation =
@@ -348,10 +348,10 @@
       base::BindOnce(callback_receiver.callback()));
   callback_receiver.WaitForCallback();
 
-  absl::optional<cbor::Value> cbor = DecodeCBOR(*callback_receiver.value());
+  std::optional<cbor::Value> cbor = DecodeCBOR(*callback_receiver.value());
   ASSERT_TRUE(cbor);
 
-  absl::optional<AuthenticatorGetAssertionResponse> response =
+  std::optional<AuthenticatorGetAssertionResponse> response =
       ReadCTAPGetAssertionResponse(
           FidoTransportProtocol::kUsbHumanInterfaceDevice, std::move(cbor));
   ASSERT_TRUE(response);
@@ -386,10 +386,10 @@
       base::BindOnce(callback_receiver.callback()));
   callback_receiver.WaitForCallback();
 
-  absl::optional<cbor::Value> cbor = DecodeCBOR(*callback_receiver.value());
+  std::optional<cbor::Value> cbor = DecodeCBOR(*callback_receiver.value());
   ASSERT_TRUE(cbor);
 
-  absl::optional<AuthenticatorGetAssertionResponse> response =
+  std::optional<AuthenticatorGetAssertionResponse> response =
       ReadCTAPGetAssertionResponse(
           FidoTransportProtocol::kUsbHumanInterfaceDevice, std::move(cbor));
   ASSERT_TRUE(response);
@@ -404,9 +404,9 @@
   SendCommand(device_.get(), test_data::kCtapSimpleMakeCredentialRequest,
               callback_receiver.callback());
   callback_receiver.WaitForCallback();
-  absl::optional<cbor::Value> cbor = DecodeCBOR(*callback_receiver.value());
+  std::optional<cbor::Value> cbor = DecodeCBOR(*callback_receiver.value());
   ASSERT_TRUE(cbor);
-  absl::optional<AuthenticatorMakeCredentialResponse> response =
+  std::optional<AuthenticatorMakeCredentialResponse> response =
       ReadCTAPMakeCredentialResponse(
           FidoTransportProtocol::kUsbHumanInterfaceDevice, std::move(cbor));
 
@@ -429,9 +429,9 @@
               callback_receiver.callback());
   callback_receiver.WaitForCallback();
 
-  absl::optional<cbor::Value> cbor = DecodeCBOR(*callback_receiver.value());
+  std::optional<cbor::Value> cbor = DecodeCBOR(*callback_receiver.value());
   ASSERT_TRUE(cbor);
-  absl::optional<AuthenticatorMakeCredentialResponse> response =
+  std::optional<AuthenticatorMakeCredentialResponse> response =
       ReadCTAPMakeCredentialResponse(
           FidoTransportProtocol::kUsbHumanInterfaceDevice, std::move(cbor));
 
@@ -445,12 +445,12 @@
   std::vector<uint8_t> credential1 = {1, 2, 3, 4};
   ASSERT_TRUE(device_->mutable_state()->InjectResidentKey(
       credential1, test_data::kRelyingPartyId, std::vector<uint8_t>{5, 6, 7, 8},
-      absl::nullopt, absl::nullopt));
+      std::nullopt, std::nullopt));
 
   std::vector<uint8_t> credential2 = {5, 6, 7, 8};
   ASSERT_TRUE(device_->mutable_state()->InjectResidentKey(
       credential2, test_data::kRelyingPartyId, std::vector<uint8_t>{9, 0, 1, 2},
-      absl::nullopt, absl::nullopt));
+      std::nullopt, std::nullopt));
 
   // Inject two large blobs.
   LargeBlob blob1({'b', 'l', 'o', 'b', '1'}, 5);
@@ -466,11 +466,11 @@
   device_->mutable_state()->InjectLargeBlob(
       &device_->mutable_state()->registrations.at(credential1), blob3);
 
-  absl::optional<LargeBlob> blob_cred1 = device_->mutable_state()->GetLargeBlob(
+  std::optional<LargeBlob> blob_cred1 = device_->mutable_state()->GetLargeBlob(
       device_->mutable_state()->registrations.at(credential1));
   EXPECT_EQ(*blob_cred1, blob3);
 
-  absl::optional<LargeBlob> blob_cred2 = device_->mutable_state()->GetLargeBlob(
+  std::optional<LargeBlob> blob_cred2 = device_->mutable_state()->GetLargeBlob(
       device_->mutable_state()->registrations.at(credential2));
   EXPECT_EQ(*blob_cred2, blob2);
 }
diff --git a/device/fido/virtual_fido_device.cc b/device/fido/virtual_fido_device.cc
index 5609967..9eadb3b9 100644
--- a/device/fido/virtual_fido_device.cc
+++ b/device/fido/virtual_fido_device.cc
@@ -181,7 +181,7 @@
     map.emplace(static_cast<int64_t>(CoseKeyKey::kRSAPublicExponent),
                 std::move(public_exponent));
 
-    absl::optional<std::vector<uint8_t>> cbor_bytes(
+    std::optional<std::vector<uint8_t>> cbor_bytes(
         cbor::Writer::Write(cbor::Value(std::move(map))));
 
     std::vector<uint8_t> der_bytes(
@@ -224,7 +224,7 @@
     map.emplace(static_cast<int64_t>(CoseKeyKey::kEllipticX),
                 base::span<const uint8_t>(public_key, sizeof(public_key)));
 
-    absl::optional<std::vector<uint8_t>> cbor_bytes(
+    std::optional<std::vector<uint8_t>> cbor_bytes(
         cbor::Writer::Write(cbor::Value(std::move(map))));
 
     std::vector<uint8_t> der_bytes(
@@ -261,12 +261,12 @@
     map.emplace(static_cast<int64_t>(CoseKeyKey::kKty),
                 static_cast<int64_t>(CoseKeyTypes::kInvalidForTesting));
 
-    absl::optional<std::vector<uint8_t>> cbor_bytes(
+    std::optional<std::vector<uint8_t>> cbor_bytes(
         cbor::Writer::Write(cbor::Value(std::move(map))));
 
     return std::make_unique<PublicKey>(
         static_cast<int32_t>(CoseAlgorithmIdentifier::kInvalidForTesting),
-        *cbor_bytes, absl::nullopt);
+        *cbor_bytes, std::nullopt);
   }
 };
 
@@ -284,7 +284,7 @@
 }
 
 // static
-absl::optional<std::unique_ptr<VirtualFidoDevice::PrivateKey>>
+std::optional<std::unique_ptr<VirtualFidoDevice::PrivateKey>>
 VirtualFidoDevice::PrivateKey::FromPKCS8(
     base::span<const uint8_t> pkcs8_private_key) {
   crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
@@ -293,14 +293,14 @@
   CBS_init(&cbs, pkcs8_private_key.data(), pkcs8_private_key.size());
   bssl::UniquePtr<EVP_PKEY> pkey(EVP_parse_private_key(&cbs));
   if (!pkey || CBS_len(&cbs) != 0) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   switch (EVP_PKEY_id(pkey.get())) {
     case EVP_PKEY_EC:
       if (EC_GROUP_get_curve_name(EC_KEY_get0_group(
               EVP_PKEY_get0_EC_KEY(pkey.get()))) != NID_X9_62_prime256v1) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       return std::unique_ptr<PrivateKey>(new P256PrivateKey(std::move(pkey)));
 
@@ -312,7 +312,7 @@
           new Ed25519PrivateKey(std::move(pkey)));
 
     default:
-      return absl::nullopt;
+      return std::nullopt;
   }
 }
 
@@ -455,8 +455,8 @@
     base::span<const uint8_t> credential_id,
     const std::string& relying_party_id,
     base::span<const uint8_t> user_id,
-    absl::optional<std::string> user_name,
-    absl::optional<std::string> user_display_name) {
+    std::optional<std::string> user_name,
+    std::optional<std::string> user_display_name) {
   return InjectResidentKey(
       credential_id, PublicKeyCredentialRpEntity(std::move(relying_party_id)),
       PublicKeyCredentialUserEntity(fido_parsing_utils::Materialize(user_id),
@@ -464,33 +464,33 @@
                                     std::move(user_display_name)));
 }
 
-absl::optional<LargeBlob> VirtualFidoDevice::State::GetLargeBlob(
+std::optional<LargeBlob> VirtualFidoDevice::State::GetLargeBlob(
     const RegistrationData& credential) {
   if (credential.large_blob) {
     return credential.large_blob;
   }
   if (!credential.large_blob_key) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   LargeBlobArrayReader reader;
   reader.Append(large_blob);
-  absl::optional<cbor::Value::ArrayValue> large_blob_array =
+  std::optional<cbor::Value::ArrayValue> large_blob_array =
       reader.Materialize();
   if (!large_blob_array) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   for (const cbor::Value& blob_cbor : *large_blob_array) {
-    absl::optional<LargeBlobData> data = LargeBlobData::Parse(blob_cbor);
+    std::optional<LargeBlobData> data = LargeBlobData::Parse(blob_cbor);
     if (!data.has_value()) {
       continue;
     }
 
-    absl::optional<LargeBlob> blob = data->Decrypt(*credential.large_blob_key);
+    std::optional<LargeBlob> blob = data->Decrypt(*credential.large_blob_key);
     if (blob) {
       return blob;
     }
   }
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 void VirtualFidoDevice::State::InjectLargeBlob(RegistrationData* credential,
@@ -503,7 +503,7 @@
   if (credential->large_blob_key) {
     base::EraseIf(
         large_blob_array, [&credential](const cbor::Value& blob_cbor) {
-          absl::optional<LargeBlobData> blob = LargeBlobData::Parse(blob_cbor);
+          std::optional<LargeBlobData> blob = LargeBlobData::Parse(blob_cbor);
           return blob && blob->Decrypt(*credential->large_blob_key).has_value();
         });
   } else {
@@ -558,7 +558,7 @@
   return signer->Sign(sign_buffer, signature);
 }
 
-absl::optional<std::vector<uint8_t>>
+std::optional<std::vector<uint8_t>>
 VirtualFidoDevice::GenerateAttestationCertificate(
     bool individual_attestation_requested,
     bool include_transports) const {
@@ -627,7 +627,7 @@
           kAttestationCertSerialNumber, base::Time::FromTimeT(1500000000),
           expiry_date, extensions, &attestation_cert)) {
     DVLOG(2) << "Failed to create attestation certificate";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return std::vector<uint8_t>(attestation_cert.begin(), attestation_cert.end());
diff --git a/device/fido/virtual_fido_device.h b/device/fido/virtual_fido_device.h
index 49d57d8..22094982 100644
--- a/device/fido/virtual_fido_device.h
+++ b/device/fido/virtual_fido_device.h
@@ -9,6 +9,7 @@
 
 #include <map>
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -26,7 +27,6 @@
 #include "device/fido/public_key_credential_descriptor.h"
 #include "device/fido/public_key_credential_rp_entity.h"
 #include "device/fido/public_key_credential_user_entity.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/boringssl/src/include/openssl/base.h"
 
 namespace crypto {
@@ -50,7 +50,7 @@
     // FromPKCS8 attempts to parse |pkcs8_private_key| as an ASN.1, DER, PKCS#8
     // private key of a supported type and returns a |PrivateKey| instance
     // representing that key.
-    static absl::optional<std::unique_ptr<PrivateKey>> FromPKCS8(
+    static std::optional<std::unique_ptr<PrivateKey>> FromPKCS8(
         base::span<const uint8_t> pkcs8_private_key);
 
     // FreshP256Key returns a randomly generated P-256 PrivateKey.
@@ -113,22 +113,22 @@
     device::CredProtect protection = device::CredProtect::kUVOptional;
 
     // user is only valid if |is_resident| is true.
-    absl::optional<device::PublicKeyCredentialUserEntity> user;
+    std::optional<device::PublicKeyCredentialUserEntity> user;
     // rp is only valid if |is_resident| is true.
-    absl::optional<device::PublicKeyCredentialRpEntity> rp;
+    std::optional<device::PublicKeyCredentialRpEntity> rp;
 
     // hmac_key is present iff the credential has the hmac_secret extension
     // enabled. The first element of the pair is the HMAC key for non-UV, and
     // the second for when UV is used.
-    absl::optional<std::pair<std::array<uint8_t, 32>, std::array<uint8_t, 32>>>
+    std::optional<std::pair<std::array<uint8_t, 32>, std::array<uint8_t, 32>>>
         hmac_key;
 
     // large_blob stores associated large blob data when the largeBlob extension
     // is used. It is not pertinent when the largeBlob command and largeBlobKey
     // extension are used.
-    absl::optional<LargeBlob> large_blob;
-    absl::optional<std::array<uint8_t, 32>> large_blob_key;
-    absl::optional<std::vector<uint8_t>> cred_blob;
+    std::optional<LargeBlob> large_blob;
+    std::optional<std::array<uint8_t, 32>> large_blob_key;
+    std::optional<std::vector<uint8_t>> cred_blob;
   };
 
   using Credential = std::pair<base::span<const uint8_t>, RegistrationData*>;
@@ -226,7 +226,7 @@
     // The permissions parameter for |pin_token|.
     uint8_t pin_uv_token_permissions = 0;
     // The permissions RPID for |pin_token|.
-    absl::optional<std::string> pin_uv_token_rpid;
+    std::optional<std::string> pin_uv_token_rpid;
     // If true, fail all PinUvAuthToken requests until a new PIN is set.
     bool force_pin_change = false;
     // The minimum PIN length as unicode code points.
@@ -242,7 +242,7 @@
     bool bio_enrollment_provisioned = false;
 
     // Current template ID being enrolled, if any.
-    absl::optional<uint8_t> bio_current_template_id;
+    std::optional<uint8_t> bio_current_template_id;
 
     // Number of remaining samples in current enrollment.
     uint8_t bio_remaining_samples = 4;
@@ -293,7 +293,7 @@
 
     // device_id_override can be used to inject a return value for `GetId()` in
     // unit tests where a stable device identifier is required.
-    absl::optional<std::string> device_id_override;
+    std::optional<std::string> device_id_override;
 
     // Observer methods.
     void AddObserver(Observer* observer);
@@ -342,11 +342,11 @@
     bool InjectResidentKey(base::span<const uint8_t> credential_id,
                            const std::string& relying_party_id,
                            base::span<const uint8_t> user_id,
-                           absl::optional<std::string> user_name,
-                           absl::optional<std::string> user_display_name);
+                           std::optional<std::string> user_name,
+                           std::optional<std::string> user_display_name);
 
     // Returns the large blob associated with the credential, if any.
-    absl::optional<LargeBlob> GetLargeBlob(const RegistrationData& credential);
+    std::optional<LargeBlob> GetLargeBlob(const RegistrationData& credential);
 
     // Injects a large blob for the credential. If the credential already has an
     // associated large blob, replaces it. If the |large_blob| is malformed,
@@ -401,7 +401,7 @@
   // Constructs certificate encoded in X.509 format to be used for packed
   // attestation statement and FIDO-U2F attestation statement.
   // https://w3c.github.io/webauthn/#defined-attestation-formats
-  absl::optional<std::vector<uint8_t>> GenerateAttestationCertificate(
+  std::optional<std::vector<uint8_t>> GenerateAttestationCertificate(
       bool individual_attestation_requested,
       bool include_transports) const;
 
diff --git a/device/fido/virtual_u2f_device.cc b/device/fido/virtual_u2f_device.cc
index cb726b0..9aa7ae0 100644
--- a/device/fido/virtual_u2f_device.cc
+++ b/device/fido/virtual_u2f_device.cc
@@ -31,7 +31,7 @@
 constexpr uint8_t kU2fRegistrationResponseHeader = 0x05;
 
 // Returns an error response with the given status.
-absl::optional<std::vector<uint8_t>> ErrorStatus(
+std::optional<std::vector<uint8_t>> ErrorStatus(
     apdu::ApduResponse::Status status) {
   return apdu::ApduResponse(std::vector<uint8_t>(), status)
       .GetEncodedResponse();
@@ -88,7 +88,7 @@
     return 0;
   }
 
-  absl::optional<std::vector<uint8_t>> response;
+  std::optional<std::vector<uint8_t>> response;
 
   switch (parsed_command->ins()) {
     // Version request is defined by the U2F spec, but is never used in
@@ -120,7 +120,7 @@
   return weak_factory_.GetWeakPtr();
 }
 
-absl::optional<std::vector<uint8_t>> VirtualU2fDevice::DoRegister(
+std::optional<std::vector<uint8_t>> VirtualU2fDevice::DoRegister(
     uint8_t ins,
     uint8_t p1,
     uint8_t p2,
@@ -130,7 +130,7 @@
   }
 
   if (!SimulatePress()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   auto challenge_param = data.first<32>();
@@ -197,7 +197,7 @@
       .GetEncodedResponse();
 }
 
-absl::optional<std::vector<uint8_t>> VirtualU2fDevice::DoSign(
+std::optional<std::vector<uint8_t>> VirtualU2fDevice::DoSign(
     uint8_t ins,
     uint8_t p1,
     uint8_t p2,
@@ -209,7 +209,7 @@
   }
 
   if (!SimulatePress()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   if (data.size() < 32 + 32 + 1)
diff --git a/device/fido/virtual_u2f_device.h b/device/fido/virtual_u2f_device.h
index 01b80fd2..774db187 100644
--- a/device/fido/virtual_u2f_device.h
+++ b/device/fido/virtual_u2f_device.h
@@ -38,16 +38,16 @@
   base::WeakPtr<FidoDevice> GetWeakPtr() override;
 
  private:
-  absl::optional<std::vector<uint8_t>> DoRegister(
+  std::optional<std::vector<uint8_t>> DoRegister(
       uint8_t ins,
       uint8_t p1,
       uint8_t p2,
       base::span<const uint8_t> data);
 
-  absl::optional<std::vector<uint8_t>> DoSign(uint8_t ins,
-                                              uint8_t p1,
-                                              uint8_t p2,
-                                              base::span<const uint8_t> data);
+  std::optional<std::vector<uint8_t>> DoSign(uint8_t ins,
+                                             uint8_t p1,
+                                             uint8_t p2,
+                                             base::span<const uint8_t> data);
 
   base::WeakPtrFactory<FidoDevice> weak_factory_{this};
 };
diff --git a/device/fido/virtual_u2f_device_unittest.cc b/device/fido/virtual_u2f_device_unittest.cc
index c4124a0..25dd74de 100644
--- a/device/fido/virtual_u2f_device_unittest.cc
+++ b/device/fido/virtual_u2f_device_unittest.cc
@@ -21,7 +21,7 @@
 namespace {
 
 using TestCallbackReceiver =
-    test::ValueCallbackReceiver<absl::optional<std::vector<uint8_t>>>;
+    test::ValueCallbackReceiver<std::optional<std::vector<uint8_t>>>;
 
 void SendCommand(VirtualU2fDevice* device, base::span<const uint8_t> command) {
   device->DeviceTransact(fido_parsing_utils::Materialize(command),
diff --git a/device/fido/win/authenticator.cc b/device/fido/win/authenticator.cc
index 50f8c9b..d0e2291d 100644
--- a/device/fido/win/authenticator.cc
+++ b/device/fido/win/authenticator.cc
@@ -6,6 +6,7 @@
 
 #include <windows.h>
 
+#include <optional>
 #include <string>
 #include <string_view>
 #include <utility>
@@ -29,7 +30,6 @@
 #include "device/fido/public_key_credential_descriptor.h"
 #include "device/fido/win/type_conversions.h"
 #include "device/fido/win/webauthn_api.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/microsoft_webauthn/webauthn.h"
 
 namespace device {
@@ -213,7 +213,7 @@
 void WinWebAuthnApiAuthenticator::MakeCredentialDone(
     MakeCredentialCallback callback,
     std::pair<CtapDeviceResponseCode,
-              absl::optional<AuthenticatorMakeCredentialResponse>> result) {
+              std::optional<AuthenticatorMakeCredentialResponse>> result) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(is_pending_);
   is_pending_ = false;
@@ -224,12 +224,12 @@
     return;
   }
   if (result.first != CtapDeviceResponseCode::kSuccess) {
-    std::move(callback).Run(result.first, absl::nullopt);
+    std::move(callback).Run(result.first, std::nullopt);
     return;
   }
   if (!result.second) {
     std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrInvalidCBOR,
-                            absl::nullopt);
+                            std::nullopt);
     return;
   }
   std::move(callback).Run(result.first, std::move(result.second));
@@ -258,7 +258,7 @@
 void WinWebAuthnApiAuthenticator::GetAssertionDone(
     GetAssertionCallback callback,
     std::pair<CtapDeviceResponseCode,
-              absl::optional<AuthenticatorGetAssertionResponse>> result) {
+              std::optional<AuthenticatorGetAssertionResponse>> result) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(is_pending_);
   is_pending_ = false;
@@ -335,11 +335,11 @@
   return "WinWebAuthnApiAuthenticator";
 }
 
-absl::optional<FidoTransportProtocol>
+std::optional<FidoTransportProtocol>
 WinWebAuthnApiAuthenticator::AuthenticatorTransport() const {
   // The Windows API could potentially use any external or
   // platform authenticator.
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 const AuthenticatorSupportedOptions& WinWebAuthnApiAuthenticator::Options()
diff --git a/device/fido/win/authenticator.h b/device/fido/win/authenticator.h
index 8a79ecd..751482f 100644
--- a/device/fido/win/authenticator.h
+++ b/device/fido/win/authenticator.h
@@ -6,7 +6,9 @@
 #define DEVICE_FIDO_WIN_AUTHENTICATOR_H_
 
 #include <Combaseapi.h>
+
 #include <memory>
+#include <optional>
 #include <string>
 
 #include "base/component_export.h"
@@ -16,7 +18,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
 #include "device/fido/fido_authenticator.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -93,17 +94,17 @@
   AuthenticatorType GetType() const override;
   std::string GetId() const override;
   const AuthenticatorSupportedOptions& Options() const override;
-  absl::optional<FidoTransportProtocol> AuthenticatorTransport() const override;
+  std::optional<FidoTransportProtocol> AuthenticatorTransport() const override;
   base::WeakPtr<FidoAuthenticator> GetWeakPtr() override;
 
   void MakeCredentialDone(
       MakeCredentialCallback callback,
       std::pair<CtapDeviceResponseCode,
-                absl::optional<AuthenticatorMakeCredentialResponse>> result);
+                std::optional<AuthenticatorMakeCredentialResponse>> result);
   void GetAssertionDone(
       GetAssertionCallback callback,
       std::pair<CtapDeviceResponseCode,
-                absl::optional<AuthenticatorGetAssertionResponse>> result);
+                std::optional<AuthenticatorGetAssertionResponse>> result);
 
   // options_ is per-instance because the capabilities of `win_api_` can
   // change at run-time in tests.
diff --git a/device/fido/win/authenticator_unittest.cc b/device/fido/win/authenticator_unittest.cc
index b0fcbe97..8aaeb9f 100644
--- a/device/fido/win/authenticator_unittest.cc
+++ b/device/fido/win/authenticator_unittest.cc
@@ -32,7 +32,7 @@
 
 using MakeCredentialCallbackReceiver = test::StatusAndValueCallbackReceiver<
     CtapDeviceResponseCode,
-    absl::optional<AuthenticatorMakeCredentialResponse>>;
+    std::optional<AuthenticatorMakeCredentialResponse>>;
 
 using GetAssertionCallbackReceiver = test::StatusAndValueCallbackReceiver<
     CtapDeviceResponseCode,
diff --git a/device/fido/win/fake_webauthn_api.cc b/device/fido/win/fake_webauthn_api.cc
index dc5d76f..699972f 100644
--- a/device/fido/win/fake_webauthn_api.cc
+++ b/device/fido/win/fake_webauthn_api.cc
@@ -6,7 +6,9 @@
 
 #include <stdint.h>
 #include <winerror.h>
+
 #include <memory>
+#include <optional>
 #include <string>
 
 #include "base/check.h"
@@ -29,7 +31,6 @@
 #include "device/fido/public_key_credential_rp_entity.h"
 #include "device/fido/public_key_credential_user_entity.h"
 #include "device/fido/virtual_fido_device.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/microsoft_webauthn/webauthn.h"
 
 namespace device {
@@ -127,10 +128,10 @@
   WebAuthnAssertionEx& operator=(const WebAuthnAssertionEx&) = delete;
 
   std::vector<uint8_t> credential_id;
-  absl::optional<std::vector<uint8_t>> user_id;
+  std::optional<std::vector<uint8_t>> user_id;
   std::vector<uint8_t> authenticator_data;
   std::vector<uint8_t> signature;
-  absl::optional<std::vector<uint8_t>> large_blob;
+  std::optional<std::vector<uint8_t>> large_blob;
   WEBAUTHN_ASSERTION assertion;
 };
 
@@ -270,7 +271,7 @@
                             WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED,
                         /*backup_eligible=*/false, /*backup_state=*/false,
                         registration.counter, std::move(credential_data),
-                        /*extensions=*/absl::nullopt)
+                        /*extensions=*/std::nullopt)
           .SerializeToByteArray();
   attestation->credential_id = credential_id;
   // For now, only support none attestation.
@@ -368,8 +369,8 @@
               WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED,
           /*backup_eligible=*/false, /*backup_state=*/false,
           registration->counter++,
-          /*attested_credential_data=*/absl::nullopt,
-          /*extensions=*/absl::nullopt)
+          /*attested_credential_data=*/std::nullopt,
+          /*extensions=*/std::nullopt)
           .SerializeToByteArray();
 
   // Create the assertion signature.
diff --git a/device/fido/win/type_conversions.cc b/device/fido/win/type_conversions.cc
index d26d1405..88a1ba5 100644
--- a/device/fido/win/type_conversions.cc
+++ b/device/fido/win/type_conversions.cc
@@ -5,6 +5,7 @@
 #include "device/fido/win/type_conversions.h"
 
 #include <algorithm>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -24,21 +25,20 @@
 #include "device/fido/get_assertion_request_handler.h"
 #include "device/fido/make_credential_request_handler.h"
 #include "device/fido/opaque_attestation_statement.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/microsoft_webauthn/webauthn.h"
 
 namespace device {
 
 namespace {
 
-absl::optional<std::vector<uint8_t>> HMACSecretOutputs(
+std::optional<std::vector<uint8_t>> HMACSecretOutputs(
     const WEBAUTHN_HMAC_SECRET_SALT& salt) {
   constexpr size_t kOutputLength = 32;
   if (salt.cbFirst != kOutputLength ||
       (salt.cbSecond != 0 && salt.cbSecond != kOutputLength)) {
     FIDO_LOG(ERROR) << "Incorrect HMAC output lengths: " << salt.cbFirst << " "
                     << salt.cbSecond;
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   std::vector<uint8_t> ret;
@@ -51,7 +51,7 @@
 
 }  // namespace
 
-absl::optional<FidoTransportProtocol> FromWinTransportsMask(
+std::optional<FidoTransportProtocol> FromWinTransportsMask(
     const DWORD transport) {
   switch (transport) {
     case WEBAUTHN_CTAP_TRANSPORT_USB:
@@ -66,7 +66,7 @@
       return FidoTransportProtocol::kHybrid;
     default:
       // Ignore _TEST and possibly future others.
-      return absl::nullopt;
+      return std::nullopt;
   }
 }
 
@@ -98,7 +98,7 @@
   return result;
 }
 
-absl::optional<AuthenticatorMakeCredentialResponse>
+std::optional<AuthenticatorMakeCredentialResponse>
 ToAuthenticatorMakeCredentialResponse(
     const WEBAUTHN_CREDENTIAL_ATTESTATION& credential_attestation) {
   auto authenticator_data = AuthenticatorData::DecodeAuthenticatorData(
@@ -108,19 +108,19 @@
     DLOG(ERROR) << "DecodeAuthenticatorData failed: "
                 << base::HexEncode(credential_attestation.pbAuthenticatorData,
                                    credential_attestation.cbAuthenticatorData);
-    return absl::nullopt;
+    return std::nullopt;
   }
-  absl::optional<cbor::Value> cbor_attestation_statement = cbor::Reader::Read(
+  std::optional<cbor::Value> cbor_attestation_statement = cbor::Reader::Read(
       base::span<const uint8_t>(credential_attestation.pbAttestation,
                                 credential_attestation.cbAttestation));
   if (!cbor_attestation_statement || !cbor_attestation_statement->is_map()) {
     DLOG(ERROR) << "CBOR decoding attestation statement failed: "
                 << base::HexEncode(credential_attestation.pbAttestation,
                                    credential_attestation.cbAttestation);
-    return absl::nullopt;
+    return std::nullopt;
   }
 
-  absl::optional<FidoTransportProtocol> transport_used;
+  std::optional<FidoTransportProtocol> transport_used;
   if (credential_attestation.dwVersion >=
       WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_3) {
     // dwUsedTransport should have exactly one of the
@@ -159,7 +159,7 @@
   return ret;
 }
 
-absl::optional<AuthenticatorGetAssertionResponse>
+std::optional<AuthenticatorGetAssertionResponse>
 ToAuthenticatorGetAssertionResponse(
     const WEBAUTHN_ASSERTION& assertion,
     const CtapGetAssertionOptions& request_options) {
@@ -170,12 +170,12 @@
     DLOG(ERROR) << "DecodeAuthenticatorData failed: "
                 << base::HexEncode(assertion.pbAuthenticatorData,
                                    assertion.cbAuthenticatorData);
-    return absl::nullopt;
+    return std::nullopt;
   }
-  absl::optional<FidoTransportProtocol> transport_used =
+  std::optional<FidoTransportProtocol> transport_used =
       assertion.dwVersion >= WEBAUTHN_ASSERTION_VERSION_4
           ? FromWinTransportsMask(assertion.dwUsedTransport)
-          : absl::nullopt;
+          : std::nullopt;
   AuthenticatorGetAssertionResponse response(
       std::move(*authenticator_data),
       std::vector<uint8_t>(assertion.pbSignature,
@@ -391,11 +391,11 @@
         PublicKeyCredentialUserEntity(
             std::vector<uint8_t>(user->pbId, user->pbId + user->cbId),
             user->pwszName
-                ? absl::make_optional(base::WideToUTF8(user->pwszName))
-                : absl::nullopt,
+                ? std::make_optional(base::WideToUTF8(user->pwszName))
+                : std::nullopt,
             user->pwszDisplayName
-                ? absl::make_optional(base::WideToUTF8(user->pwszDisplayName))
-                : absl::nullopt));
+                ? std::make_optional(base::WideToUTF8(user->pwszDisplayName))
+                : std::nullopt));
     metadata.system_created = !credential->bRemovable;
     result.push_back(std::move(metadata));
   }
diff --git a/device/fido/win/type_conversions.h b/device/fido/win/type_conversions.h
index ceeb001d..a3fe2eb 100644
--- a/device/fido/win/type_conversions.h
+++ b/device/fido/win/type_conversions.h
@@ -7,6 +7,7 @@
 
 #include <windows.h>
 
+#include <optional>
 #include <string>
 
 #include "base/component_export.h"
@@ -16,7 +17,6 @@
 #include "device/fido/ctap_get_assertion_request.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_types.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/microsoft_webauthn/webauthn.h"
 
 namespace device {
@@ -26,12 +26,12 @@
 enum class MakeCredentialStatus;
 
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<AuthenticatorMakeCredentialResponse>
+std::optional<AuthenticatorMakeCredentialResponse>
 ToAuthenticatorMakeCredentialResponse(
     const WEBAUTHN_CREDENTIAL_ATTESTATION& credential_attestation);
 
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<AuthenticatorGetAssertionResponse>
+std::optional<AuthenticatorGetAssertionResponse>
 ToAuthenticatorGetAssertionResponse(
     const WEBAUTHN_ASSERTION& credential_attestation,
     const CtapGetAssertionOptions& request_options);
@@ -95,7 +95,7 @@
     const WEBAUTHN_CREDENTIAL_DETAILS_LIST& credentials);
 
 COMPONENT_EXPORT(DEVICE_FIDO)
-absl::optional<FidoTransportProtocol> FromWinTransportsMask(
+std::optional<FidoTransportProtocol> FromWinTransportsMask(
     const DWORD transport);
 
 COMPONENT_EXPORT(DEVICE_FIDO)
diff --git a/device/fido/win/type_conversions_unittest.cc b/device/fido/win/type_conversions_unittest.cc
index 13b7e1f..fe2942454 100644
--- a/device/fido/win/type_conversions_unittest.cc
+++ b/device/fido/win/type_conversions_unittest.cc
@@ -26,7 +26,7 @@
     std::vector<uint8_t> cbor_attestation_statement;
     uint8_t used_transport;  // WEBAUTHN_CTAP_TRANSPORT_* from <webauthn.h>
     bool success;
-    absl::optional<FidoTransportProtocol> expected_transport;
+    std::optional<FidoTransportProtocol> expected_transport;
   } test_cases[] = {
       {L"packed",
        fido_parsing_utils::Materialize(test_data::kTestSignAuthenticatorData),
@@ -50,7 +50,7 @@
        fido_parsing_utils::Materialize(test_data::kTestSignAuthenticatorData),
        fido_parsing_utils::Materialize(
            test_data::kPackedAttestationStatementCBOR),
-       WEBAUTHN_CTAP_TRANSPORT_TEST, true, absl::nullopt},
+       WEBAUTHN_CTAP_TRANSPORT_TEST, true, std::nullopt},
       // Unknown attestation formats
       {L"weird-unknown-format",
        fido_parsing_utils::Materialize(test_data::kTestSignAuthenticatorData),
@@ -137,7 +137,7 @@
 TEST(TypeConversionsTest, Transports) {
   for (int i = 0; i < 16; i++) {
     const uint32_t mask = 1u << i;
-    const absl::optional<FidoTransportProtocol> transport =
+    const std::optional<FidoTransportProtocol> transport =
         FromWinTransportsMask(mask);
     if (transport) {
       const uint32_t result = ToWinTransportsMask({*transport});
diff --git a/device/fido/win/webauthn_api.cc b/device/fido/win/webauthn_api.cc
index 892d220..5fb218d 100644
--- a/device/fido/win/webauthn_api.cc
+++ b/device/fido/win/webauthn_api.cc
@@ -4,6 +4,7 @@
 
 #include "device/fido/win/webauthn_api.h"
 
+#include <optional>
 #include <string>
 #include <string_view>
 #include <vector>
@@ -24,7 +25,6 @@
 #include "device/fido/fido_types.h"
 #include "device/fido/win/logging.h"
 #include "device/fido/win/type_conversions.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/microsoft_webauthn/webauthn.h"
 
 namespace device {
@@ -320,7 +320,7 @@
 }
 
 std::pair<CtapDeviceResponseCode,
-          absl::optional<AuthenticatorMakeCredentialResponse>>
+          std::optional<AuthenticatorMakeCredentialResponse>>
 AuthenticatorMakeCredentialBlocking(WinWebAuthnApi* webauthn_api,
                                     HWND h_wnd,
                                     GUID cancellation_id,
@@ -392,7 +392,7 @@
     // enforced=true if webauthn.dll does not support credProtect.
     if (request.cred_protect_enforce && api_version < WEBAUTHN_API_VERSION_2) {
       NOTREACHED();
-      return {CtapDeviceResponseCode::kCtap2ErrNotAllowed, absl::nullopt};
+      return {CtapDeviceResponseCode::kCtap2ErrNotAllowed, std::nullopt};
     }
     // Windows doesn't support the concept of
     // CredProtectRequest::kUVOrCredIDRequiredOrBetter. So an authenticators
@@ -530,7 +530,7 @@
                     << webauthn_api->GetErrorName(hresult) << ")";
     return {WinErrorNameToCtapDeviceResponseCode(
                 base::as_u16cstr(webauthn_api->GetErrorName(hresult))),
-            absl::nullopt};
+            std::nullopt};
   }
   FIDO_LOG(DEBUG) << "WebAuthNAuthenticatorMakeCredential()="
                   << *credential_attestation;
@@ -539,7 +539,7 @@
 }
 
 std::pair<CtapDeviceResponseCode,
-          absl::optional<AuthenticatorGetAssertionResponse>>
+          std::optional<AuthenticatorGetAssertionResponse>>
 AuthenticatorGetAssertionBlocking(WinWebAuthnApi* webauthn_api,
                                   HWND h_wnd,
                                   GUID cancellation_id,
@@ -557,7 +557,7 @@
           reinterpret_cast<const unsigned char*>(client_data_json.data())),
       WEBAUTHN_HASH_ALGORITHM_SHA_256};
 
-  absl::optional<std::u16string> opt_app_id16 = absl::nullopt;
+  std::optional<std::u16string> opt_app_id16 = std::nullopt;
   if (request.app_id) {
     opt_app_id16 = base::UTF8ToUTF16(
         std::string_view(reinterpret_cast<const char*>(request.app_id->data()),
@@ -669,11 +669,11 @@
                     << webauthn_api->GetErrorName(hresult) << ")";
     return {WinErrorNameToCtapDeviceResponseCode(
                 base::as_u16cstr(webauthn_api->GetErrorName(hresult))),
-            absl::nullopt};
+            std::nullopt};
   }
 
   FIDO_LOG(DEBUG) << "WebAuthNAuthenticatorGetAssertion()=" << *assertion;
-  absl::optional<AuthenticatorGetAssertionResponse> response =
+  std::optional<AuthenticatorGetAssertionResponse> response =
       ToAuthenticatorGetAssertionResponse(*assertion, request_options);
   if (response && !request_options.prf_inputs.empty() &&
       webauthn_api->Version() < WEBAUTHN_API_VERSION_4) {
diff --git a/device/fido/win/webauthn_api.h b/device/fido/win/webauthn_api.h
index 29322aa..95ef270 100644
--- a/device/fido/win/webauthn_api.h
+++ b/device/fido/win/webauthn_api.h
@@ -9,6 +9,7 @@
 
 #include <functional>
 #include <memory>
+#include <optional>
 #include <string_view>
 
 #include "base/component_export.h"
@@ -20,7 +21,6 @@
 #include "device/fido/ctap_make_credential_request.h"
 #include "device/fido/discoverable_credential_metadata.h"
 #include "device/fido/fido_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/microsoft_webauthn/webauthn.h"
 
 namespace device {
@@ -111,7 +111,7 @@
 };
 
 std::pair<CtapDeviceResponseCode,
-          absl::optional<AuthenticatorMakeCredentialResponse>>
+          std::optional<AuthenticatorMakeCredentialResponse>>
 AuthenticatorMakeCredentialBlocking(WinWebAuthnApi* webauthn_api,
                                     HWND h_wnd,
                                     GUID cancellation_id,
@@ -119,7 +119,7 @@
                                     MakeCredentialOptions request_options);
 
 std::pair<CtapDeviceResponseCode,
-          absl::optional<AuthenticatorGetAssertionResponse>>
+          std::optional<AuthenticatorGetAssertionResponse>>
 AuthenticatorGetAssertionBlocking(WinWebAuthnApi* webauthn_api,
                                   HWND h_wnd,
                                   GUID cancellation_id,
diff --git a/device/gamepad/dualshock4_controller.cc b/device/gamepad/dualshock4_controller.cc
index e329466b..4ca4a8e 100644
--- a/device/gamepad/dualshock4_controller.cc
+++ b/device/gamepad/dualshock4_controller.cc
@@ -171,7 +171,7 @@
 template <typename Transform>
 void ProcessTouchData(base::span<const TouchPadData> touchpad_data,
                       Transform& id_transform,
-                      absl::optional<uint32_t>& initial_touch_id,
+                      std::optional<uint32_t>& initial_touch_id,
                       Gamepad* pad) {
   pad->touch_events_length = 0;
   GamepadTouch* touches = pad->touch_events;
diff --git a/device/gamepad/dualshock4_controller.h b/device/gamepad/dualshock4_controller.h
index 7ae25699..59d4752 100644
--- a/device/gamepad/dualshock4_controller.h
+++ b/device/gamepad/dualshock4_controller.h
@@ -6,7 +6,9 @@
 #define DEVICE_GAMEPAD_DUALSHOCK4_CONTROLLER_H_
 
 #include <stdint.h>
+
 #include <memory>
+#include <optional>
 #include <tuple>
 
 #include "base/memory/weak_ptr.h"
@@ -14,7 +16,6 @@
 #include "device/gamepad/gamepad_export.h"
 #include "device/gamepad/gamepad_id_list.h"
 #include "device/gamepad/gamepad_standard_mappings.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -111,7 +112,7 @@
   GamepadId gamepad_id_;
   GamepadBusType bus_type_;
   // Used to offset touch ids sent to Gamepad
-  absl::optional<uint32_t> initial_touch_id_;
+  std::optional<uint32_t> initial_touch_id_;
   ContinueCircularIndexPair transform_touch_id_;
   std::unique_ptr<HidWriter> writer_;
   base::WeakPtrFactory<Dualshock4Controller> weak_factory_{this};
diff --git a/device/gamepad/gamepad_pad_state_provider.cc b/device/gamepad/gamepad_pad_state_provider.cc
index 3fd9c18c..c762f4e2 100644
--- a/device/gamepad/gamepad_pad_state_provider.cc
+++ b/device/gamepad/gamepad_pad_state_provider.cc
@@ -6,11 +6,11 @@
 
 #include <cmath>
 #include <memory>
+#include <optional>
 
 #include "device/gamepad/gamepad_data_fetcher.h"
 #include "device/gamepad/gamepad_provider.h"
 #include "device/gamepad/public/cpp/gamepads.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -36,8 +36,8 @@
                                                int source_id,
                                                bool new_gamepad_recognized) {
   // Check to see if the device already has a reserved slot
-  absl::optional<size_t> empty_slot_index;
-  absl::optional<size_t> unrecognized_slot_index;
+  std::optional<size_t> empty_slot_index;
+  std::optional<size_t> unrecognized_slot_index;
   for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i) {
     auto& state = pad_states_.get()[i];
     if (state.source == source && state.source_id == source_id) {
diff --git a/device/gamepad/nintendo_controller.cc b/device/gamepad/nintendo_controller.cc
index dd50d2e5..134c463 100644
--- a/device/gamepad/nintendo_controller.cc
+++ b/device/gamepad/nintendo_controller.cc
@@ -1679,7 +1679,7 @@
 void NintendoController::OnReadInputReport(
     bool success,
     uint8_t report_id,
-    const absl::optional<std::vector<uint8_t>>& report_bytes) {
+    const std::optional<std::vector<uint8_t>>& report_bytes) {
   if (success) {
     DCHECK(report_bytes);
     HandleInputReport(report_id, *report_bytes);
diff --git a/device/gamepad/nintendo_controller.h b/device/gamepad/nintendo_controller.h
index 6f01b46..e527810 100644
--- a/device/gamepad/nintendo_controller.h
+++ b/device/gamepad/nintendo_controller.h
@@ -6,6 +6,7 @@
 #define DEVICE_GAMEPAD_NINTENDO_CONTROLLER_H_
 
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "base/cancelable_callback.h"
@@ -18,7 +19,6 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/device/public/mojom/hid.mojom.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -289,7 +289,7 @@
   void OnReadInputReport(
       bool success,
       uint8_t report_id,
-      const absl::optional<std::vector<uint8_t>>& report_bytes);
+      const std::optional<std::vector<uint8_t>>& report_bytes);
 
   // Request to send an output report to the underlying HID device. If
   // |expect_reply| is true, a timeout is armed that will retry the current
diff --git a/device/gamepad/raw_input_gamepad_device_win.cc b/device/gamepad/raw_input_gamepad_device_win.cc
index 51d57fe..ac6229eb 100644
--- a/device/gamepad/raw_input_gamepad_device_win.cc
+++ b/device/gamepad/raw_input_gamepad_device_win.cc
@@ -16,6 +16,7 @@
 // clang-format on
 
 #include <algorithm>
+#include <optional>
 
 #include "base/strings/string_util_win.h"
 #include "base/strings/sys_string_conversions.h"
@@ -25,7 +26,6 @@
 #include "device/gamepad/hid_haptic_gamepad.h"
 #include "device/gamepad/hid_writer_win.h"
 #include "device/gamepad/public/cpp/gamepad_features.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -91,7 +91,7 @@
     : handle_(device_handle),
       source_id_(source_id),
       last_update_timestamp_(GamepadDataFetcher::CurrentTimeInMicroseconds()),
-      button_report_id_(Gamepad::kButtonsLengthCap, absl::nullopt) {
+      button_report_id_(Gamepad::kButtonsLengthCap, std::nullopt) {
   ::ZeroMemory(buttons_, sizeof(buttons_));
   ::ZeroMemory(axes_, sizeof(axes_));
 
diff --git a/device/gamepad/raw_input_gamepad_device_win.h b/device/gamepad/raw_input_gamepad_device_win.h
index 1e9c1222..ba15be9e 100644
--- a/device/gamepad/raw_input_gamepad_device_win.h
+++ b/device/gamepad/raw_input_gamepad_device_win.h
@@ -13,12 +13,12 @@
 #include <windows.h>
 
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "base/memory/weak_ptr.h"
 #include "device/gamepad/abstract_haptic_gamepad.h"
 #include "device/gamepad/public/cpp/gamepad.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -129,7 +129,7 @@
   bool buttons_[Gamepad::kButtonsLengthCap];
 
   // The report ID for each button index, or nullopt if the button is not used.
-  std::vector<absl::optional<uint8_t>> button_report_id_;
+  std::vector<std::optional<uint8_t>> button_report_id_;
 
   // Bitfield to keep track of which axes indices are in use.
   uint32_t axes_used_ = 0;
diff --git a/device/gamepad/wgi_data_fetcher_win.cc b/device/gamepad/wgi_data_fetcher_win.cc
index 53b443a..b490750 100644
--- a/device/gamepad/wgi_data_fetcher_win.cc
+++ b/device/gamepad/wgi_data_fetcher_win.cc
@@ -11,6 +11,7 @@
 
 #include <algorithm>
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 
@@ -30,7 +31,6 @@
 #include "device/gamepad/gamepad_standard_mappings.h"
 #include "device/gamepad/nintendo_controller.h"
 #include "device/gamepad/wgi_gamepad_device.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -70,7 +70,7 @@
   return raw_game_controller;
 }
 
-absl::optional<GamepadId> GetGamepadId(
+std::optional<GamepadId> GetGamepadId(
     const std::u16string& product_name,
     ABI::Windows::Gaming::Input::IGamepad* gamepad,
     WgiDataFetcherWin::GetActivationFactoryFunction
@@ -81,19 +81,19 @@
       raw_game_controller =
           GetRawGameController(gamepad, get_activation_factory_function);
   if (!raw_game_controller) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   uint16_t vendor_id;
   hr = raw_game_controller->get_HardwareVendorId(&vendor_id);
   if (FAILED(hr)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   uint16_t product_id;
   hr = raw_game_controller->get_HardwareProductId(&product_id);
   if (FAILED(hr)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return GamepadIdList::Get().GetGamepadId(product_name_string, vendor_id,
@@ -235,10 +235,10 @@
     return;
 
   const std::u16string display_name = GetGamepadDisplayName(gamepad);
-  absl::optional<GamepadId> gamepad_id_optional =
+  std::optional<GamepadId> gamepad_id_optional =
       GetGamepadId(display_name, gamepad, get_activation_factory_function_);
 
-  // If `gamepad_id_optional` has absl::nullopt, it means that an error has
+  // If `gamepad_id_optional` has std::nullopt, it means that an error has
   // happened when calling the Windows API's.
   if (!gamepad_id_optional.has_value()) {
     return;
diff --git a/device/gamepad/wgi_data_fetcher_win.h b/device/gamepad/wgi_data_fetcher_win.h
index a5a2784..ef51baa 100644
--- a/device/gamepad/wgi_data_fetcher_win.h
+++ b/device/gamepad/wgi_data_fetcher_win.h
@@ -5,12 +5,11 @@
 #ifndef DEVICE_GAMEPAD_WGI_DATA_FETCHER_WIN_H_
 #define DEVICE_GAMEPAD_WGI_DATA_FETCHER_WIN_H_
 
-#include "device/gamepad/gamepad_data_fetcher.h"
-
 #include <Windows.Gaming.Input.h>
 #include <wrl/event.h>
 
 #include <memory>
+#include <optional>
 #include <string>
 
 #include "base/containers/flat_map.h"
@@ -19,11 +18,11 @@
 #include "base/no_destructor.h"
 #include "base/sequence_checker.h"
 #include "base/task/sequenced_task_runner.h"
+#include "device/gamepad/gamepad_data_fetcher.h"
 #include "device/gamepad/gamepad_id_list.h"
 #include "device/gamepad/public/mojom/gamepad.mojom.h"
 #include "device/gamepad/wgi_gamepad_device.h"
 #include "device/gamepad/xinput_data_fetcher_win.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -121,8 +120,8 @@
 
   GetActivationFactoryFunction get_activation_factory_function_;
 
-  absl::optional<EventRegistrationToken> added_event_token_;
-  absl::optional<EventRegistrationToken> removed_event_token_;
+  std::optional<EventRegistrationToken> added_event_token_;
+  std::optional<EventRegistrationToken> removed_event_token_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/device/udev_linux/fake_udev_loader.cc b/device/udev_linux/fake_udev_loader.cc
index be6a846..7169cae 100644
--- a/device/udev_linux/fake_udev_loader.cc
+++ b/device/udev_linux/fake_udev_loader.cc
@@ -30,8 +30,8 @@
   udev_device(std::string name,
               std::string syspath,
               std::string subsystem,
-              absl::optional<std::string> devnode,
-              absl::optional<std::string> devtype,
+              std::optional<std::string> devnode,
+              std::optional<std::string> devtype,
               std::map<std::string, std::string> sysattrs,
               std::map<std::string, std::string> prop_map)
       : name(std::move(name)),
@@ -54,8 +54,8 @@
   const std::string name;
   const std::string syspath;
   const std::string subsystem;
-  const absl::optional<std::string> devnode;
-  const absl::optional<std::string> devtype;
+  const std::optional<std::string> devnode;
+  const std::optional<std::string> devtype;
   std::map<std::string, std::string> sysattrs;
   std::map<std::string, std::string> properties;
   std::vector<std::unique_ptr<udev_list_entry>> udev_prop_list;
@@ -110,8 +110,8 @@
     std::string name,
     std::string syspath,
     std::string subsystem,
-    absl::optional<std::string> devnode,
-    absl::optional<std::string> devtype,
+    std::optional<std::string> devnode,
+    std::optional<std::string> devtype,
     std::map<std::string, std::string> sysattrs,
     std::map<std::string, std::string> properties) {
   devices_.emplace_back(
diff --git a/device/udev_linux/fake_udev_loader.h b/device/udev_linux/fake_udev_loader.h
index 6fdc4a9..0a974fe 100644
--- a/device/udev_linux/fake_udev_loader.h
+++ b/device/udev_linux/fake_udev_loader.h
@@ -7,11 +7,11 @@
 
 #include <map>
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
 #include "device/udev_linux/udev_loader.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace testing {
 
@@ -25,8 +25,8 @@
   udev_device* AddFakeDevice(std::string name,
                              std::string syspath,
                              std::string subsystem,
-                             absl::optional<std::string> devnode,
-                             absl::optional<std::string> devtype,
+                             std::optional<std::string> devnode,
+                             std::optional<std::string> devtype,
                              std::map<std::string, std::string> sysattrs,
                              std::map<std::string, std::string> properties);
 
diff --git a/device/udev_linux/udev_unittest.cc b/device/udev_linux/udev_unittest.cc
index 9dd5f55..9fda5ea0 100644
--- a/device/udev_linux/udev_unittest.cc
+++ b/device/udev_linux/udev_unittest.cc
@@ -32,8 +32,8 @@
   testing::FakeUdevLoader fake_udev;
   udev_device* device =
       fake_udev.AddFakeDevice(/*name=*/"Foo", /*syspath=*/"/device/foo",
-                              /*subsystem=*/"", /*devnode=*/absl::nullopt,
-                              /*devtype=*/absl::nullopt, /*sysattrs=*/{},
+                              /*subsystem=*/"", /*devnode=*/std::nullopt,
+                              /*devtype=*/std::nullopt, /*sysattrs=*/{},
                               /*properties=*/{});
 
   const std::string attr_value = UdevDeviceGetPropertyValue(device, "prop");
@@ -46,7 +46,7 @@
   props.emplace("prop", "prop value");
   udev_device* device = fake_udev.AddFakeDevice(
       /*name=*/"Foo", /*syspath=*/"/device/foo",
-      /*subsystem=*/"", /*devnode=*/absl::nullopt, /*devtype=*/absl::nullopt,
+      /*subsystem=*/"", /*devnode=*/std::nullopt, /*devtype=*/std::nullopt,
       /*sysattrs=*/{}, std::move(props));
 
   std::string attr_value = UdevDeviceGetPropertyValue(device, "prop");
@@ -77,7 +77,7 @@
   udev_device* device = fake_udev.AddFakeDevice(
       /*name=*/"/dev/dri/card0",
       /*syspath=*/"/devices/pci0000:00/0000:00:02.0/drm/card0",
-      /*subsystem=*/"drm", /*devnode=*/absl::nullopt, /*devtype=*/absl::nullopt,
+      /*subsystem=*/"drm", /*devnode=*/std::nullopt, /*devtype=*/std::nullopt,
       /*sysattrs=*/{}, std::move(props));
 
   udev_list_entry* prop_list = udev_device_get_properties_list_entry(device);
@@ -100,7 +100,7 @@
   testing::FakeUdevLoader fake_udev;
   udev_device* device = fake_udev.AddFakeDevice(
       /*name=*/"Foo", /*syspath=*/"/device/foo",
-      /*subsystem=*/"", /*devnode=*/absl::nullopt, /*devtype=*/absl::nullopt,
+      /*subsystem=*/"", /*devnode=*/std::nullopt, /*devtype=*/std::nullopt,
       /*sysattrs=*/{}, /*properties=*/{});
 
   const std::string attr_value = UdevDeviceGetSysattrValue(device, "attr");
@@ -113,7 +113,7 @@
   attrs.emplace("attr", "attr value");
   udev_device* device = fake_udev.AddFakeDevice(
       /*name=*/"Foo", /*syspath=*/"/device/foo",
-      /*subsystem=*/"", /*devnode=*/absl::nullopt, /*devtype=*/absl::nullopt,
+      /*subsystem=*/"", /*devnode=*/std::nullopt, /*devtype=*/std::nullopt,
       std::move(attrs), /*properties=*/{});
 
   std::string attr_value = UdevDeviceGetSysattrValue(device, "attr");
@@ -128,15 +128,15 @@
   std::map<std::string, std::string> attrs;
   udev_device* grandparent = fake_udev.AddFakeDevice(
       /*name=*/"Foo", /*syspath=*/"/device/foo",
-      /*subsystem=*/"", /*devnode=*/absl::nullopt, /*devtype=*/absl::nullopt,
+      /*subsystem=*/"", /*devnode=*/std::nullopt, /*devtype=*/std::nullopt,
       /*sysattrs=*/{}, /*properties=*/{});
   udev_device* parent = fake_udev.AddFakeDevice(
       /*name=*/"Foo", /*syspath=*/"/device/foo/bar",
-      /*subsystem=*/"", /*devnode=*/absl::nullopt, /*devtype=*/absl::nullopt,
+      /*subsystem=*/"", /*devnode=*/std::nullopt, /*devtype=*/std::nullopt,
       /*sysattrs=*/{}, /*properties=*/{});
   udev_device* device = fake_udev.AddFakeDevice(
       /*name=*/"Foo", /*syspath=*/"/device/foo/bar/baz",
-      /*subsystem=*/"", /*devnode=*/absl::nullopt, /*devtype=*/absl::nullopt,
+      /*subsystem=*/"", /*devnode=*/std::nullopt, /*devtype=*/std::nullopt,
       /*sysattrs=*/{}, /*properties=*/{});
 
   EXPECT_EQ(parent, udev_device_get_parent(device));
@@ -149,12 +149,12 @@
   std::map<std::string, std::string> attrs;
   attrs.emplace("attr", "attr value");
   fake_udev.AddFakeDevice(/*name=*/"Foo", /*syspath=*/"/device/foo",
-                          /*subsystem=*/"", /*devnode=*/absl::nullopt,
-                          /*devtype=*/absl::nullopt, std::move(attrs),
+                          /*subsystem=*/"", /*devnode=*/std::nullopt,
+                          /*devtype=*/std::nullopt, std::move(attrs),
                           /*properties=*/{});
   udev_device* device = fake_udev.AddFakeDevice(
       /*name=*/"Foo", /*syspath=*/"/device/foo/bar",
-      /*subsystem=*/"", /*devnode=*/absl::nullopt, /*devtype=*/absl::nullopt,
+      /*subsystem=*/"", /*devnode=*/std::nullopt, /*devtype=*/std::nullopt,
       /*sysattrs=*/{}, /*properties=*/{});
 
   // Don't find the attr on the current device.
diff --git a/device/udev_linux/udev_watcher.h b/device/udev_linux/udev_watcher.h
index 350faf11..37734b1 100644
--- a/device/udev_linux/udev_watcher.h
+++ b/device/udev_linux/udev_watcher.h
@@ -6,6 +6,7 @@
 #define DEVICE_UDEV_LINUX_UDEV_WATCHER_H_
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <string_view>
 #include <vector>
@@ -15,7 +16,6 @@
 #include "base/memory/raw_ptr.h"
 #include "base/sequence_checker.h"
 #include "device/udev_linux/scoped_udev.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -45,8 +45,8 @@
     const char* subsystem() const;
 
    private:
-    absl::optional<std::string> subsystem_;
-    absl::optional<std::string> devtype_;
+    std::optional<std::string> subsystem_;
+    std::optional<std::string> devtype_;
   };
 
   static std::unique_ptr<UdevWatcher> StartWatching(
diff --git a/device/vr/android/arcore/address_to_id_map.h b/device/vr/android/arcore/address_to_id_map.h
index d81ce365..dd48982 100644
--- a/device/vr/android/arcore/address_to_id_map.h
+++ b/device/vr/android/arcore/address_to_id_map.h
@@ -6,10 +6,10 @@
 #define DEVICE_VR_ANDROID_ARCORE_ADDRESS_TO_ID_MAP_H_
 
 #include <limits>
+#include <optional>
 #include <unordered_map>
 
 #include "base/check.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -53,10 +53,10 @@
   }
 
   // Gets the id for the corresponding address, if it's available.
-  absl::optional<IdType> GetId(void* address) const {
+  std::optional<IdType> GetId(void* address) const {
     auto it = address_to_id_.find(address);
     if (it == address_to_id_.end()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     return it->second;
diff --git a/device/vr/android/arcore/ar_compositor_frame_sink.cc b/device/vr/android/arcore/ar_compositor_frame_sink.cc
index ab0c5542..74d35e0 100644
--- a/device/vr/android/arcore/ar_compositor_frame_sink.cc
+++ b/device/vr/android/arcore/ar_compositor_frame_sink.cc
@@ -163,7 +163,7 @@
     // If the frame sink client doesn't have a valid SurfaceId at this point,
     // then the compositor hierarchy did not get set up, which means that we'll
     // be unable to composite DOM content.
-    absl::optional<viz::SurfaceId> dom_surface =
+    std::optional<viz::SurfaceId> dom_surface =
         xr_frame_sink_client_->GetDOMSurface();
     if (dom_surface && dom_surface->is_valid()) {
       should_composite_dom_overlay_ = true;
@@ -213,7 +213,7 @@
   DCHECK(xr_frame);
   sink_remote_->SubmitCompositorFrame(allocator_.GetCurrentLocalSurfaceId(),
                                       CreateFrame(xr_frame, frame_type),
-                                      absl::optional<viz::HitTestRegionList>(),
+                                      std::optional<viz::HitTestRegionList>(),
                                       /*trace_time=*/0);
 }
 
@@ -387,7 +387,7 @@
           gfx::Transform(),
           /*quad_layer_rect=*/output_rect,
           /*visible_layer_rect=*/output_rect, gfx::MaskFilterInfo(),
-          /*clip_rect=*/absl::nullopt, /*are_contents_opaque=*/false,
+          /*clip_rect=*/std::nullopt, /*are_contents_opaque=*/false,
           /*opacity=*/1.f, SkBlendMode::kSrcOver, /*sorting_context_id=*/0,
           /*layer_id=*/0u, /*fast_rounded_corner=*/false);
 
@@ -415,7 +415,7 @@
         gfx::Transform(),
         /*quad_layer_rect=*/output_rect,
         /*visible_layer_rect=*/output_rect, gfx::MaskFilterInfo(),
-        /*clip_rect=*/absl::nullopt, /*are_contents_opaque=*/false,
+        /*clip_rect=*/std::nullopt, /*are_contents_opaque=*/false,
         /*opacity=*/1.f, SkBlendMode::kSrcOver, /*sorting_context_id=*/0,
         /*layer_id=*/0u, /*fast_rounded_corner=*/false);
 
@@ -456,7 +456,7 @@
       gfx::Transform(),
       /*quad_layer_rect=*/output_rect,
       /*visible_layer_rect=*/output_rect, gfx::MaskFilterInfo(),
-      /*clip_rect=*/absl::nullopt, /*are_contents_opaque=*/true,
+      /*clip_rect=*/std::nullopt, /*are_contents_opaque=*/true,
       /*opacity=*/1.f, SkBlendMode::kSrcOver, /*sorting_context_id=*/0,
       /*layer_id=*/0u, /*fast_rounded_corner=*/false);
 
diff --git a/device/vr/android/arcore/arcore.cc b/device/vr/android/arcore/arcore.cc
index 1fe5341c..1483f5b3 100644
--- a/device/vr/android/arcore/arcore.cc
+++ b/device/vr/android/arcore/arcore.cc
@@ -40,7 +40,7 @@
 
 ArCore::InitializeResult::InitializeResult(
     const std::unordered_set<device::mojom::XRSessionFeature>& enabled_features,
-    absl::optional<device::mojom::XRDepthConfig> depth_configuration)
+    std::optional<device::mojom::XRDepthConfig> depth_configuration)
     : enabled_features(enabled_features),
       depth_configuration(std::move(depth_configuration)) {}
 
diff --git a/device/vr/android/arcore/arcore.h b/device/vr/android/arcore/arcore.h
index c237f19..1959ccb 100644
--- a/device/vr/android/arcore/arcore.h
+++ b/device/vr/android/arcore/arcore.h
@@ -6,6 +6,7 @@
 #define DEVICE_VR_ANDROID_ARCORE_ARCORE_H_
 
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "base/android/scoped_java_ref.h"
@@ -14,7 +15,6 @@
 #include "device/vr/public/mojom/isolated_xr_service.mojom.h"
 #include "device/vr/public/mojom/vr_service.mojom.h"
 #include "device/vr/public/mojom/xr_session.mojom.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/display/display.h"
 #include "ui/gfx/geometry/transform.h"
 
@@ -40,12 +40,12 @@
     // If the depth sensing API was requested, the depth_configuration will
     // contain the device-selected depth API usage and data format.
 
-    absl::optional<device::mojom::XRDepthConfig> depth_configuration;
+    std::optional<device::mojom::XRDepthConfig> depth_configuration;
 
     InitializeResult(
         const std::unordered_set<device::mojom::XRSessionFeature>&
             enabled_features,
-        absl::optional<device::mojom::XRDepthConfig> depth_configuration);
+        std::optional<device::mojom::XRDepthConfig> depth_configuration);
     InitializeResult(const InitializeResult& other);
     ~InitializeResult();
   };
@@ -70,14 +70,14 @@
 
   // Initializes the runtime and returns whether it was successful.
   // If successful, the runtime must be paused when this method returns.
-  virtual absl::optional<InitializeResult> Initialize(
+  virtual std::optional<InitializeResult> Initialize(
       base::android::ScopedJavaLocalRef<jobject> application_context,
       const std::unordered_set<device::mojom::XRSessionFeature>&
           required_features,
       const std::unordered_set<device::mojom::XRSessionFeature>&
           optional_features,
       const std::vector<device::mojom::XRTrackedImagePtr>& tracked_images,
-      absl::optional<DepthSensingConfiguration> depth_sensing_config) = 0;
+      std::optional<DepthSensingConfiguration> depth_sensing_config) = 0;
 
   // Returns the target framerate range in Hz. Actual capture frame rate will
   // vary within this range, i.e. lower in low light to increase exposure time.
@@ -126,24 +126,24 @@
       const mojom::XRRayPtr& ray,
       std::vector<mojom::XRHitResultPtr>* hit_results) = 0;
 
-  // Subscribes to hit test. Returns absl::nullopt if subscription failed.
+  // Subscribes to hit test. Returns std::nullopt if subscription failed.
   // This variant will subscribe for a hit test to a specific native origin
   // specified in |native_origin_information|. The native origin will be used
   // along with passed in ray to compute the hit test results as of latest
   // frame. The passed in |entity_types| will be used to filter out the results
   // that do not match anything in the vector.
-  virtual absl::optional<uint64_t> SubscribeToHitTest(
+  virtual std::optional<uint64_t> SubscribeToHitTest(
       mojom::XRNativeOriginInformationPtr native_origin_information,
       const std::vector<mojom::EntityTypeForHitTest>& entity_types,
       mojom::XRRayPtr ray) = 0;
-  // Subscribes to hit test for transient input sources. Returns absl::nullopt
+  // Subscribes to hit test for transient input sources. Returns std::nullopt
   // if subscription failed. This variant will subscribe for a hit test to
   // transient input sources that match the |profile_name|. The passed in ray
   // will be used to compute the hit test results as of latest frame (relative
   // to the location of transient input source). The passed in |entity_types|
   // will be used to filter out the results that do not match anything in the
   // vector.
-  virtual absl::optional<uint64_t> SubscribeToHitTestForTransientInput(
+  virtual std::optional<uint64_t> SubscribeToHitTestForTransientInput(
       const std::string& profile_name,
       const std::vector<mojom::EntityTypeForHitTest>& entity_types,
       mojom::XRRayPtr ray) = 0;
diff --git a/device/vr/android/arcore/arcore_anchor_manager.cc b/device/vr/android/arcore/arcore_anchor_manager.cc
index 28b0a4b..9fe0c38 100644
--- a/device/vr/android/arcore/arcore_anchor_manager.cc
+++ b/device/vr/android/arcore/arcore_anchor_manager.cc
@@ -69,7 +69,7 @@
                << ", position=untracked, orientation=untracked";
 
       updated_anchors.push_back(
-          mojom::XRAnchorData::New(anchor_id.GetUnsafeValue(), absl::nullopt));
+          mojom::XRAnchorData::New(anchor_id.GetUnsafeValue(), std::nullopt));
     }
   }
 
@@ -188,7 +188,7 @@
   updated_anchor_ids_.swap(updated_anchor_ids);
 }
 
-absl::optional<AnchorId> ArCoreAnchorManager::CreateAnchor(
+std::optional<AnchorId> ArCoreAnchorManager::CreateAnchor(
     const device::mojom::Pose& pose) {
   auto ar_pose = GetArPoseFromMojomPose(arcore_session_, pose);
 
@@ -199,7 +199,7 @@
           .get());
 
   if (status != AR_SUCCESS) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   auto result = anchor_address_to_id_.CreateOrGetId(ar_anchor.get());
@@ -219,7 +219,7 @@
   return result.id;
 }
 
-absl::optional<AnchorId> ArCoreAnchorManager::CreateAnchor(
+std::optional<AnchorId> ArCoreAnchorManager::CreateAnchor(
     ArCorePlaneManager* plane_manager,
     const device::mojom::Pose& pose,
     PlaneId plane_id) {
@@ -230,7 +230,7 @@
   auto ar_anchor = plane_manager->CreateAnchor(
       base::PassKey<ArCoreAnchorManager>(), plane_id, pose);
   if (!ar_anchor.is_valid()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   auto result = anchor_address_to_id_.CreateOrGetId(ar_anchor.get());
@@ -265,11 +265,11 @@
   return base::Contains(anchor_id_to_anchor_info_, id);
 }
 
-absl::optional<gfx::Transform> ArCoreAnchorManager::GetMojoFromAnchor(
+std::optional<gfx::Transform> ArCoreAnchorManager::GetMojoFromAnchor(
     AnchorId id) const {
   auto it = anchor_id_to_anchor_info_.find(id);
   if (it == anchor_id_to_anchor_info_.end()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   ArAnchor_getPose(arcore_session_, it->second.anchor.get(), ar_pose_.get());
diff --git a/device/vr/android/arcore/arcore_anchor_manager.h b/device/vr/android/arcore/arcore_anchor_manager.h
index ca47eaa..0db9deca 100644
--- a/device/vr/android/arcore/arcore_anchor_manager.h
+++ b/device/vr/android/arcore/arcore_anchor_manager.h
@@ -6,6 +6,7 @@
 #define DEVICE_VR_ANDROID_ARCORE_ARCORE_ANCHOR_MANAGER_H_
 
 #include <map>
+#include <optional>
 
 #include "base/memory/raw_ptr.h"
 #include "base/types/id_type.h"
@@ -15,7 +16,6 @@
 #include "device/vr/android/arcore/arcore_sdk.h"
 #include "device/vr/android/arcore/scoped_arcore_objects.h"
 #include "device/vr/public/mojom/vr_service.mojom.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -37,16 +37,16 @@
 
   bool AnchorExists(AnchorId id) const;
 
-  // Returns absl::nullopt if anchor with the given id does not exist.
-  absl::optional<gfx::Transform> GetMojoFromAnchor(AnchorId id) const;
+  // Returns std::nullopt if anchor with the given id does not exist.
+  std::optional<gfx::Transform> GetMojoFromAnchor(AnchorId id) const;
 
   // Creates Anchor object given a plane ID.
-  absl::optional<AnchorId> CreateAnchor(ArCorePlaneManager* plane_manager,
-                                        const device::mojom::Pose& pose,
-                                        PlaneId plane_id);
+  std::optional<AnchorId> CreateAnchor(ArCorePlaneManager* plane_manager,
+                                       const device::mojom::Pose& pose,
+                                       PlaneId plane_id);
 
   // Creates free-floating Anchor.
-  absl::optional<AnchorId> CreateAnchor(const device::mojom::Pose& pose);
+  std::optional<AnchorId> CreateAnchor(const device::mojom::Pose& pose);
 
   void DetachAnchor(AnchorId anchor_id);
 
diff --git a/device/vr/android/arcore/arcore_device.cc b/device/vr/android/arcore/arcore_device.cc
index 93fa23fe..c9acd94 100644
--- a/device/vr/android/arcore/arcore_device.cc
+++ b/device/vr/android/arcore/arcore_device.cc
@@ -5,6 +5,7 @@
 #include "device/vr/android/arcore/arcore_device.h"
 
 #include <algorithm>
+#include <optional>
 
 #include "base/android/android_hardware_buffer_compat.h"
 #include "base/containers/contains.h"
@@ -22,7 +23,6 @@
 #include "device/vr/android/mailbox_to_surface_bridge.h"
 #include "device/vr/android/xr_java_coordinator.h"
 #include "device/vr/public/cpp/xr_frame_sink_client.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/android/window_android.h"
 #include "ui/display/display.h"
 
@@ -481,7 +481,7 @@
     return;
   } else {
     session_state_->enabled_features_ = {};
-    session_state_->depth_configuration_ = absl::nullopt;
+    session_state_->depth_configuration_ = std::nullopt;
   }
 
   // We only start GL initialization after the user has granted consent, so we
diff --git a/device/vr/android/arcore/arcore_device.h b/device/vr/android/arcore/arcore_device.h
index 183c8e1..d496185 100644
--- a/device/vr/android/arcore/arcore_device.h
+++ b/device/vr/android/arcore/arcore_device.h
@@ -6,7 +6,9 @@
 #define DEVICE_VR_ANDROID_ARCORE_ARCORE_DEVICE_H_
 
 #include <jni.h>
+
 #include <memory>
+#include <optional>
 #include <unordered_set>
 #include <utility>
 
@@ -20,7 +22,6 @@
 #include "device/vr/vr_device_base.h"
 #include "gpu/ipc/common/surface_handle.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/geometry/size_f.h"
 #include "ui/gfx/native_widget_types.h"
 
@@ -150,7 +151,7 @@
     // empty, will be set only once the ArCoreGl has been initialized.
     std::unordered_set<device::mojom::XRSessionFeature> enabled_features_;
 
-    absl::optional<device::mojom::XRDepthConfig> depth_configuration_;
+    std::optional<device::mojom::XRDepthConfig> depth_configuration_;
 
     std::vector<device::mojom::XRTrackedImagePtr> tracked_images_;
 
diff --git a/device/vr/android/arcore/arcore_gl.cc b/device/vr/android/arcore/arcore_gl.cc
index 313e0ee..092b29c 100644
--- a/device/vr/android/arcore/arcore_gl.cc
+++ b/device/vr/android/arcore/arcore_gl.cc
@@ -120,7 +120,7 @@
 
 ArCoreGlInitializeResult::ArCoreGlInitializeResult(
     std::unordered_set<device::mojom::XRSessionFeature> enabled_features,
-    absl::optional<device::mojom::XRDepthConfig> depth_configuration,
+    std::optional<device::mojom::XRDepthConfig> depth_configuration,
     viz::FrameSinkId frame_sink_id)
     : enabled_features(enabled_features),
       depth_configuration(depth_configuration),
@@ -230,7 +230,7 @@
     return;
   }
 
-  absl::optional<ArCore::DepthSensingConfiguration> depth_sensing_config;
+  std::optional<ArCore::DepthSensingConfiguration> depth_sensing_config;
   if (depth_options) {
     depth_sensing_config = ArCore::DepthSensingConfiguration(
         depth_options->usage_preferences,
@@ -249,7 +249,7 @@
   }
 
   arcore_ = arcore_factory->Create();
-  absl::optional<ArCore::InitializeResult> maybe_initialize_result =
+  std::optional<ArCore::InitializeResult> maybe_initialize_result =
       arcore_->Initialize(application_context, required_features,
                           optional_features, tracked_images,
                           std::move(depth_sensing_config));
@@ -1442,7 +1442,7 @@
     return;
   }
 
-  absl::optional<uint64_t> maybe_subscription_id = arcore_->SubscribeToHitTest(
+  std::optional<uint64_t> maybe_subscription_id = arcore_->SubscribeToHitTest(
       std::move(native_origin_information), entity_types, std::move(ray));
 
   if (maybe_subscription_id) {
@@ -1465,7 +1465,7 @@
   DVLOG(2) << __func__ << ": ray origin=" << ray->origin.ToString()
            << ", ray direction=" << ray->direction.ToString();
 
-  absl::optional<uint64_t> maybe_subscription_id =
+  std::optional<uint64_t> maybe_subscription_id =
       arcore_->SubscribeToHitTestForTransientInput(profile_name, entity_types,
                                                    std::move(ray));
 
diff --git a/device/vr/android/arcore/arcore_gl.h b/device/vr/android/arcore/arcore_gl.h
index decb4cd..0eb6171 100644
--- a/device/vr/android/arcore/arcore_gl.h
+++ b/device/vr/android/arcore/arcore_gl.h
@@ -74,12 +74,12 @@
 
 struct ArCoreGlInitializeResult {
   std::unordered_set<device::mojom::XRSessionFeature> enabled_features;
-  absl::optional<device::mojom::XRDepthConfig> depth_configuration;
+  std::optional<device::mojom::XRDepthConfig> depth_configuration;
   viz::FrameSinkId frame_sink_id;
 
   ArCoreGlInitializeResult(
       std::unordered_set<device::mojom::XRSessionFeature> enabled_features,
-      absl::optional<device::mojom::XRDepthConfig> depth_configuration,
+      std::optional<device::mojom::XRDepthConfig> depth_configuration,
       viz::FrameSinkId frame_sink_id);
   ArCoreGlInitializeResult(ArCoreGlInitializeResult&& other);
   ~ArCoreGlInitializeResult();
@@ -254,7 +254,7 @@
   // the session and only send out necessary data related to reference spaces to
   // blink. Valid after the call to |Initialize()| method.
   std::unordered_set<device::mojom::XRSessionFeature> enabled_features_;
-  absl::optional<device::mojom::XRDepthConfig> depth_configuration_;
+  std::optional<device::mojom::XRDepthConfig> depth_configuration_;
 
   base::OnceClosure session_shutdown_callback_;
 
@@ -380,7 +380,7 @@
   uint32_t stage_parameters_id_;
 
   // Currently estimated floor height.
-  absl::optional<float> floor_height_estimate_;
+  std::optional<float> floor_height_estimate_;
 
   // Touch-related data.
   // Android will report touch events via MotionEvent - see XrImmersiveOverlay
diff --git a/device/vr/android/arcore/arcore_impl.cc b/device/vr/android/arcore/arcore_impl.cc
index e372501b..829202e 100644
--- a/device/vr/android/arcore/arcore_impl.cc
+++ b/device/vr/android/arcore/arcore_impl.cc
@@ -4,6 +4,8 @@
 
 #include "device/vr/android/arcore/arcore_impl.h"
 
+#include <optional>
+
 #include "base/android/jni_android.h"
 #include "base/containers/contains.h"
 #include "base/containers/span.h"
@@ -23,7 +25,6 @@
 #include "device/vr/public/mojom/pose.h"
 #include "device/vr/public/mojom/vr_service.mojom.h"
 #include "device/vr/public/mojom/xr_session.mojom.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkImage.h"
@@ -82,12 +83,12 @@
 // ray mode is "tapping", which is OK for input sources available for AR on
 // Android, but is not true in the general case. This method should duplicate
 // the logic found in XRTargetRaySpace::MojoFromNative().
-absl::optional<gfx::Transform> GetMojoFromInputSource(
+std::optional<gfx::Transform> GetMojoFromInputSource(
     const device::mojom::XRInputSourceStatePtr& input_source_state,
     const gfx::Transform& mojo_from_viewer) {
   if (!input_source_state->description ||
       !input_source_state->description->input_from_pointer) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   gfx::Transform viewer_from_pointer =
@@ -533,14 +534,14 @@
   }
 }
 
-absl::optional<ArCore::InitializeResult> ArCoreImpl::Initialize(
+std::optional<ArCore::InitializeResult> ArCoreImpl::Initialize(
     base::android::ScopedJavaLocalRef<jobject> context,
     const std::unordered_set<device::mojom::XRSessionFeature>&
         required_features,
     const std::unordered_set<device::mojom::XRSessionFeature>&
         optional_features,
     const std::vector<device::mojom::XRTrackedImagePtr>& tracked_images,
-    absl::optional<ArCore::DepthSensingConfiguration> depth_sensing_config) {
+    std::optional<ArCore::DepthSensingConfiguration> depth_sensing_config) {
   DCHECK(IsOnGlThread());
   DCHECK(!arcore_session_.is_valid());
 
@@ -549,7 +550,7 @@
   JNIEnv* env = base::android::AttachCurrentThread();
   if (!env) {
     DLOG(ERROR) << "Unable to get JNIEnv for ArCore";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   // Use a local scoped ArSession for the next steps, we want the
@@ -562,7 +563,7 @@
       internal::ScopedArCoreObject<ArSession*>::Receiver(session).get());
   if (status != AR_SUCCESS) {
     DLOG(ERROR) << "ArSession_create failed: " << status;
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   // Set incognito mode for ARCore session - this is done unconditionally as we
@@ -582,14 +583,14 @@
   if (!ConfigureCamera(session.get(), required_features, optional_features,
                        enabled_features)) {
     DLOG(ERROR) << "Failed to configure session camera";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   if (!ConfigureFeatures(session.get(), required_features, optional_features,
                          tracked_images, depth_sensing_config,
                          enabled_features)) {
     DLOG(ERROR) << "Failed to configure session features";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   internal::ScopedArCoreObject<ArFrame*> frame;
@@ -597,7 +598,7 @@
                  internal::ScopedArCoreObject<ArFrame*>::Receiver(frame).get());
   if (!frame.is_valid()) {
     DLOG(ERROR) << "ArFrame_create failed";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   if (base::Contains(enabled_features,
@@ -609,7 +610,7 @@
             .get());
     if (!light_estimate.is_valid()) {
       DVLOG(1) << "ArLightEstimate_create failed";
-      return absl::nullopt;
+      return std::nullopt;
     }
     arcore_light_estimate_ = std::move(light_estimate);
   }
@@ -639,7 +640,7 @@
     const std::unordered_set<device::mojom::XRSessionFeature>&
         optional_features,
     const std::vector<device::mojom::XRTrackedImagePtr>& tracked_images,
-    const absl::optional<ArCore::DepthSensingConfiguration>&
+    const std::optional<ArCore::DepthSensingConfiguration>&
         depth_sensing_config,
     std::unordered_set<device::mojom::XRSessionFeature>& enabled_features) {
   internal::ScopedArCoreObject<ArConfig*> arcore_config;
@@ -747,7 +748,7 @@
 }
 
 bool ArCoreImpl::ConfigureDepthSensing(
-    const absl::optional<ArCore::DepthSensingConfiguration>&
+    const std::optional<ArCore::DepthSensingConfiguration>&
         depth_sensing_config) {
   if (!depth_sensing_config) {
     return false;
@@ -1095,7 +1096,7 @@
     return ret;
   } else {
     return mojom::XRTrackedImagesData::New(std::move(images_data),
-                                           absl::nullopt);
+                                           std::nullopt);
   }
 }
 
@@ -1211,7 +1212,7 @@
   return kDefaultFloorHeightEstimation;
 }
 
-absl::optional<uint64_t> ArCoreImpl::SubscribeToHitTest(
+std::optional<uint64_t> ArCoreImpl::SubscribeToHitTest(
     mojom::XRNativeOriginInformationPtr native_origin_information,
     const std::vector<mojom::EntityTypeForHitTest>& entity_types,
     mojom::XRRayPtr ray) {
@@ -1229,23 +1230,23 @@
       // tracking.
       if (!plane_manager_ || !plane_manager_->PlaneExists(PlaneId(
                                  native_origin_information->get_plane_id()))) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       break;
     case mojom::XRNativeOriginInformation::Tag::kHandJointSpaceInfo:
       // Unsupported by ARCore:
-      return absl::nullopt;
+      return std::nullopt;
     case mojom::XRNativeOriginInformation::Tag::kImageIndex:
       // TODO(https://crbug.com/1143575): Add hit test support for tracked
       // images.
-      return absl::nullopt;
+      return std::nullopt;
     case mojom::XRNativeOriginInformation::Tag::kAnchorId:
       // Validate that we know which anchor's space the hit test is interested
       // in tracking.
       if (!anchor_manager_ ||
           !anchor_manager_->AnchorExists(
               AnchorId(native_origin_information->get_anchor_id()))) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       break;
   }
@@ -1260,7 +1261,7 @@
   return subscription_id.GetUnsafeValue();
 }
 
-absl::optional<uint64_t> ArCoreImpl::SubscribeToHitTestForTransientInput(
+std::optional<uint64_t> ArCoreImpl::SubscribeToHitTestForTransientInput(
     const std::string& profile_name,
     const std::vector<mojom::EntityTypeForHitTest>& entity_types,
     mojom::XRRayPtr ray) {
@@ -1401,7 +1402,7 @@
                          profile_name)) {
         // Input source represented by input_state matches the profile, find
         // the transform and grab input source id.
-        absl::optional<gfx::Transform> maybe_mojo_from_input_source =
+        std::optional<gfx::Transform> maybe_mojo_from_input_source =
             GetMojoFromInputSource(input_source_state, mojo_from_viewer);
 
         if (!maybe_mojo_from_input_source)
@@ -1416,7 +1417,7 @@
   return result;
 }
 
-absl::optional<gfx::Transform> ArCoreImpl::GetMojoFromReferenceSpace(
+std::optional<gfx::Transform> ArCoreImpl::GetMojoFromReferenceSpace(
     device::mojom::XRReferenceSpaceType type,
     const gfx::Transform& mojo_from_viewer) {
   DVLOG(3) << __func__ << ": type=" << type;
@@ -1432,9 +1433,9 @@
     case device::mojom::XRReferenceSpaceType::kViewer:
       return mojo_from_viewer;
     case device::mojom::XRReferenceSpaceType::kBoundedFloor:
-      return absl::nullopt;
+      return std::nullopt;
     case device::mojom::XRReferenceSpaceType::kUnbounded:
-      return absl::nullopt;
+      return std::nullopt;
   }
 }
 
@@ -1486,7 +1487,7 @@
   }
 }
 
-absl::optional<gfx::Transform> ArCoreImpl::GetMojoFromNativeOrigin(
+std::optional<gfx::Transform> ArCoreImpl::GetMojoFromNativeOrigin(
     const mojom::XRNativeOriginInformation& native_origin_information,
     const gfx::Transform& mojo_from_viewer,
     const std::vector<mojom::XRInputSourceStatePtr>& input_state) {
@@ -1500,7 +1501,7 @@
       // guaranteed not to be localizable:
       if (input_source_space_info->input_source_space_type ==
           mojom::XRInputSourceSpaceType::kGrip) {
-        return absl::nullopt;
+        return std::nullopt;
       }
 
       // Linear search should be fine for ARCore device as it only has one input
@@ -1512,7 +1513,7 @@
         }
       }
 
-      return absl::nullopt;
+      return std::nullopt;
     }
     case mojom::XRNativeOriginInformation::Tag::kReferenceSpaceType:
       return GetMojoFromReferenceSpace(
@@ -1521,18 +1522,18 @@
     case mojom::XRNativeOriginInformation::Tag::kPlaneId:
       return plane_manager_ ? plane_manager_->GetMojoFromPlane(PlaneId(
                                   native_origin_information.get_plane_id()))
-                            : absl::nullopt;
+                            : std::nullopt;
     case mojom::XRNativeOriginInformation::Tag::kAnchorId:
       return anchor_manager_ ? anchor_manager_->GetMojoFromAnchor(AnchorId(
                                    native_origin_information.get_anchor_id()))
-                             : absl::nullopt;
+                             : std::nullopt;
     case mojom::XRNativeOriginInformation::Tag::kHandJointSpaceInfo:
-      return absl::nullopt;
+      return std::nullopt;
 
     case mojom::XRNativeOriginInformation::Tag::kImageIndex:
       // TODO(https://crbug.com/1143575): Needed for hit test and anchors
       // support for tracked images.
-      return absl::nullopt;
+      return std::nullopt;
   }
 }
 
@@ -1681,8 +1682,8 @@
         }
       }
 
-      absl::optional<PlaneId> maybe_plane_id =
-          plane_manager_ ? plane_manager_->GetPlaneId(ar_plane) : absl::nullopt;
+      std::optional<PlaneId> maybe_plane_id =
+          plane_manager_ ? plane_manager_->GetPlaneId(ar_plane) : std::nullopt;
       if (maybe_plane_id) {
         plane_id = maybe_plane_id->GetUnsafeValue();
       }
@@ -1829,7 +1830,7 @@
       continue;
     }
 
-    absl::optional<gfx::Transform> maybe_mojo_from_native_origin =
+    std::optional<gfx::Transform> maybe_mojo_from_native_origin =
         GetMojoFromNativeOrigin(native_origin_information, mojo_from_viewer,
                                 input_state);
 
@@ -1843,7 +1844,7 @@
       continue;
     }
 
-    absl::optional<device::Pose> mojo_from_anchor =
+    std::optional<device::Pose> mojo_from_anchor =
         device::Pose::Create(*maybe_mojo_from_native_origin *
                              create_anchor.GetNativeOriginFromAnchor());
 
@@ -1857,7 +1858,7 @@
       continue;
     }
 
-    absl::optional<AnchorId> maybe_anchor_id = std::forward<FunctionType>(
+    std::optional<AnchorId> maybe_anchor_id = std::forward<FunctionType>(
         create_anchor_function)(create_anchor, mojo_from_anchor->position(),
                                 mojo_from_anchor->orientation());
 
diff --git a/device/vr/android/arcore/arcore_impl.h b/device/vr/android/arcore/arcore_impl.h
index dd653fc..df4dc3d2 100644
--- a/device/vr/android/arcore/arcore_impl.h
+++ b/device/vr/android/arcore/arcore_impl.h
@@ -5,6 +5,8 @@
 #ifndef DEVICE_VR_ANDROID_ARCORE_ARCORE_IMPL_H_
 #define DEVICE_VR_ANDROID_ARCORE_ARCORE_IMPL_H_
 
+#include <optional>
+
 #include "base/component_export.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/time/time.h"
@@ -18,7 +20,6 @@
 #include "device/vr/public/mojom/vr_service.mojom.h"
 #include "device/vr/public/mojom/xr_session.mojom.h"
 #include "device/vr/util/hit_test_subscription_data.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -62,14 +63,14 @@
 
   ~ArCoreImpl() override;
 
-  absl::optional<ArCore::InitializeResult> Initialize(
+  std::optional<ArCore::InitializeResult> Initialize(
       base::android::ScopedJavaLocalRef<jobject> application_context,
       const std::unordered_set<device::mojom::XRSessionFeature>&
           required_features,
       const std::unordered_set<device::mojom::XRSessionFeature>&
           optional_features,
       const std::vector<device::mojom::XRTrackedImagePtr>& tracked_images,
-      absl::optional<ArCore::DepthSensingConfiguration> depth_sensing_config)
+      std::optional<ArCore::DepthSensingConfiguration> depth_sensing_config)
       override;
   MinMaxRange GetTargetFramerateRange() override;
   void SetDisplayGeometry(const gfx::Size& frame_size,
@@ -100,11 +101,11 @@
       const std::vector<mojom::EntityTypeForHitTest>& entity_types,
       std::vector<mojom::XRHitResultPtr>* hit_results);
 
-  absl::optional<uint64_t> SubscribeToHitTest(
+  std::optional<uint64_t> SubscribeToHitTest(
       mojom::XRNativeOriginInformationPtr nativeOriginInformation,
       const std::vector<mojom::EntityTypeForHitTest>& entity_types,
       mojom::XRRayPtr ray) override;
-  absl::optional<uint64_t> SubscribeToHitTestForTransientInput(
+  std::optional<uint64_t> SubscribeToHitTestForTransientInput(
       const std::string& profile_name,
       const std::vector<mojom::EntityTypeForHitTest>& entity_types,
       mojom::XRRayPtr ray) override;
@@ -183,7 +184,7 @@
   // list of images. The index values are needed for blink communication.
   std::unordered_map<int32_t, uint64_t> tracked_image_arcore_id_to_index_;
 
-  absl::optional<device::mojom::XRDepthConfig> depth_configuration_;
+  std::optional<device::mojom::XRDepthConfig> depth_configuration_;
 
   uint64_t next_id_ = 1;
 
@@ -233,8 +234,8 @@
 
   // Returns mojo_from_native_origin transform given native origin
   // information. If the transform cannot be found or is unknown, it will return
-  // absl::nullopt.
-  absl::optional<gfx::Transform> GetMojoFromNativeOrigin(
+  // std::nullopt.
+  std::optional<gfx::Transform> GetMojoFromNativeOrigin(
       const mojom::XRNativeOriginInformation& native_origin_information,
       const gfx::Transform& mojo_from_viewer,
       const std::vector<mojom::XRInputSourceStatePtr>& input_state);
@@ -242,8 +243,8 @@
   // Returns mojo_from_reference_space transform given reference space type.
   // Mojo_from_reference_space is equivalent to mojo_from_native_origin for
   // native origins that are reference spaces. If the transform cannot be found,
-  // it will return absl::nullopt.
-  absl::optional<gfx::Transform> GetMojoFromReferenceSpace(
+  // it will return std::nullopt.
+  std::optional<gfx::Transform> GetMojoFromReferenceSpace(
       device::mojom::XRReferenceSpaceType type,
       const gfx::Transform& mojo_from_viewer);
 
@@ -266,7 +267,7 @@
   // contain the requests that have not been processed.
   // |create_anchor_function| - function to call to actually create the anchor;
   // it will receive the specific anchor creation request, along with position
-  // and orientation for the anchor, and must return absl::optional<AnchorId>.
+  // and orientation for the anchor, and must return std::optional<AnchorId>.
   template <typename T, typename FunctionType>
   void ProcessAnchorCreationRequestsHelper(
       const gfx::Transform& mojo_from_viewer,
@@ -301,7 +302,7 @@
       const std::unordered_set<device::mojom::XRSessionFeature>&
           optional_features,
       const std::vector<device::mojom::XRTrackedImagePtr>& tracked_images,
-      const absl::optional<ArCore::DepthSensingConfiguration>&
+      const std::optional<ArCore::DepthSensingConfiguration>&
           depth_sensing_config,
       std::unordered_set<device::mojom::XRSessionFeature>& enabled_features);
 
@@ -310,7 +311,7 @@
   // supported combination of mode and data format. Affects
   // |depth_sensing_usage_| and |depth_sensing_data_format_| members.
   bool ConfigureDepthSensing(
-      const absl::optional<ArCore::DepthSensingConfiguration>&
+      const std::optional<ArCore::DepthSensingConfiguration>&
           depth_sensing_config);
 
   // Must be last.
diff --git a/device/vr/android/arcore/arcore_plane_manager.cc b/device/vr/android/arcore/arcore_plane_manager.cc
index 4525e1bd..7baae45 100644
--- a/device/vr/android/arcore/arcore_plane_manager.cc
+++ b/device/vr/android/arcore/arcore_plane_manager.cc
@@ -269,7 +269,7 @@
 
       updated_planes.push_back(mojom::XRPlaneData::New(
           plane_id.GetUnsafeValue(), device::mojom::XRPlaneOrientation::UNKNOWN,
-          absl::nullopt, std::vector<mojom::XRPlanePointDataPtr>{}));
+          std::nullopt, std::vector<mojom::XRPlanePointDataPtr>{}));
     }
   }
 
@@ -281,7 +281,7 @@
                                           std::move(updated_planes));
 }
 
-absl::optional<PlaneId> ArCorePlaneManager::GetPlaneId(
+std::optional<PlaneId> ArCorePlaneManager::GetPlaneId(
     void* plane_address) const {
   return plane_address_to_id_.GetId(plane_address);
 }
@@ -290,11 +290,11 @@
   return base::Contains(plane_id_to_plane_info_, id);
 }
 
-absl::optional<gfx::Transform> ArCorePlaneManager::GetMojoFromPlane(
+std::optional<gfx::Transform> ArCorePlaneManager::GetMojoFromPlane(
     PlaneId id) const {
   auto it = plane_id_to_plane_info_.find(id);
   if (it == plane_id_to_plane_info_.end()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   // Naked pointer is fine here, ArAsPlane does not increase the internal
diff --git a/device/vr/android/arcore/arcore_plane_manager.h b/device/vr/android/arcore/arcore_plane_manager.h
index 9f4a244c..5f7aaa25 100644
--- a/device/vr/android/arcore/arcore_plane_manager.h
+++ b/device/vr/android/arcore/arcore_plane_manager.h
@@ -6,6 +6,7 @@
 #define DEVICE_VR_ANDROID_ARCORE_ARCORE_PLANE_MANAGER_H_
 
 #include <map>
+#include <optional>
 
 #include "base/memory/raw_ptr.h"
 #include "base/types/id_type.h"
@@ -14,7 +15,6 @@
 #include "device/vr/android/arcore/arcore_sdk.h"
 #include "device/vr/android/arcore/scoped_arcore_objects.h"
 #include "device/vr/public/mojom/vr_service.mojom.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -50,11 +50,11 @@
 
   bool PlaneExists(PlaneId id) const;
 
-  // Returns absl::nullopt if plane with the given address does not exist.
-  absl::optional<PlaneId> GetPlaneId(void* plane_address) const;
+  // Returns std::nullopt if plane with the given address does not exist.
+  std::optional<PlaneId> GetPlaneId(void* plane_address) const;
 
-  // Returns absl::nullopt if plane with the given id does not exist.
-  absl::optional<gfx::Transform> GetMojoFromPlane(PlaneId id) const;
+  // Returns std::nullopt if plane with the given id does not exist.
+  std::optional<gfx::Transform> GetMojoFromPlane(PlaneId id) const;
 
   // Creates Anchor object given a plane ID. This is needed since Plane objects
   // are managed by this class in its entirety and are not accessible outside
diff --git a/device/vr/openxr/openxr_api_wrapper.h b/device/vr/openxr/openxr_api_wrapper.h
index e843ec3..5774fda 100644
--- a/device/vr/openxr/openxr_api_wrapper.h
+++ b/device/vr/openxr/openxr_api_wrapper.h
@@ -6,7 +6,9 @@
 #define DEVICE_VR_OPENXR_OPENXR_API_WRAPPER_H_
 
 #include <stdint.h>
+
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "base/functional/callback.h"
@@ -22,7 +24,6 @@
 #include "device/vr/public/mojom/vr_service.mojom.h"
 #include "device/vr/public/mojom/xr_session.mojom.h"
 #include "device/vr/vr_export.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/openxr/src/include/openxr/openxr.h"
 
 #if BUILDFLAG(IS_WIN)
diff --git a/device/vr/openxr/openxr_controller.cc b/device/vr/openxr/openxr_controller.cc
index ce4f24e..deda708 100644
--- a/device/vr/openxr/openxr_controller.cc
+++ b/device/vr/openxr/openxr_controller.cc
@@ -39,11 +39,10 @@
   return std::string("/user/hand/") + GetStringFromType(type);
 }
 
-absl::optional<gfx::Transform> GetOriginFromTarget(
-    XrTime predicted_display_time,
-    XrSpace origin,
-    XrSpace target,
-    bool* emulated_position) {
+std::optional<gfx::Transform> GetOriginFromTarget(XrTime predicted_display_time,
+                                                  XrSpace origin,
+                                                  XrSpace target,
+                                                  bool* emulated_position) {
   XrSpaceLocation location = {XR_TYPE_SPACE_LOCATION};
   // emulated_position indicates when there is a fallback from a fully-tracked
   // (i.e. 6DOF) type case to some form of orientation-only type tracking
@@ -56,7 +55,7 @@
           xrLocateSpace(target, origin, predicted_display_time, &location)) ||
       !(location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT) ||
       !(location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   *emulated_position = true;
@@ -82,13 +81,13 @@
   return gfx::Transform::Compose(decomp);
 }
 
-absl::optional<GamepadBuilder::ButtonData> GetAxisButtonData(
+std::optional<GamepadBuilder::ButtonData> GetAxisButtonData(
     OpenXrAxisType openxr_button_type,
-    absl::optional<GamepadButton> button_data,
+    std::optional<GamepadButton> button_data,
     std::vector<double> axis) {
   GamepadBuilder::ButtonData data;
   if (!button_data || axis.size() != 2) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   switch (openxr_button_type) {
@@ -363,7 +362,7 @@
              hand_tracker_->controller()->interaction_profile();
 }
 
-absl::optional<GamepadButton> OpenXrController::GetButton(
+std::optional<GamepadButton> OpenXrController::GetButton(
     OpenXrButtonType type) const {
   if (IsCurrentProfileFromHandTracker()) {
     return hand_tracker_->controller()->GetButton(type);
@@ -406,7 +405,7 @@
   }
 
   if (!has_value) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return ret;
@@ -422,7 +421,7 @@
   return {axis_state_v2f.currentState.x, axis_state_v2f.currentState.y};
 }
 
-absl::optional<Gamepad> OpenXrController::GetWebXRGamepad() const {
+std::optional<Gamepad> OpenXrController::GetWebXRGamepad() const {
   // We can return an XR-Standard gamepad as long as the following are true:
   // 1) It targets via a tracked-pointer
   // 2) It has a non-null grip space
@@ -433,69 +432,67 @@
   // tracking loss. We validate the other two requirements below before building
   // the gamepad.
   if (GetTargetRayMode() != mojom::XRTargetRayMode::POINTING) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
-  absl::optional<GamepadButton> trigger_button =
+  std::optional<GamepadButton> trigger_button =
       GetButton(OpenXrButtonType::kTrigger);
   if (!trigger_button) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   XRStandardGamepadBuilder builder(GetHandness());
   builder.SetPrimaryButton(trigger_button.value());
 
-  absl::optional<GamepadButton> squeeze_button =
+  std::optional<GamepadButton> squeeze_button =
       GetButton(OpenXrButtonType::kSqueeze);
   if (squeeze_button) {
     builder.SetSecondaryButton(squeeze_button.value());
   }
 
-  absl::optional<GamepadButton> trackpad_button =
+  std::optional<GamepadButton> trackpad_button =
       GetButton(OpenXrButtonType::kTrackpad);
   std::vector<double> trackpad_axis = GetAxis(OpenXrAxisType::kTrackpad);
-  absl::optional<GamepadBuilder::ButtonData> trackpad_button_data =
+  std::optional<GamepadBuilder::ButtonData> trackpad_button_data =
       GetAxisButtonData(OpenXrAxisType::kTrackpad, trackpad_button,
                         trackpad_axis);
   if (trackpad_button_data) {
     builder.SetTouchpadData(trackpad_button_data.value());
   }
 
-  absl::optional<GamepadButton> thumbstick_button =
+  std::optional<GamepadButton> thumbstick_button =
       GetButton(OpenXrButtonType::kThumbstick);
   std::vector<double> thumbstick_axis = GetAxis(OpenXrAxisType::kThumbstick);
-  absl::optional<GamepadBuilder::ButtonData> thumbstick_button_data =
+  std::optional<GamepadBuilder::ButtonData> thumbstick_button_data =
       GetAxisButtonData(OpenXrAxisType::kThumbstick, thumbstick_button,
                         thumbstick_axis);
   if (thumbstick_button_data) {
     builder.SetThumbstickData(thumbstick_button_data.value());
   }
 
-  absl::optional<GamepadButton> x_button =
-      GetButton(OpenXrButtonType::kButton1);
+  std::optional<GamepadButton> x_button = GetButton(OpenXrButtonType::kButton1);
   if (x_button) {
     builder.AddOptionalButtonData(x_button.value());
   }
 
-  absl::optional<GamepadButton> y_button =
-      GetButton(OpenXrButtonType::kButton2);
+  std::optional<GamepadButton> y_button = GetButton(OpenXrButtonType::kButton2);
   if (y_button) {
     builder.AddOptionalButtonData(y_button.value());
   }
 
-  absl::optional<GamepadButton> thumbrest_button =
+  std::optional<GamepadButton> thumbrest_button =
       GetButton(OpenXrButtonType::kThumbrest);
   if (thumbrest_button) {
     builder.AddOptionalButtonData(thumbrest_button.value());
   }
 
-  absl::optional<GamepadButton> grasp_button =
+  std::optional<GamepadButton> grasp_button =
       GetButton(OpenXrButtonType::kGrasp);
   if (grasp_button) {
     builder.AddOptionalButtonData(grasp_button.value());
   }
 
-  absl::optional<GamepadButton> shoulder_button =
+  std::optional<GamepadButton> shoulder_button =
       GetButton(OpenXrButtonType::kShoulder);
   if (shoulder_button) {
     builder.AddOptionalButtonData(shoulder_button.value());
@@ -546,7 +543,7 @@
   return hand_tracker_->GetHandTrackingData();
 }
 
-absl::optional<gfx::Transform> OpenXrController::GetMojoFromGripTransform(
+std::optional<gfx::Transform> OpenXrController::GetMojoFromGripTransform(
     XrTime predicted_display_time,
     XrSpace local_space,
     bool* emulated_position) const {
@@ -559,7 +556,7 @@
                              grip_pose_space_, emulated_position);
 }
 
-absl::optional<gfx::Transform> OpenXrController::GetGripFromPointerTransform(
+std::optional<gfx::Transform> OpenXrController::GetGripFromPointerTransform(
     XrTime predicted_display_time) const {
   if (IsCurrentProfileFromHandTracker()) {
     return hand_tracker_->controller()->GetGripFromPointerTransform();
diff --git a/device/vr/openxr/openxr_controller.h b/device/vr/openxr/openxr_controller.h
index dc1f4d88..5c8a4ef7 100644
--- a/device/vr/openxr/openxr_controller.h
+++ b/device/vr/openxr/openxr_controller.h
@@ -7,7 +7,9 @@
 
 #include <stdint.h>
 #include <string.h>
+
 #include <map>
+#include <optional>
 #include <unordered_map>
 #include <vector>
 
@@ -18,7 +20,6 @@
 #include "device/vr/openxr/openxr_path_helper.h"
 #include "device/vr/public/mojom/openxr_interaction_profile_type.mojom.h"
 #include "device/vr/public/mojom/vr_service.mojom.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/openxr/src/include/openxr/openxr.h"
 #include "ui/gfx/geometry/transform.h"
 
@@ -52,10 +53,10 @@
   mojom::XRInputSourceDescriptionPtr GetDescription(
       XrTime predicted_display_time);
 
-  absl::optional<GamepadButton> GetButton(OpenXrButtonType type) const;
-  absl::optional<Gamepad> GetWebXRGamepad() const;
+  std::optional<GamepadButton> GetButton(OpenXrButtonType type) const;
+  std::optional<Gamepad> GetWebXRGamepad() const;
 
-  absl::optional<gfx::Transform> GetMojoFromGripTransform(
+  std::optional<gfx::Transform> GetMojoFromGripTransform(
       XrTime predicted_display_time,
       XrSpace local_space,
       bool* emulated_position) const;
@@ -96,7 +97,7 @@
 
   bool IsCurrentProfileFromHandTracker() const;
 
-  absl::optional<gfx::Transform> GetGripFromPointerTransform(
+  std::optional<gfx::Transform> GetGripFromPointerTransform(
       XrTime predicted_display_time) const;
 
   mojom::XRTargetRayMode GetTargetRayMode() const;
diff --git a/device/vr/openxr/openxr_input_helper.cc b/device/vr/openxr/openxr_input_helper.cc
index 6d691ad..17dcc9c6 100644
--- a/device/vr/openxr/openxr_input_helper.cc
+++ b/device/vr/openxr/openxr_input_helper.cc
@@ -98,7 +98,7 @@
   for (uint32_t i = 0; i < controller_states_.size(); i++) {
     device::OpenXrController* controller = &controller_states_[i].controller;
 
-    absl::optional<GamepadButton> menu_button =
+    std::optional<GamepadButton> menu_button =
         controller->GetButton(OpenXrButtonType::kMenu);
 
     // Pressing a menu buttons is treated as a signal to exit the WebXR session.
@@ -106,9 +106,9 @@
       OnExitGesture();
     }
 
-    absl::optional<GamepadButton> primary_button =
+    std::optional<GamepadButton> primary_button =
         controller->GetButton(OpenXrButtonType::kTrigger);
-    absl::optional<GamepadButton> squeeze_button =
+    std::optional<GamepadButton> squeeze_button =
         controller->GetButton(OpenXrButtonType::kSqueeze);
 
     // Having a trigger button is the minimum for an webxr input.
diff --git a/device/vr/openxr/openxr_input_helper.h b/device/vr/openxr/openxr_input_helper.h
index 624f238..c4e910d 100644
--- a/device/vr/openxr/openxr_input_helper.h
+++ b/device/vr/openxr/openxr_input_helper.h
@@ -7,11 +7,11 @@
 
 #include <array>
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "device/vr/openxr/openxr_controller.h"
 #include "device/vr/openxr/openxr_interaction_profiles.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/openxr/src/include/openxr/openxr.h"
 
 namespace device {
diff --git a/device/vr/openxr/openxr_platform_helper.cc b/device/vr/openxr/openxr_platform_helper.cc
index f17b1e4..7d2b4a2 100644
--- a/device/vr/openxr/openxr_platform_helper.cc
+++ b/device/vr/openxr/openxr_platform_helper.cc
@@ -56,7 +56,7 @@
 }
 
 void OpenXrPlatformHelper::CreateInstanceWithCreateInfo(
-    absl::optional<OpenXrCreateInfo> create_info,
+    std::optional<OpenXrCreateInfo> create_info,
     CreateInstanceCallback instance_ready_callback,
     PlatormInitiatedShutdownCallback shutdown_callback) {
   DVLOG(1) << __func__;
diff --git a/device/vr/openxr/openxr_platform_helper.h b/device/vr/openxr/openxr_platform_helper.h
index 0256dd5..4791350 100644
--- a/device/vr/openxr/openxr_platform_helper.h
+++ b/device/vr/openxr/openxr_platform_helper.h
@@ -6,6 +6,7 @@
 #define DEVICE_VR_OPENXR_OPENXR_PLATFORM_HELPER_H_
 
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "base/functional/callback.h"
@@ -13,7 +14,6 @@
 #include "device/vr/openxr/openxr_extension_helper.h"
 #include "device/vr/public/mojom/isolated_xr_service.mojom-forward.h"
 #include "device/vr/vr_export.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_WIN)
 #include "device/vr/windows/d3d11_texture_helper.h"
@@ -104,7 +104,7 @@
   XrResult CreateInstance(XrInstance* instance);
 
   void CreateInstanceWithCreateInfo(
-      absl::optional<OpenXrCreateInfo> create_info,
+      std::optional<OpenXrCreateInfo> create_info,
       CreateInstanceCallback instance_ready_callback,
       PlatormInitiatedShutdownCallback shutdown_callback);
 
diff --git a/device/vr/openxr/openxr_render_loop.cc b/device/vr/openxr/openxr_render_loop.cc
index 07b3f492..5a7d0be 100644
--- a/device/vr/openxr/openxr_render_loop.cc
+++ b/device/vr/openxr/openxr_render_loop.cc
@@ -4,6 +4,8 @@
 
 #include "device/vr/openxr/openxr_render_loop.h"
 
+#include <optional>
+
 #include "base/containers/contains.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
@@ -21,7 +23,6 @@
 #include "gpu/command_buffer/client/context_support.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "mojo/public/cpp/bindings/message.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/openxr/src/include/openxr/openxr.h"
 #include "ui/gfx/geometry/angle_conversions.h"
 #include "ui/gfx/geometry/transform.h"
diff --git a/device/vr/openxr/openxr_render_loop.h b/device/vr/openxr/openxr_render_loop.h
index b77b0158..d412b7e 100644
--- a/device/vr/openxr/openxr_render_loop.h
+++ b/device/vr/openxr/openxr_render_loop.h
@@ -285,7 +285,7 @@
   SlidingTimeDeltaAverage webxr_js_time_;
   SlidingTimeDeltaAverage webxr_gpu_time_;
 
-  absl::optional<OutstandingFrame> pending_frame_;
+  std::optional<OutstandingFrame> pending_frame_;
 
   bool is_presenting_ = false;  // True if we have a presenting session.
   bool webxr_visible_ = true;   // The browser may hide a presenting session.
diff --git a/device/vr/openxr/openxr_util.cc b/device/vr/openxr/openxr_util.cc
index 47d9f42..4c072e5 100644
--- a/device/vr/openxr/openxr_util.cc
+++ b/device/vr/openxr/openxr_util.cc
@@ -38,7 +38,7 @@
 }
 
 XrPosef GfxTransformToXrPose(const gfx::Transform& transform) {
-  absl::optional<gfx::DecomposedTransform> decomposed_transform =
+  std::optional<gfx::DecomposedTransform> decomposed_transform =
       transform.Decompose();
   // This pose should always be a simple translation and rotation so this should
   // always be true
diff --git a/device/vr/openxr/test/openxr_test_helper.cc b/device/vr/openxr/test/openxr_test_helper.cc
index cd4cfe2..f977e2fe 100644
--- a/device/vr/openxr/test/openxr_test_helper.cc
+++ b/device/vr/openxr/test/openxr_test_helper.cc
@@ -911,7 +911,7 @@
   }
 }
 
-absl::optional<gfx::Transform> OpenXrTestHelper::GetPose() {
+std::optional<gfx::Transform> OpenXrTestHelper::GetPose() {
   base::AutoLock lock(lock_);
   if (test_hook_) {
     device::PoseFrameData pose_data = test_hook_->WaitGetPresentingPose();
@@ -919,15 +919,15 @@
       return PoseFrameDataToTransform(pose_data);
     }
   }
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-absl::optional<device::DeviceConfig> OpenXrTestHelper::GetDeviceConfig() {
+std::optional<device::DeviceConfig> OpenXrTestHelper::GetDeviceConfig() {
   base::AutoLock lock(lock_);
   if (test_hook_) {
     return test_hook_->WaitGetDeviceConfig();
   }
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 bool OpenXrTestHelper::GetCanCreateSession() {
@@ -1010,7 +1010,7 @@
 void OpenXrTestHelper::LocateSpace(XrSpace space, XrPosef* pose) {
   DCHECK_NE(pose, nullptr);
   *pose = device::PoseIdentity();
-  absl::optional<gfx::Transform> transform = absl::nullopt;
+  std::optional<gfx::Transform> transform = std::nullopt;
 
   if (reference_spaces_.count(space) == 1) {
     if (reference_spaces_.at(space).compare(kStageReferenceSpacePath) == 0) {
@@ -1042,7 +1042,7 @@
   }
 
   if (transform) {
-    absl::optional<gfx::DecomposedTransform> decomposed_transform =
+    std::optional<gfx::DecomposedTransform> decomposed_transform =
         transform->Decompose();
     DCHECK(decomposed_transform);
 
@@ -1082,8 +1082,8 @@
   RETURN_IF(size != 1 && size != 2, XR_ERROR_VALIDATION_FAILURE,
             "UpdateViews only supports view configurations with 1 or 2 views");
 
-  absl::optional<gfx::Transform> pose = GetPose();
-  absl::optional<device::DeviceConfig> config = GetDeviceConfig();
+  std::optional<gfx::Transform> pose = GetPose();
+  std::optional<device::DeviceConfig> config = GetDeviceConfig();
 
   if (!pose.has_value() && !config.has_value()) {
     return true;
diff --git a/device/vr/openxr/test/openxr_test_helper.h b/device/vr/openxr/test/openxr_test_helper.h
index fc751d84..08ca3660 100644
--- a/device/vr/openxr/test/openxr_test_helper.h
+++ b/device/vr/openxr/test/openxr_test_helper.h
@@ -6,6 +6,7 @@
 #define DEVICE_VR_OPENXR_TEST_OPENXR_TEST_HELPER_H_
 
 #include <array>
+#include <optional>
 #include <queue>
 #include <unordered_map>
 #include <unordered_set>
@@ -16,7 +17,6 @@
 #include "device/vr/openxr/openxr_platform.h"
 #include "device/vr/openxr/openxr_view_configuration.h"
 #include "device/vr/test/test_hook.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/openxr/src/include/openxr/openxr.h"
 
 #if BUILDFLAG(IS_WIN)
@@ -190,8 +190,8 @@
                      uint32_t& height) const;
   XrResult UpdateAction(XrAction action);
   void SetSessionState(XrSessionState state);
-  absl::optional<gfx::Transform> GetPose();
-  absl::optional<device::DeviceConfig> GetDeviceConfig();
+  std::optional<gfx::Transform> GetPose();
+  std::optional<device::DeviceConfig> GetDeviceConfig();
   device::ControllerFrameData GetControllerDataFromPath(
       std::string path_string) const;
   void UpdateInteractionProfile(
diff --git a/device/vr/orientation/orientation_device.h b/device/vr/orientation/orientation_device.h
index bc78450..7e3f1a4 100644
--- a/device/vr/orientation/orientation_device.h
+++ b/device/vr/orientation/orientation_device.h
@@ -76,7 +76,7 @@
   base::OnceClosure ready_callback_;
 
   // The initial state of the world used to define forwards.
-  absl::optional<gfx::Quaternion> base_pose_;
+  std::optional<gfx::Quaternion> base_pose_;
   gfx::Quaternion latest_pose_;
 
   mojo::Remote<mojom::Sensor> sensor_;
diff --git a/device/vr/public/cpp/xr_frame_sink_client.h b/device/vr/public/cpp/xr_frame_sink_client.h
index 5e70358..21197c8 100644
--- a/device/vr/public/cpp/xr_frame_sink_client.h
+++ b/device/vr/public/cpp/xr_frame_sink_client.h
@@ -6,10 +6,11 @@
 #define DEVICE_VR_PUBLIC_CPP_XR_FRAME_SINK_CLIENT_H_
 
 #include <memory>
+#include <optional>
+
 #include "base/component_export.h"
 #include "base/functional/callback_forward.h"
 #include "services/viz/privileged/mojom/compositing/frame_sink_manager.mojom-forward.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace viz {
 class FrameSinkId;
@@ -49,7 +50,7 @@
 
   // Used to get the SurfaceId of the DOM content to be rendered.
   // May be called from any thread.
-  virtual absl::optional<viz::SurfaceId> GetDOMSurface() = 0;
+  virtual std::optional<viz::SurfaceId> GetDOMSurface() = 0;
 
   virtual viz::FrameSinkId FrameSinkId() = 0;
 };
diff --git a/device/vr/public/mojom/pose.cc b/device/vr/public/mojom/pose.cc
index 0dedbbc0..898c320 100644
--- a/device/vr/public/mojom/pose.cc
+++ b/device/vr/public/mojom/pose.cc
@@ -22,11 +22,11 @@
   other_from_this_ = gfx::Transform::Compose(decomposed_pose);
 }
 
-absl::optional<Pose> Pose::Create(const gfx::Transform& other_from_this) {
-  absl::optional<gfx::DecomposedTransform> decomposed_other_from_this =
+std::optional<Pose> Pose::Create(const gfx::Transform& other_from_this) {
+  std::optional<gfx::DecomposedTransform> decomposed_other_from_this =
       other_from_this.Decompose();
   if (!decomposed_other_from_this)
-    return absl::nullopt;
+    return std::nullopt;
 
   return Pose(gfx::Point3F(decomposed_other_from_this->translate[0],
                            decomposed_other_from_this->translate[1],
diff --git a/device/vr/public/mojom/pose.h b/device/vr/public/mojom/pose.h
index e88de36a..ac9801c 100644
--- a/device/vr/public/mojom/pose.h
+++ b/device/vr/public/mojom/pose.h
@@ -5,8 +5,9 @@
 #ifndef DEVICE_VR_PUBLIC_MOJOM_POSE_H_
 #define DEVICE_VR_PUBLIC_MOJOM_POSE_H_
 
+#include <optional>
+
 #include "base/component_export.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/geometry/point3_f.h"
 #include "ui/gfx/geometry/quaternion.h"
 #include "ui/gfx/geometry/transform.h"
@@ -40,8 +41,8 @@
   // the passed in matrix represents a rigid transformation (i.e. only the
   // orientation and translation components of the decomposed matrix will affect
   // the result). If the matrix could not be decomposed, the method will return
-  // a absl::nullopt.
-  static absl::optional<Pose> Create(const gfx::Transform& other_from_this);
+  // a std::nullopt.
+  static std::optional<Pose> Create(const gfx::Transform& other_from_this);
 
   const gfx::Point3F& position() const { return position_; }
 
diff --git a/device/vr/util/gamepad_builder.cc b/device/vr/util/gamepad_builder.cc
index 8874ef0a..e653c94 100644
--- a/device/vr/util/gamepad_builder.cc
+++ b/device/vr/util/gamepad_builder.cc
@@ -56,11 +56,11 @@
   NOTREACHED();
 }
 
-absl::optional<Gamepad> GamepadBuilder::GetGamepad() {
+std::optional<Gamepad> GamepadBuilder::GetGamepad() {
   if (IsValid())
     return gamepad_;
 
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 void GamepadBuilder::AddButton(const GamepadButton& button) {
diff --git a/device/vr/util/gamepad_builder.h b/device/vr/util/gamepad_builder.h
index e71510d2..f848fdb 100644
--- a/device/vr/util/gamepad_builder.h
+++ b/device/vr/util/gamepad_builder.h
@@ -4,11 +4,11 @@
 #ifndef DEVICE_VR_UTIL_GAMEPAD_BUILDER_H_
 #define DEVICE_VR_UTIL_GAMEPAD_BUILDER_H_
 
+#include <optional>
 #include <string>
 
 #include "device/gamepad/public/cpp/gamepads.h"
 #include "device/vr/public/mojom/isolated_xr_service.mojom.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -38,7 +38,7 @@
   virtual ~GamepadBuilder();
 
   virtual bool IsValid() const;
-  virtual absl::optional<Gamepad> GetGamepad();
+  virtual std::optional<Gamepad> GetGamepad();
 
   void AddButton(const GamepadButton& button);
   void AddButton(const ButtonData& data);
diff --git a/device/vr/util/xr_standard_gamepad_builder.cc b/device/vr/util/xr_standard_gamepad_builder.cc
index b2225e9..10af0c4 100644
--- a/device/vr/util/xr_standard_gamepad_builder.cc
+++ b/device/vr/util/xr_standard_gamepad_builder.cc
@@ -28,9 +28,9 @@
   AddOptionalButtonData(data);
 }
 
-absl::optional<Gamepad> XRStandardGamepadBuilder::GetGamepad() const {
+std::optional<Gamepad> XRStandardGamepadBuilder::GetGamepad() const {
   if (!primary_button_) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   GamepadBuilder builder("", GamepadMapping::kXrStandard, handedness_);
diff --git a/device/vr/util/xr_standard_gamepad_builder.h b/device/vr/util/xr_standard_gamepad_builder.h
index 772d4236..5f346031 100644
--- a/device/vr/util/xr_standard_gamepad_builder.h
+++ b/device/vr/util/xr_standard_gamepad_builder.h
@@ -4,10 +4,11 @@
 #ifndef DEVICE_VR_UTIL_XR_STANDARD_GAMEPAD_BUILDER_H_
 #define DEVICE_VR_UTIL_XR_STANDARD_GAMEPAD_BUILDER_H_
 
+#include <optional>
+
 #include "base/component_export.h"
 #include "device/vr/public/mojom/isolated_xr_service.mojom.h"
 #include "device/vr/util/gamepad_builder.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device {
 
@@ -43,18 +44,18 @@
   void AddOptionalButtonData(const GamepadBuilder::ButtonData& data);
   void AddOptionalButtonData(const GamepadButton& button);
 
-  absl::optional<Gamepad> GetGamepad() const;
+  std::optional<Gamepad> GetGamepad() const;
 
   bool HasSecondaryButton() const { return !!secondary_button_; }
   bool HasTouchpad() const { return !!touchpad_data_; }
   bool HasThumbstick() const { return !!thumbstick_data_; }
 
  private:
-  absl::optional<GamepadButton> primary_button_;
-  absl::optional<GamepadButton> secondary_button_;
+  std::optional<GamepadButton> primary_button_;
+  std::optional<GamepadButton> secondary_button_;
 
-  absl::optional<GamepadBuilder::ButtonData> touchpad_data_;
-  absl::optional<GamepadBuilder::ButtonData> thumbstick_data_;
+  std::optional<GamepadBuilder::ButtonData> touchpad_data_;
+  std::optional<GamepadBuilder::ButtonData> thumbstick_data_;
 
   std::vector<GamepadBuilder::ButtonData> optional_button_data_;
 
diff --git a/docs/testing/images/web-tests/wptrunner-paused.jpg b/docs/testing/images/web-tests/wptrunner-paused.jpg
deleted file mode 100644
index 349970bd..0000000
--- a/docs/testing/images/web-tests/wptrunner-paused.jpg
+++ /dev/null
Binary files differ
diff --git a/docs/testing/run_web_platform_tests.md b/docs/testing/run_web_platform_tests.md
index 14f33a8..e449caf 100644
--- a/docs/testing/run_web_platform_tests.md
+++ b/docs/testing/run_web_platform_tests.md
@@ -88,16 +88,6 @@
 
 ## Debugging Support
 
-### Headful Mode
-
-Passing the `--no-headless` flag to `run_wpt_tests.py` will pause execution
-after running each test headfully.
-You can interact with the paused test page afterwards, including with DevTools:
-
-![Testharness paused](images/web-tests/wptrunner-paused.jpg)
-
-Closing the tab or window will unpause the testharness and run the next test.
-
 ### Text-Based Debuggers
 
 To interactively debug WPTs, prefix the `run_wpt_tests.py` command with
diff --git a/extensions/browser/extension_web_contents_observer.cc b/extensions/browser/extension_web_contents_observer.cc
index b0c36a6d..31b199b 100644
--- a/extensions/browser/extension_web_contents_observer.cc
+++ b/extensions/browser/extension_web_contents_observer.cc
@@ -83,18 +83,18 @@
 
   extension_frame_host_ = CreateExtensionFrameHost(web_contents());
 
+  content::RenderFrameHost* main_frame = web_contents()->GetPrimaryMainFrame();
+  // We only initialize the frame if the renderer counterpart is live;
+  // otherwise we wait for the RenderFrameCreated notification.
+  if (main_frame->IsRenderFrameLive()) {
+    InitializeRenderFrame(main_frame);
+  }
+
+  // At the point of initialization, the *only* frame that can exist is the
+  // main frame.
   web_contents()->ForEachRenderFrameHost(
-      [this](content::RenderFrameHost* render_frame_host) {
-        // ForEachRenderFrameHost descends into inner WebContents, so make sure
-        // the RenderFrameHost is actually one bound to this object.
-        if (content::WebContents::FromRenderFrameHost(render_frame_host) !=
-            web_contents()) {
-          return;
-        }
-        // We only initialize the frame if the renderer counterpart is live;
-        // otherwise we wait for the RenderFrameCreated notification.
-        if (render_frame_host->IsRenderFrameLive())
-          InitializeRenderFrame(render_frame_host);
+      [main_frame](content::RenderFrameHost* render_frame_host) {
+        CHECK_EQ(render_frame_host, main_frame);
       });
 
   // It would be ideal if SessionTabHelper was created before this object,
diff --git a/extensions/common/api/declarative_net_request.idl b/extensions/common/api/declarative_net_request.idl
index c31108a..34a6010 100644
--- a/extensions/common/api/declarative_net_request.idl
+++ b/extensions/common/api/declarative_net_request.idl
@@ -730,8 +730,8 @@
     // Returns all rules matched for the extension. Callers can optionally
     // filter the list of matched rules by specifying a <code>filter</code>.
     // This method is only available to extensions with the
-    // <code>declarativeNetRequestFeedback</code> permission or having the
-    // <code>activeTab</code> permission granted for the <code>tabId</code>
+    // <code>"declarativeNetRequestFeedback"</code> permission or having the
+    // <code>"activeTab"</code> permission granted for the <code>tabId</code>
     // specified in <code>filter</code>.
     // Note: Rules not associated with an active document that were matched more
     // than five minutes ago will not be returned.
@@ -833,7 +833,7 @@
 
   interface Events {
     // Fired when a rule is matched with a request. Only available for unpacked
-    // extensions with the <code>declarativeNetRequestFeedback</code> permission
+    // extensions with the <code>"declarativeNetRequestFeedback"</code> permission
     // as this is intended to be used for debugging purposes only.
     // |info|: The rule that has been matched along with information about the
     // associated request.
diff --git a/extensions/common/api/runtime.json b/extensions/common/api/runtime.json
index 5c53659..bbd9d08 100644
--- a/extensions/common/api/runtime.json
+++ b/extensions/common/api/runtime.json
@@ -456,7 +456,7 @@
         "name": "connectNative",
         "type": "function",
         "nocompile": true,
-        "description": "Connects to a native application in the host machine. See <a href=\"develop/concepts/native-messaging\">Native Messaging</a> for more information.",
+        "description": "Connects to a native application in the host machine. This method requires the <code>\"nativeMessaging\"</code> permission. See <a href=\"develop/concepts/native-messaging\">Native Messaging</a> for more information.",
         "parameters": [
           {
             "type": "string",
@@ -503,7 +503,7 @@
         "name": "sendNativeMessage",
         "type": "function",
         "nocompile": true,
-        "description": "Send a single message to a native application.",
+        "description": "Send a single message to a native application. This method requires the <code>\"nativeMessaging\"</code> permission.",
         "parameters": [
           {
             "name": "application",
@@ -698,7 +698,7 @@
         "name": "onConnectNative",
         "type": "function",
         "nocompile": true,
-        "description": "Fired when a connection is made from a native application. Currently only supported on Chrome OS.",
+        "description": "Fired when a connection is made from a native application. This event requires the <code>\"nativeMessaging\"</code> permission. It is only supported on Chrome OS.",
         "parameters": [
           {"$ref": "Port", "name": "port"}
         ]
diff --git a/fuchsia_web/webengine/browser/request_monitoring_browsertest.cc b/fuchsia_web/webengine/browser/request_monitoring_browsertest.cc
index 609c85cf..9d4d0635 100644
--- a/fuchsia_web/webengine/browser/request_monitoring_browsertest.cc
+++ b/fuchsia_web/webengine/browser/request_monitoring_browsertest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <string>
+
 #include "base/task/sequenced_task_runner.h"
 #include "content/public/test/browser_test.h"
 #include "fuchsia_web/common/string_util.h"
@@ -21,6 +23,13 @@
 constexpr char kPage1Title[] = "title 1";
 constexpr char kPage3ImgPath[] = "/img.png";
 constexpr char kSetHeaderRequestPath[] = "/set_header_request.html";
+constexpr char kWebWorkerPostLoadedPath[] = "/post_loaded.js";
+constexpr char kWebWorkerImportScriptPath[] = "/import_script.js";
+constexpr char kNestedWebWorkerPath[] = "/nested_worker.js";
+constexpr char kSharedWorkerPostLoadedPath[] = "/shared_post_loaded.js";
+constexpr char kPageWithWebWorkerPath[] = "/web_worker.html?worker_url=";
+constexpr char kPageWithSharedWorkerPath[] = "/shared_worker.html?worker_url=";
+constexpr char kWorkerLoadedMessage[] = "loaded";
 
 class RequestMonitoringTest : public FrameImplTestBase {
  public:
@@ -246,6 +255,172 @@
   }
 }
 
+// Tests that URLRequestRewrite applies to worker scripts loaded by the page.
+IN_PROC_BROWSER_TEST_F(RequestMonitoringTest,
+                       UrlRequestRewriteAddHeadersForWebWorkers) {
+  auto frame = FrameForTest::Create(context(), {});
+
+  std::vector<fuchsia::web::UrlRequestRewrite> rewrites;
+  rewrites.push_back(CreateRewriteAddHeaders("Test", "Value"));
+  fuchsia::web::UrlRequestRewriteRule rule;
+  rule.set_rewrites(std::move(rewrites));
+  std::vector<fuchsia::web::UrlRequestRewriteRule> rules;
+  rules.push_back(std::move(rule));
+  frame->SetUrlRequestRewriteRules(std::move(rules), []() {});
+
+  // Navigate, we should get the additional header on the main request and the
+  // image request.
+  const GURL page_url(
+      embedded_test_server()->GetURL(std::string(kPageWithWebWorkerPath) +
+                                     std::string(kWebWorkerPostLoadedPath)));
+  const GURL worker_url(
+      embedded_test_server()->GetURL(kWebWorkerPostLoadedPath));
+
+  EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
+                                       fuchsia::web::LoadUrlParams(),
+                                       page_url.spec()));
+  frame.navigation_listener().RunUntilTitleEquals(kWorkerLoadedMessage);
+
+  {
+    const auto iter = accumulated_requests_.find(page_url);
+    ASSERT_NE(iter, accumulated_requests_.end());
+    EXPECT_THAT(iter->second.headers, testing::Contains(testing::Key("Test")));
+  }
+  {
+    const auto iter = accumulated_requests_.find(worker_url);
+    ASSERT_NE(iter, accumulated_requests_.end());
+    EXPECT_THAT(iter->second.headers, testing::Contains(testing::Key("Test")));
+  }
+}
+
+// Tests that URLRequestRewrite applies to scripts imported by workers on the
+// page.
+IN_PROC_BROWSER_TEST_F(RequestMonitoringTest,
+                       UrlRequestRewriteAddHeadersForWebWorkersImportScripts) {
+  auto frame = FrameForTest::Create(context(), {});
+
+  std::vector<fuchsia::web::UrlRequestRewrite> rewrites;
+  rewrites.push_back(CreateRewriteAddHeaders("Test", "Value"));
+  fuchsia::web::UrlRequestRewriteRule rule;
+  rule.set_rewrites(std::move(rewrites));
+  std::vector<fuchsia::web::UrlRequestRewriteRule> rules;
+  rules.push_back(std::move(rule));
+  frame->SetUrlRequestRewriteRules(std::move(rules), []() {});
+
+  // Navigate, we should get the additional header on the main request and the
+  // image request.
+  const GURL page_url(
+      embedded_test_server()->GetURL(std::string(kPageWithWebWorkerPath) +
+                                     std::string(kWebWorkerImportScriptPath)));
+  const GURL worker_url(
+      embedded_test_server()->GetURL(kWebWorkerImportScriptPath));
+  const GURL imported_url(
+      embedded_test_server()->GetURL(kWebWorkerPostLoadedPath));
+
+  EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
+                                       fuchsia::web::LoadUrlParams(),
+                                       page_url.spec()));
+  frame.navigation_listener().RunUntilTitleEquals(kWorkerLoadedMessage);
+
+  {
+    const auto iter = accumulated_requests_.find(page_url);
+    ASSERT_NE(iter, accumulated_requests_.end());
+    EXPECT_THAT(iter->second.headers, testing::Contains(testing::Key("Test")));
+  }
+  {
+    const auto iter = accumulated_requests_.find(worker_url);
+    ASSERT_NE(iter, accumulated_requests_.end());
+    EXPECT_THAT(iter->second.headers, testing::Contains(testing::Key("Test")));
+  }
+  {
+    const auto iter = accumulated_requests_.find(imported_url);
+    ASSERT_NE(iter, accumulated_requests_.end());
+    EXPECT_THAT(iter->second.headers, testing::Contains(testing::Key("Test")));
+  }
+}
+
+// Tests that URLRequestRewrite applies to nested workers on the page.
+IN_PROC_BROWSER_TEST_F(RequestMonitoringTest,
+                       UrlRequestRewriteAddHeadersForNestedWebWorkers) {
+  auto frame = FrameForTest::Create(context(), {});
+
+  std::vector<fuchsia::web::UrlRequestRewrite> rewrites;
+  rewrites.push_back(CreateRewriteAddHeaders("Test", "Value"));
+  fuchsia::web::UrlRequestRewriteRule rule;
+  rule.set_rewrites(std::move(rewrites));
+  std::vector<fuchsia::web::UrlRequestRewriteRule> rules;
+  rules.push_back(std::move(rule));
+  frame->SetUrlRequestRewriteRules(std::move(rules), []() {});
+
+  // Navigate, we should get the additional header on the main request and the
+  // image request.
+  const GURL page_url(embedded_test_server()->GetURL(
+      std::string(kPageWithWebWorkerPath) + std::string(kNestedWebWorkerPath)));
+  const GURL worker_url(embedded_test_server()->GetURL(kNestedWebWorkerPath));
+  const GURL nested_url(
+      embedded_test_server()->GetURL(kWebWorkerPostLoadedPath));
+
+  EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
+                                       fuchsia::web::LoadUrlParams(),
+                                       page_url.spec()));
+  frame.navigation_listener().RunUntilTitleEquals(kWorkerLoadedMessage);
+
+  {
+    const auto iter = accumulated_requests_.find(page_url);
+    ASSERT_NE(iter, accumulated_requests_.end());
+    EXPECT_THAT(iter->second.headers, testing::Contains(testing::Key("Test")));
+  }
+  {
+    const auto iter = accumulated_requests_.find(worker_url);
+    ASSERT_NE(iter, accumulated_requests_.end());
+    EXPECT_THAT(iter->second.headers, testing::Contains(testing::Key("Test")));
+  }
+  {
+    const auto iter = accumulated_requests_.find(nested_url);
+    ASSERT_NE(iter, accumulated_requests_.end());
+    EXPECT_THAT(iter->second.headers, testing::Contains(testing::Key("Test")));
+  }
+}
+
+// Tests that URLRequestRewrite does not apply to shared workers on the page.
+IN_PROC_BROWSER_TEST_F(RequestMonitoringTest,
+                       UrlRequestRewriteDoesNotAddHeadersForSharedWorkers) {
+  auto frame = FrameForTest::Create(context(), {});
+
+  std::vector<fuchsia::web::UrlRequestRewrite> rewrites;
+  rewrites.push_back(CreateRewriteAddHeaders("Test", "Value"));
+  fuchsia::web::UrlRequestRewriteRule rule;
+  rule.set_rewrites(std::move(rewrites));
+  std::vector<fuchsia::web::UrlRequestRewriteRule> rules;
+  rules.push_back(std::move(rule));
+  frame->SetUrlRequestRewriteRules(std::move(rules), []() {});
+
+  // Navigate, we should get the additional header on the main request and the
+  // image request.
+  const GURL page_url(
+      embedded_test_server()->GetURL(std::string(kPageWithSharedWorkerPath) +
+                                     std::string(kSharedWorkerPostLoadedPath)));
+  const GURL worker_url(
+      embedded_test_server()->GetURL(kSharedWorkerPostLoadedPath));
+
+  EXPECT_TRUE(LoadUrlAndExpectResponse(frame.GetNavigationController(),
+                                       fuchsia::web::LoadUrlParams(),
+                                       page_url.spec()));
+  frame.navigation_listener().RunUntilTitleEquals(kWorkerLoadedMessage);
+
+  {
+    const auto iter = accumulated_requests_.find(page_url);
+    ASSERT_NE(iter, accumulated_requests_.end());
+    EXPECT_THAT(iter->second.headers, testing::Contains(testing::Key("Test")));
+  }
+  {
+    const auto iter = accumulated_requests_.find(worker_url);
+    ASSERT_NE(iter, accumulated_requests_.end());
+    EXPECT_THAT(iter->second.headers,
+                testing::Not(testing::Contains(testing::Key("Test"))));
+  }
+}
+
 // Tests the URLRequestRewrite API properly adds headers on every requests.
 IN_PROC_BROWSER_TEST_F(RequestMonitoringTest,
                        UrlRequestRewriteAddExistingHeader) {
diff --git a/fuchsia_web/webengine/browser/web_engine_content_browser_client.cc b/fuchsia_web/webengine/browser/web_engine_content_browser_client.cc
index 32ce71e..b1ca06b4 100644
--- a/fuchsia_web/webengine/browser/web_engine_content_browser_client.cc
+++ b/fuchsia_web/webengine/browser/web_engine_content_browser_client.cc
@@ -341,7 +341,7 @@
     int frame_tree_node_id,
     absl::optional<int64_t> navigation_id) {
   if (frame_tree_node_id == content::RenderFrameHost::kNoFrameTreeNodeId) {
-    // TODO(crbug.com/1378791): Add support for workers.
+    // TODO(crbug.com/1378791): Add support for Shared and Service Workers.
     return {};
   }
 
diff --git a/fuchsia_web/webengine/renderer/web_engine_content_renderer_client.cc b/fuchsia_web/webengine/renderer/web_engine_content_renderer_client.cc
index fb4f2ab..c55b35f 100644
--- a/fuchsia_web/webengine/renderer/web_engine_content_renderer_client.cc
+++ b/fuchsia_web/webengine/renderer/web_engine_content_renderer_client.cc
@@ -9,6 +9,8 @@
 #include <optional>
 #include "base/command_line.h"
 #include "base/feature_list.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/synchronization/lock.h"
 #include "build/chromecast_buildflags.h"
 #include "components/media_control/renderer/media_playback_options.h"
 #include "components/memory_pressure/multi_source_memory_pressure_monitor.h"
@@ -27,6 +29,7 @@
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
+#include "third_party/blink/public/platform/url_loader_throttle_provider.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 #include "third_party/blink/public/web/web_view.h"
 #include "third_party/widevine/cdm/buildflags.h"
@@ -133,18 +136,25 @@
 
 WebEngineContentRendererClient::WebEngineContentRendererClient() = default;
 
-WebEngineContentRendererClient::~WebEngineContentRendererClient() = default;
+WebEngineContentRendererClient::~WebEngineContentRendererClient() {
+  base::AutoLock lock(observer_map_lock_);
+  frame_token_to_observer_map_.clear();
+}
 
-WebEngineRenderFrameObserver*
-WebEngineContentRendererClient::GetWebEngineRenderFrameObserverForFrameToken(
+scoped_refptr<url_rewrite::UrlRequestRewriteRules>
+WebEngineContentRendererClient::GetRewriteRulesForFrameToken(
     const blink::LocalFrameToken& frame_token) const {
+  base::AutoLock lock(observer_map_lock_);
   auto iter = frame_token_to_observer_map_.find(frame_token);
-  DCHECK(iter != frame_token_to_observer_map_.end());
-  return iter->second.get();
+  if (iter == frame_token_to_observer_map_.end()) {
+    return nullptr;
+  }
+  return iter->second->url_request_rules_receiver()->GetCachedRules();
 }
 
 void WebEngineContentRendererClient::OnRenderFrameDeleted(
     const blink::LocalFrameToken& frame_token) {
+  base::AutoLock lock(observer_map_lock_);
   size_t count = frame_token_to_observer_map_.erase(frame_token);
   DCHECK_EQ(count, 1u);
 }
@@ -173,9 +183,12 @@
       render_frame,
       base::BindOnce(&WebEngineContentRendererClient::OnRenderFrameDeleted,
                      base::Unretained(this)));
-  auto render_frame_observer_iter = frame_token_to_observer_map_.emplace(
-      frame_token, std::move(render_frame_observer));
-  DCHECK(render_frame_observer_iter.second);
+  {
+    base::AutoLock lock(observer_map_lock_);
+    auto render_frame_observer_iter = frame_token_to_observer_map_.emplace(
+        frame_token, std::move(render_frame_observer));
+    DCHECK(render_frame_observer_iter.second);
+  }
 
   // Lifetime is tied to |render_frame| via content::RenderFrameObserver.
   new media_control::MediaPlaybackOptions(render_frame);
@@ -184,10 +197,6 @@
 std::unique_ptr<blink::URLLoaderThrottleProvider>
 WebEngineContentRendererClient::CreateURLLoaderThrottleProvider(
     blink::URLLoaderThrottleProviderType type) {
-  // TODO(crbug.com/1378791): Add support for workers.
-  if (type == blink::URLLoaderThrottleProviderType::kWorker)
-    return nullptr;
-
   return std::make_unique<WebEngineURLLoaderThrottleProvider>(this);
 }
 
diff --git a/fuchsia_web/webengine/renderer/web_engine_content_renderer_client.h b/fuchsia_web/webengine/renderer/web_engine_content_renderer_client.h
index 384d8eb..c68656b 100644
--- a/fuchsia_web/webengine/renderer/web_engine_content_renderer_client.h
+++ b/fuchsia_web/webengine/renderer/web_engine_content_renderer_client.h
@@ -7,11 +7,16 @@
 
 #include <memory>
 
+#include "base/memory/scoped_refptr.h"
+#include "base/synchronization/lock.h"
+#include "base/thread_annotations.h"
 #include "build/chromecast_buildflags.h"
+#include "components/url_rewrite/common/url_request_rewrite_rules.h"
 #include "content/public/renderer/content_renderer_client.h"
 #include "fuchsia_web/webengine/renderer/web_engine_audio_device_factory.h"
 #include "fuchsia_web/webengine/renderer/web_engine_render_frame_observer.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
+#include "third_party/blink/public/platform/url_loader_throttle_provider.h"
 
 #if BUILDFLAG(ENABLE_CAST_RECEIVER)
 namespace cast_streaming {
@@ -34,10 +39,9 @@
 
   ~WebEngineContentRendererClient() override;
 
-  // Returns the WebEngineRenderFrameObserver corresponding to
-  // `frame_token`.
-  WebEngineRenderFrameObserver* GetWebEngineRenderFrameObserverForFrameToken(
-      const blink::LocalFrameToken& frame_token) const;
+  // Returns the UrlRequestRewriteRules corresponding to `frame_token`.
+  scoped_refptr<url_rewrite::UrlRequestRewriteRules>
+  GetRewriteRulesForFrameToken(const blink::LocalFrameToken& frame_token) const;
 
  private:
   // Called by WebEngineRenderFrameObserver when its corresponding RenderFrame
@@ -74,10 +78,12 @@
   // use the AudioConsumer service directly.
   WebEngineAudioDeviceFactory audio_device_factory_;
 
+  mutable base::Lock observer_map_lock_;
+
   // Map of `blink::LocalFrameToken` to WebEngineRenderFrameObserver.
   std::map<blink::LocalFrameToken,
            std::unique_ptr<WebEngineRenderFrameObserver>>
-      frame_token_to_observer_map_;
+      frame_token_to_observer_map_ GUARDED_BY(observer_map_lock_);
 
   // Initiates cache purges and Blink/V8 garbage collection when free memory
   // is limited.
diff --git a/fuchsia_web/webengine/renderer/web_engine_url_loader_throttle_provider.cc b/fuchsia_web/webengine/renderer/web_engine_url_loader_throttle_provider.cc
index a301c21..05f906f 100644
--- a/fuchsia_web/webengine/renderer/web_engine_url_loader_throttle_provider.cc
+++ b/fuchsia_web/webengine/renderer/web_engine_url_loader_throttle_provider.cc
@@ -4,15 +4,21 @@
 
 #include "fuchsia_web/webengine/renderer/web_engine_url_loader_throttle_provider.h"
 
+#include <functional>
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/no_destructor.h"
 #include "components/url_rewrite/common/url_loader_throttle.h"
 #include "components/url_rewrite/mojom/url_request_rewrite.mojom.h"
 #include "content/public/renderer/render_frame.h"
 #include "fuchsia_web/webengine/common/cors_exempt_headers.h"
 #include "fuchsia_web/webengine/renderer/web_engine_content_renderer_client.h"
-#include "third_party/blink/public/web/web_local_frame.h"
+#include "net/base/net_errors.h"
+#include "third_party/blink/public/common/loader/url_loader_throttle.h"
 
 WebEngineURLLoaderThrottleProvider::WebEngineURLLoaderThrottleProvider(
-    WebEngineContentRendererClient* content_renderer_client)
+    const WebEngineContentRendererClient* const content_renderer_client)
     : content_renderer_client_(content_renderer_client) {
   DETACH_FROM_SEQUENCE(sequence_checker_);
 }
@@ -23,9 +29,12 @@
 
 std::unique_ptr<blink::URLLoaderThrottleProvider>
 WebEngineURLLoaderThrottleProvider::Clone() {
-  // This should only happen for workers, which we do not support here.
-  NOTREACHED();
-  return nullptr;
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  std::unique_ptr<WebEngineURLLoaderThrottleProvider> cloned_provider =
+      std::make_unique<WebEngineURLLoaderThrottleProvider>(
+          content_renderer_client_);
+  DETACH_FROM_SEQUENCE(cloned_provider->sequence_checker_);
+  return cloned_provider;
 }
 
 blink::WebVector<std::unique_ptr<blink::URLLoaderThrottle>>
@@ -33,18 +42,16 @@
     base::optional_ref<const blink::LocalFrameToken> local_frame_token,
     const network::ResourceRequest& request) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  CHECK(local_frame_token.has_value());
-  blink::WebVector<std::unique_ptr<blink::URLLoaderThrottle>> throttles;
-  blink::WebLocalFrame* web_frame =
-      blink::WebLocalFrame::FromFrameToken(local_frame_token.value());
-  if (!web_frame) {
-    return throttles;
+
+  if (!local_frame_token.has_value()) {
+    // `local_frame_token` is only set for Dedicated Workers. We do not support
+    // other types of workers.
+    return {};
   }
-  auto rules = content_renderer_client_
-                   ->GetWebEngineRenderFrameObserverForFrameToken(
-                       local_frame_token.value())
-                   ->url_request_rules_receiver()
-                   ->GetCachedRules();
+
+  blink::WebVector<std::unique_ptr<blink::URLLoaderThrottle>> throttles;
+  auto rules = content_renderer_client_->GetRewriteRulesForFrameToken(
+      local_frame_token.value());
   if (rules) {
     throttles.emplace_back(std::make_unique<url_rewrite::URLLoaderThrottle>(
         rules, base::BindRepeating(&IsHeaderCorsExempt)));
diff --git a/fuchsia_web/webengine/renderer/web_engine_url_loader_throttle_provider.h b/fuchsia_web/webengine/renderer/web_engine_url_loader_throttle_provider.h
index 47991e4..12378a4 100644
--- a/fuchsia_web/webengine/renderer/web_engine_url_loader_throttle_provider.h
+++ b/fuchsia_web/webengine/renderer/web_engine_url_loader_throttle_provider.h
@@ -17,7 +17,7 @@
     : public blink::URLLoaderThrottleProvider {
  public:
   explicit WebEngineURLLoaderThrottleProvider(
-      WebEngineContentRendererClient* content_renderer_client);
+      const WebEngineContentRendererClient* const content_renderer_client);
 
   WebEngineURLLoaderThrottleProvider(
       const WebEngineURLLoaderThrottleProvider&) = delete;
diff --git a/fuchsia_web/webengine/test/data/import_script.js b/fuchsia_web/webengine/test/data/import_script.js
new file mode 100644
index 0000000..26cb350
--- /dev/null
+++ b/fuchsia_web/webengine/test/data/import_script.js
@@ -0,0 +1,5 @@
+// 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.
+
+self.importScripts("post_loaded.js");
diff --git a/fuchsia_web/webengine/test/data/nested_worker.js b/fuchsia_web/webengine/test/data/nested_worker.js
new file mode 100644
index 0000000..350ee8c
--- /dev/null
+++ b/fuchsia_web/webengine/test/data/nested_worker.js
@@ -0,0 +1,8 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+const worker = new Worker('post_loaded.js');
+worker.addEventListener("message", (event) => {
+    self.postMessage(event.data);
+});
diff --git a/fuchsia_web/webengine/test/data/post_loaded.js b/fuchsia_web/webengine/test/data/post_loaded.js
new file mode 100644
index 0000000..deba8c02
--- /dev/null
+++ b/fuchsia_web/webengine/test/data/post_loaded.js
@@ -0,0 +1,5 @@
+// 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.
+
+self.postMessage("loaded");
diff --git a/fuchsia_web/webengine/test/data/shared_post_loaded.js b/fuchsia_web/webengine/test/data/shared_post_loaded.js
new file mode 100644
index 0000000..698be06
--- /dev/null
+++ b/fuchsia_web/webengine/test/data/shared_post_loaded.js
@@ -0,0 +1,7 @@
+// 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.
+
+self.addEventListener("connect", (evt) => {
+    evt.ports[0].postMessage("loaded");
+});
diff --git a/fuchsia_web/webengine/test/data/shared_worker.html b/fuchsia_web/webengine/test/data/shared_worker.html
new file mode 100644
index 0000000..56bdc1c
--- /dev/null
+++ b/fuchsia_web/webengine/test/data/shared_worker.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<!-- Disable favicon fetching. -->
+<link rel="icon" href="data:;base64,=">
+<title>create a shared worker</title>
+<script>
+    const params = new URLSearchParams(location.search);
+    const worker = new SharedWorker(params.get('worker_url'));
+    worker.port.addEventListener("message", (event) => {
+        document.title = event.data;
+    });
+    worker.port.start();
+</script>
diff --git a/fuchsia_web/webengine/test/data/web_worker.html b/fuchsia_web/webengine/test/data/web_worker.html
new file mode 100644
index 0000000..a003938
--- /dev/null
+++ b/fuchsia_web/webengine/test/data/web_worker.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<!-- Disable favicon fetching. -->
+<link rel="icon" href="data:;base64,=">
+<title>create a web worker</title>
+<script>
+    const params = new URLSearchParams(location.search);
+    const worker = new Worker(params.get('worker_url'));
+    worker.addEventListener("message", (event) => {
+        document.title = event.data;
+    });
+</script>
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index 2b2e27a..cd80d92 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -784,6 +784,12 @@
   if (is_chromeos) {
     deps += [ "//ui/gfx/linux:gbm" ]
   }
+
+  if (is_ios) {
+    bundle_deps = [
+      "//testing/buildbot/filters:gpu_unittests_filters_bundle_data",
+    ]
+  }
 }
 
 test("gpu_perftests") {
diff --git a/gpu/command_buffer/service/shared_image/d3d_image_backing.cc b/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
index 587fe797..0d13ef7 100644
--- a/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
@@ -838,9 +838,6 @@
   if (!ValidateBeginAccess(write_access))
     return false;
 
-  // If read fences or write fence are present, shared handle should be too.
-  CHECK((read_fences_.empty() && !write_fence_) || dxgi_shared_handle_state_);
-
   // Defer clearing fences until later to handle D3D11 failure to synchronize.
   std::vector<scoped_refptr<gfx::D3DSharedFence>> wait_fences =
       GetPendingWaitFences(d3d11_device, /*dawn_device=*/nullptr, write_access);
diff --git a/infra/config/generated/builders/ci/android-cronet-arm64-rel/gn-args.json b/infra/config/generated/builders/ci/android-cronet-arm64-rel/gn-args.json
index 09f3bb1..d9b3e7b1 100644
--- a/infra/config/generated/builders/ci/android-cronet-arm64-rel/gn-args.json
+++ b/infra/config/generated/builders/ci/android-cronet-arm64-rel/gn-args.json
@@ -13,7 +13,6 @@
     "is_component_build": false,
     "is_cronet_build": true,
     "is_debug": false,
-    "is_high_end_android": false,
     "is_official_build": true,
     "media_use_ffmpeg": false,
     "strip_debug_info": true,
diff --git a/infra/config/generated/builders/ci/android-cronet-mainline-clang-arm64-rel/gn-args.json b/infra/config/generated/builders/ci/android-cronet-mainline-clang-arm64-rel/gn-args.json
index 8de50f5..65566ad 100644
--- a/infra/config/generated/builders/ci/android-cronet-mainline-clang-arm64-rel/gn-args.json
+++ b/infra/config/generated/builders/ci/android-cronet-mainline-clang-arm64-rel/gn-args.json
@@ -15,7 +15,6 @@
     "is_component_build": false,
     "is_cronet_build": true,
     "is_debug": false,
-    "is_high_end_android": false,
     "is_official_build": true,
     "llvm_android_mainline": true,
     "media_use_ffmpeg": false,
diff --git a/infra/config/generated/builders/ci/android-cronet-marshmallow-arm64-perf-rel/gn-args.json b/infra/config/generated/builders/ci/android-cronet-marshmallow-arm64-perf-rel/gn-args.json
index 09f3bb1..d9b3e7b1 100644
--- a/infra/config/generated/builders/ci/android-cronet-marshmallow-arm64-perf-rel/gn-args.json
+++ b/infra/config/generated/builders/ci/android-cronet-marshmallow-arm64-perf-rel/gn-args.json
@@ -13,7 +13,6 @@
     "is_component_build": false,
     "is_cronet_build": true,
     "is_debug": false,
-    "is_high_end_android": false,
     "is_official_build": true,
     "media_use_ffmpeg": false,
     "strip_debug_info": true,
diff --git "a/infra/config/generated/builders/ci/chromeos-amd64-generic-rel \050reclient compare\051/gn-args.json" "b/infra/config/generated/builders/ci/chromeos-amd64-generic-rel \050reclient compare\051/gn-args.json"
deleted file mode 100644
index 13ca3b7..0000000
--- "a/infra/config/generated/builders/ci/chromeos-amd64-generic-rel \050reclient compare\051/gn-args.json"
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "args_file": "//build/args/chromeos/amd64-generic-vm.gni",
-  "gn_args": {
-    "also_build_lacros_chrome_for_architecture": "amd64",
-    "dcheck_always_on": false,
-    "is_chromeos_device": true,
-    "ozone_platform_headless": true,
-    "use_real_dbus_clients": false,
-    "use_remoteexec": true
-  }
-}
\ No newline at end of file
diff --git "a/infra/config/generated/builders/ci/chromeos-amd64-generic-rel \050reclient compare\051/properties.json" "b/infra/config/generated/builders/ci/chromeos-amd64-generic-rel \050reclient compare\051/properties.json"
deleted file mode 100644
index d13d3130d..0000000
--- "a/infra/config/generated/builders/ci/chromeos-amd64-generic-rel \050reclient compare\051/properties.json"
+++ /dev/null
@@ -1,70 +0,0 @@
-{
-  "$build/chromium_tests_builder_config": {
-    "builder_config": {
-      "additional_exclusions": [
-        "infra/config/generated/builders/ci/chromeos-amd64-generic-rel (reclient compare)/gn-args.json"
-      ],
-      "builder_db": {
-        "entries": [
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "chromeos-amd64-generic-rel (reclient compare)",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "build_gs_bucket": "chromium-fyi-archive",
-              "builder_group": "chromium.fyi",
-              "execution_mode": "COMPILE_AND_TEST",
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
-                "config": "chromium",
-                "cros_boards_with_qemu_images": [
-                  "amd64-generic",
-                  "amd64-generic-vm"
-                ],
-                "target_bits": 64,
-                "target_platform": "chromeos"
-              },
-              "legacy_gclient_config": {
-                "apply_configs": [
-                  "chromeos"
-                ],
-                "config": "chromium"
-              }
-            }
-          }
-        ]
-      },
-      "builder_ids": [
-        {
-          "bucket": "ci",
-          "builder": "chromeos-amd64-generic-rel (reclient compare)",
-          "project": "chromium"
-        }
-      ]
-    }
-  },
-  "$build/reclient": {
-    "ensure_verified": true,
-    "instance": "rbe-chromium-trusted",
-    "metrics_project": "chromium-reclient-metrics",
-    "rewrapper_env": {
-      "RBE_compare": "true",
-      "RBE_num_local_reruns": "1",
-      "RBE_num_remote_reruns": "1"
-    },
-    "scandeps_server": true
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "chromium.fyi",
-  "recipe": "chromium"
-}
\ No newline at end of file
diff --git "a/infra/config/generated/builders/ci/chromeos-amd64-generic-rel \050reclient compare\051/shadow-properties.json" "b/infra/config/generated/builders/ci/chromeos-amd64-generic-rel \050reclient compare\051/shadow-properties.json"
deleted file mode 100644
index d54aac8f..0000000
--- "a/infra/config/generated/builders/ci/chromeos-amd64-generic-rel \050reclient compare\051/shadow-properties.json"
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "$build/reclient": {
-    "ensure_verified": true,
-    "instance": "rbe-chromium-untrusted",
-    "metrics_project": "chromium-reclient-metrics",
-    "rewrapper_env": {
-      "RBE_compare": "true",
-      "RBE_num_local_reruns": "1",
-      "RBE_num_remote_reruns": "1"
-    },
-    "scandeps_server": true
-  }
-}
\ No newline at end of file
diff --git "a/infra/config/generated/builders/ci/chromeos-amd64-generic-rel \050reclient\051/gn-args.json" "b/infra/config/generated/builders/ci/chromeos-amd64-generic-rel \050reclient\051/gn-args.json"
deleted file mode 100644
index 13ca3b7..0000000
--- "a/infra/config/generated/builders/ci/chromeos-amd64-generic-rel \050reclient\051/gn-args.json"
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "args_file": "//build/args/chromeos/amd64-generic-vm.gni",
-  "gn_args": {
-    "also_build_lacros_chrome_for_architecture": "amd64",
-    "dcheck_always_on": false,
-    "is_chromeos_device": true,
-    "ozone_platform_headless": true,
-    "use_real_dbus_clients": false,
-    "use_remoteexec": true
-  }
-}
\ No newline at end of file
diff --git "a/infra/config/generated/builders/ci/chromeos-amd64-generic-rel \050reclient\051/properties.json" "b/infra/config/generated/builders/ci/chromeos-amd64-generic-rel \050reclient\051/properties.json"
deleted file mode 100644
index 39fe15bd..0000000
--- "a/infra/config/generated/builders/ci/chromeos-amd64-generic-rel \050reclient\051/properties.json"
+++ /dev/null
@@ -1,67 +0,0 @@
-{
-  "$build/chromium_tests_builder_config": {
-    "builder_config": {
-      "additional_exclusions": [
-        "infra/config/generated/builders/ci/chromeos-amd64-generic-rel (reclient)/gn-args.json"
-      ],
-      "builder_db": {
-        "entries": [
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "chromeos-amd64-generic-rel (reclient)",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "build_gs_bucket": "chromium-fyi-archive",
-              "builder_group": "chromium.fyi",
-              "execution_mode": "COMPILE_AND_TEST",
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
-                "config": "chromium",
-                "cros_boards_with_qemu_images": [
-                  "amd64-generic",
-                  "amd64-generic-vm"
-                ],
-                "target_bits": 64,
-                "target_platform": "chromeos"
-              },
-              "legacy_gclient_config": {
-                "apply_configs": [
-                  "chromeos"
-                ],
-                "config": "chromium"
-              }
-            }
-          }
-        ]
-      },
-      "builder_ids": [
-        {
-          "bucket": "ci",
-          "builder": "chromeos-amd64-generic-rel (reclient)",
-          "project": "chromium"
-        }
-      ]
-    }
-  },
-  "$build/reclient": {
-    "instance": "rbe-chromium-trusted",
-    "metrics_project": "chromium-reclient-metrics",
-    "rewrapper_env": {
-      "RBE_cache_silo": "chromeos-amd64-generic-rel (reclient)"
-    },
-    "scandeps_server": true
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "chromium.fyi",
-  "recipe": "chromium"
-}
\ No newline at end of file
diff --git "a/infra/config/generated/builders/ci/chromeos-amd64-generic-rel \050reclient\051/shadow-properties.json" "b/infra/config/generated/builders/ci/chromeos-amd64-generic-rel \050reclient\051/shadow-properties.json"
deleted file mode 100644
index 63a9eca..0000000
--- "a/infra/config/generated/builders/ci/chromeos-amd64-generic-rel \050reclient\051/shadow-properties.json"
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "$build/reclient": {
-    "instance": "rbe-chromium-untrusted",
-    "metrics_project": "chromium-reclient-metrics",
-    "rewrapper_env": {
-      "RBE_cache_silo": "chromeos-amd64-generic-rel (reclient)"
-    },
-    "scandeps_server": true
-  }
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/ios16-sdk-simulator/properties.json b/infra/config/generated/builders/ci/ios16-sdk-simulator/properties.json
index 918fa68..436ff374 100644
--- a/infra/config/generated/builders/ci/ios16-sdk-simulator/properties.json
+++ b/infra/config/generated/builders/ci/ios16-sdk-simulator/properties.json
@@ -63,5 +63,5 @@
   },
   "builder_group": "chromium.fyi",
   "recipe": "chromium",
-  "xcode_build_version": "15e5178i"
+  "xcode_build_version": "15e5188j"
 }
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/ios17-sdk-device/properties.json b/infra/config/generated/builders/ci/ios17-sdk-device/properties.json
index a834175..f14f7226 100644
--- a/infra/config/generated/builders/ci/ios17-sdk-device/properties.json
+++ b/infra/config/generated/builders/ci/ios17-sdk-device/properties.json
@@ -57,5 +57,5 @@
   },
   "builder_group": "chromium.fyi",
   "recipe": "chromium",
-  "xcode_build_version": "15e5178i"
+  "xcode_build_version": "15e5188j"
 }
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/ios17-sdk-simulator/properties.json b/infra/config/generated/builders/ci/ios17-sdk-simulator/properties.json
index b9140e8..763dcc1 100644
--- a/infra/config/generated/builders/ci/ios17-sdk-simulator/properties.json
+++ b/infra/config/generated/builders/ci/ios17-sdk-simulator/properties.json
@@ -63,5 +63,5 @@
   },
   "builder_group": "chromium.fyi",
   "recipe": "chromium",
-  "xcode_build_version": "15e5178i"
+  "xcode_build_version": "15e5188j"
 }
\ No newline at end of file
diff --git a/infra/config/generated/builders/gn_args_locations.json b/infra/config/generated/builders/gn_args_locations.json
index ff89849d..96064be 100644
--- a/infra/config/generated/builders/gn_args_locations.json
+++ b/infra/config/generated/builders/gn_args_locations.json
@@ -275,8 +275,6 @@
     "Win x64 Builder (reclient)": "ci/Win x64 Builder (reclient)/gn-args.json",
     "android-fieldtrial-rel": "ci/android-fieldtrial-rel/gn-args.json",
     "android-perfetto-rel": "ci/android-perfetto-rel/gn-args.json",
-    "chromeos-amd64-generic-rel (reclient compare)": "ci/chromeos-amd64-generic-rel (reclient compare)/gn-args.json",
-    "chromeos-amd64-generic-rel (reclient)": "ci/chromeos-amd64-generic-rel (reclient)/gn-args.json",
     "chromeos-jacuzzi-rel-skylab-fyi": "ci/chromeos-jacuzzi-rel-skylab-fyi/gn-args.json",
     "chromeos-octopus-rel-skylab-fyi": "ci/chromeos-octopus-rel-skylab-fyi/gn-args.json",
     "ios-blink-dbg-fyi": "ci/ios-blink-dbg-fyi/gn-args.json",
diff --git a/infra/config/generated/builders/try/android-cronet-arm64-rel/gn-args.json b/infra/config/generated/builders/try/android-cronet-arm64-rel/gn-args.json
index fb1acb1..99fc373 100644
--- a/infra/config/generated/builders/try/android-cronet-arm64-rel/gn-args.json
+++ b/infra/config/generated/builders/try/android-cronet-arm64-rel/gn-args.json
@@ -13,7 +13,6 @@
     "is_component_build": false,
     "is_cronet_build": true,
     "is_debug": false,
-    "is_high_end_android": false,
     "is_official_build": true,
     "media_use_ffmpeg": false,
     "strip_debug_info": true,
diff --git a/infra/config/generated/builders/try/android-cronet-mainline-clang-arm64-rel/gn-args.json b/infra/config/generated/builders/try/android-cronet-mainline-clang-arm64-rel/gn-args.json
index e9606ae..aa35304 100644
--- a/infra/config/generated/builders/try/android-cronet-mainline-clang-arm64-rel/gn-args.json
+++ b/infra/config/generated/builders/try/android-cronet-mainline-clang-arm64-rel/gn-args.json
@@ -15,7 +15,6 @@
     "is_component_build": false,
     "is_cronet_build": true,
     "is_debug": false,
-    "is_high_end_android": false,
     "is_official_build": true,
     "llvm_android_mainline": true,
     "media_use_ffmpeg": false,
diff --git a/infra/config/generated/builders/try/ios16-sdk-simulator/properties.json b/infra/config/generated/builders/try/ios16-sdk-simulator/properties.json
index 52422b88..abca72c 100644
--- a/infra/config/generated/builders/try/ios16-sdk-simulator/properties.json
+++ b/infra/config/generated/builders/try/ios16-sdk-simulator/properties.json
@@ -56,5 +56,5 @@
   },
   "builder_group": "tryserver.chromium.mac",
   "recipe": "chromium_trybot",
-  "xcode_build_version": "15e5178i"
+  "xcode_build_version": "15e5188j"
 }
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/ios17-sdk-simulator/properties.json b/infra/config/generated/builders/try/ios17-sdk-simulator/properties.json
index 90f1543..daa7cfa8 100644
--- a/infra/config/generated/builders/try/ios17-sdk-simulator/properties.json
+++ b/infra/config/generated/builders/try/ios17-sdk-simulator/properties.json
@@ -56,5 +56,5 @@
   },
   "builder_group": "tryserver.chromium.mac",
   "recipe": "chromium_trybot",
-  "xcode_build_version": "15e5178i"
+  "xcode_build_version": "15e5188j"
 }
\ No newline at end of file
diff --git a/infra/config/generated/health-specs/health-specs.json b/infra/config/generated/health-specs/health-specs.json
index e4976522..adc850ce 100644
--- a/infra/config/generated/health-specs/health-specs.json
+++ b/infra/config/generated/health-specs/health-specs.json
@@ -7814,46 +7814,6 @@
           }
         ]
       },
-      "chromeos-amd64-generic-rel (reclient compare)": {
-        "problem_specs": [
-          {
-            "name": "Unhealthy",
-            "period_days": 7,
-            "score": 5,
-            "thresholds": {
-              "_default": "_default"
-            }
-          },
-          {
-            "name": "Low Value",
-            "period_days": 90,
-            "score": 1,
-            "thresholds": {
-              "_default": "_default"
-            }
-          }
-        ]
-      },
-      "chromeos-amd64-generic-rel (reclient)": {
-        "problem_specs": [
-          {
-            "name": "Unhealthy",
-            "period_days": 7,
-            "score": 5,
-            "thresholds": {
-              "_default": "_default"
-            }
-          },
-          {
-            "name": "Low Value",
-            "period_days": 90,
-            "score": 1,
-            "thresholds": {
-              "_default": "_default"
-            }
-          }
-        ]
-      },
       "chromeos-amd64-generic-rel-gtest": {
         "contact_team_email": "chromeos-sw-engprod@google.com",
         "problem_specs": [
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 945046a..fcb45b7d 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -15432,7 +15432,7 @@
         '    "chromium"'
         '  ]'
         '}'
-      execution_timeout_secs: 21600
+      execution_timeout_secs: 28800
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -36747,191 +36747,6 @@
       contact_team_email: "chromeos-sw-engprod@google.com"
     }
     builders {
-      name: "chromeos-amd64-generic-rel (reclient compare)"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
-      dimensions: "os:Ubuntu-22.04"
-      dimensions: "pool:luci.chromium.ci"
-      dimensions: "ssd:0"
-      exe {
-        cipd_package: "infra/chromium/bootstrapper/${platform}"
-        cipd_version: "latest"
-        cmd: "bootstrapper"
-      }
-      properties:
-        '{'
-        '  "$bootstrap/exe": {'
-        '    "exe": {'
-        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
-        '      "cipd_version": "refs/heads/main",'
-        '      "cmd": ['
-        '        "luciexe"'
-        '      ]'
-        '    }'
-        '  },'
-        '  "$bootstrap/properties": {'
-        '    "properties_file": "infra/config/generated/builders/ci/chromeos-amd64-generic-rel (reclient compare)/properties.json",'
-        '    "shadow_properties_file": "infra/config/generated/builders/ci/chromeos-amd64-generic-rel (reclient compare)/shadow-properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "chromium.fyi",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium"'
-        '}'
-      priority: 35
-      execution_timeout_secs: 50400
-      build_numbers: YES
-      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "chromium_swarming.expose_merge_script_failures"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "ci_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-      description_html: "Verifies whether local and remote build artifacts are identical."
-      shadow_builder_adjustments {
-        service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
-        pool: "luci.chromium.try"
-        dimensions: "free_space:"
-        dimensions: "pool:luci.chromium.try"
-      }
-    }
-    builders {
-      name: "chromeos-amd64-generic-rel (reclient)"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
-      dimensions: "os:Ubuntu-22.04"
-      dimensions: "pool:luci.chromium.ci"
-      dimensions: "ssd:0"
-      exe {
-        cipd_package: "infra/chromium/bootstrapper/${platform}"
-        cipd_version: "latest"
-        cmd: "bootstrapper"
-      }
-      properties:
-        '{'
-        '  "$bootstrap/exe": {'
-        '    "exe": {'
-        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
-        '      "cipd_version": "refs/heads/main",'
-        '      "cmd": ['
-        '        "luciexe"'
-        '      ]'
-        '    }'
-        '  },'
-        '  "$bootstrap/properties": {'
-        '    "properties_file": "infra/config/generated/builders/ci/chromeos-amd64-generic-rel (reclient)/properties.json",'
-        '    "shadow_properties_file": "infra/config/generated/builders/ci/chromeos-amd64-generic-rel (reclient)/shadow-properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "chromium.fyi",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium"'
-        '}'
-      priority: 35
-      execution_timeout_secs: 36000
-      build_numbers: YES
-      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "chromium_swarming.expose_merge_script_failures"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "ci_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-      shadow_builder_adjustments {
-        service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
-        pool: "luci.chromium.try"
-        dimensions: "free_space:"
-        dimensions: "pool:luci.chromium.try"
-      }
-    }
-    builders {
       name: "chromeos-amd64-generic-rel-gtest"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -40636,8 +40451,8 @@
       priority: 35
       execution_timeout_secs: 36000
       caches {
-        name: "xcode_ios_15e5178i"
-        path: "xcode_ios_15e5178i.app"
+        name: "xcode_ios_15e5188j"
+        path: "xcode_ios_15e5188j.app"
       }
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
@@ -40826,8 +40641,8 @@
       priority: 35
       execution_timeout_secs: 36000
       caches {
-        name: "xcode_ios_15e5178i"
-        path: "xcode_ios_15e5178i.app"
+        name: "xcode_ios_15e5188j"
+        path: "xcode_ios_15e5188j.app"
       }
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
@@ -40920,8 +40735,8 @@
       priority: 35
       execution_timeout_secs: 36000
       caches {
-        name: "xcode_ios_15e5178i"
-        path: "xcode_ios_15e5178i.app"
+        name: "xcode_ios_15e5188j"
+        path: "xcode_ios_15e5188j.app"
       }
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
@@ -82494,8 +82309,8 @@
         seconds: 120
       }
       caches {
-        name: "xcode_ios_15e5178i"
-        path: "xcode_ios_15e5178i.app"
+        name: "xcode_ios_15e5188j"
+        path: "xcode_ios_15e5188j.app"
       }
       build_numbers: YES
       service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
@@ -82688,8 +82503,8 @@
         seconds: 120
       }
       caches {
-        name: "xcode_ios_15e5178i"
-        path: "xcode_ios_15e5178i.app"
+        name: "xcode_ios_15e5188j"
+        path: "xcode_ios_15e5188j.app"
       }
       build_numbers: YES
       service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 8b8c78ea..69b21b0 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -9509,20 +9509,11 @@
     category: "celab"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/chromeos-amd64-generic-rel (reclient)"
-    category: "cros x64"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/Comparison Simple Chrome (reclient)"
     category: "cros x64"
     short_name: "cmp"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/chromeos-amd64-generic-rel (reclient compare)"
-    category: "cros x64"
-    short_name: "cmp"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/Comparison Simple Chrome (reclient)(CQ)"
     category: "cros x64|cq"
     short_name: "cmp"
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index 8ebd4083..2c5267b4 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -4031,24 +4031,6 @@
   }
 }
 job {
-  id: "chromeos-amd64-generic-rel (reclient compare)"
-  realm: "ci"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "ci"
-    builder: "chromeos-amd64-generic-rel (reclient compare)"
-  }
-}
-job {
-  id: "chromeos-amd64-generic-rel (reclient)"
-  realm: "ci"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "ci"
-    builder: "chromeos-amd64-generic-rel (reclient)"
-  }
-}
-job {
   id: "chromeos-amd64-generic-rel-goma-rbe-canary"
   realm: "goma"
   buildbucket {
@@ -6523,8 +6505,6 @@
   triggers: "chromeos-amd64-generic-dbg"
   triggers: "chromeos-amd64-generic-lacros-dbg"
   triggers: "chromeos-amd64-generic-rel"
-  triggers: "chromeos-amd64-generic-rel (reclient compare)"
-  triggers: "chromeos-amd64-generic-rel (reclient)"
   triggers: "chromeos-arm-generic-dbg"
   triggers: "chromeos-arm-generic-rel"
   triggers: "chromeos-arm64-generic-rel"
diff --git a/infra/config/generated/testing/mixins.pyl b/infra/config/generated/testing/mixins.pyl
index efb1d65..b01791a9 100644
--- a/infra/config/generated/testing/mixins.pyl
+++ b/infra/config/generated/testing/mixins.pyl
@@ -490,6 +490,13 @@
       '--logs-dir=${ISOLATED_OUTDIR}/logs',
     ],
   },
+  'gce': {
+    'swarming': {
+      'dimensions': {
+        'gce': '1',
+      },
+    },
+  },
   'gpu-swarming-pool': {
     'swarming': {
       'dimensions': {
@@ -1323,12 +1330,12 @@
   'xcode_15_beta': {
     'args': [
       '--xcode-build-version',
-      '15e5178i',
+      '15e5188j',
     ],
     'swarming': {
       'named_caches': [
         {
-          'name': 'xcode_ios_15e5178i',
+          'name': 'xcode_ios_15e5188j',
           'path': 'Xcode.app',
         },
       ],
diff --git a/infra/config/generated/testing/test_suites.pyl b/infra/config/generated/testing/test_suites.pyl
index 57027dd..dba5d18 100644
--- a/infra/config/generated/testing/test_suites.pyl
+++ b/infra/config/generated/testing/test_suites.pyl
@@ -758,20 +758,6 @@
       'chrome_public_smoke_test': {},
     },
 
-    'chromium_dev_desktop_gtests': {
-      'base_unittests': {},
-      'content_browsertests': {
-        'swarming': {
-          'shards': 4,
-        },
-      },
-      'content_unittests': {},
-      'interactive_ui_tests': {},
-      'net_unittests': {},
-      'rust_gtest_interop_unittests': {},
-      'unit_tests': {},
-    },
-
     'chromium_dev_linux_gtests': {
       'base_unittests': {
         'swarming': {
@@ -828,6 +814,32 @@
       },
     },
 
+    'chromium_dev_mac_gtests': {
+      'base_unittests': {},
+      'content_unittests': {},
+      'net_unittests': {},
+      'rust_gtest_interop_unittests': {},
+      'unit_tests': {},
+    },
+
+    'chromium_dev_win_gtests': {
+      'base_unittests': {},
+      'content_browsertests': {
+        'swarming': {
+          'shards': 4,
+        },
+      },
+      'content_unittests': {},
+      'interactive_ui_tests': {
+        'swarming': {
+          'shards': 3,
+        },
+      },
+      'net_unittests': {},
+      'rust_gtest_interop_unittests': {},
+      'unit_tests': {},
+    },
+
     'chromium_gtests': {
       'absl_hardening_tests': {},
       'angle_unittests': {
@@ -4104,7 +4116,12 @@
       'gin_unittests': {},
       'gl_unittests': {},
       'google_apis_unittests': {},
-      'gpu_unittests': {},
+      'gpu_unittests': {
+        'args': [
+          '--test-launcher-bot-mode',
+          '--test-launcher-filter-file=testing/buildbot/filters/ios.gpu_unittests.filter',
+        ],
+      },
       'gwp_asan_unittests': {},
       'ipc_tests': {},
       'latency_unittests': {},
@@ -7974,6 +7991,9 @@
 
     'optimization_guide_linux_script_tests': {
       'model_validation_tests': {
+        'mixins': [
+          'gce',
+        ],
         'variants': [
           'MODEL_VALIDATION_BASE',
           'MODEL_VALIDATION_TRUNK',
@@ -8008,16 +8028,29 @@
           'INTEL_UHD_630',
         ],
       },
-      'optimization_guide_nogpu_gtests': {},
+      'optimization_guide_nogpu_gtests': {
+        'mixins': [
+          'gce',
+        ],
+      },
     },
 
     'optimization_guide_win_script_tests': {
       'model_validation_tests': {
+        'mixins': [
+          'gce',
+        ],
         'variants': [
           'MODEL_VALIDATION_BASE',
           'MODEL_VALIDATION_TRUNK',
         ],
       },
+      'ondevice_stability_tests': {
+        'variants': [
+          'AMD_RADEON_RX_5500_XT',
+          'INTEL_UHD_630',
+        ],
+      },
     },
 
     'webview_trichrome_10_cts_tests_gtest': {
diff --git a/infra/config/generated/testing/variants.pyl b/infra/config/generated/testing/variants.pyl
index 90c72e8d..fab1872 100644
--- a/infra/config/generated/testing/variants.pyl
+++ b/infra/config/generated/testing/variants.pyl
@@ -307,16 +307,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'identifier': 'Lacros version skew testing ash canary',
-    'description': 'Run with ash-chrome version 123.0.6286.0',
+    'description': 'Run with ash-chrome version 123.0.6287.0',
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6286.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6287.0/test_ash_chrome',
     ],
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v123.0.6286.0',
-          'revision': 'version:123.0.6286.0',
+          'location': 'lacros_version_skew_tests_v123.0.6287.0',
+          'revision': 'version:123.0.6287.0',
         },
       ],
     },
diff --git a/infra/config/gn_args/gn_args.star b/infra/config/gn_args/gn_args.star
index e41f8a3..89f85fe 100644
--- a/infra/config/gn_args/gn_args.star
+++ b/infra/config/gn_args/gn_args.star
@@ -107,13 +107,6 @@
 )
 
 gn_args.config(
-    name = "android_low_end",
-    args = {
-        "is_high_end_android": False,
-    },
-)
-
-gn_args.config(
     name = "android_low_end_secondary_toolchain",
     args = {
         "is_high_end_android_secondary_toolchain": False,
diff --git a/infra/config/lib/builder_health_indicators.star b/infra/config/lib/builder_health_indicators.star
index e0b9b6a..fc6ced3 100644
--- a/infra/config/lib/builder_health_indicators.star
+++ b/infra/config/lib/builder_health_indicators.star
@@ -361,8 +361,6 @@
         "chromeos-amd64-generic-cfi-thin-lto-rel",
         "chromeos-amd64-generic-dbg",
         "chromeos-amd64-generic-lacros-dbg",
-        "chromeos-amd64-generic-rel (reclient compare)",
-        "chromeos-amd64-generic-rel (reclient)",
         "chromeos-amd64-generic-rel",
         "chromeos-arm-generic-dbg",
         "chromeos-arm-generic-rel",
diff --git a/infra/config/lib/builders.star b/infra/config/lib/builders.star
index ae4a9b4..a9a46d2 100644
--- a/infra/config/lib/builders.star
+++ b/infra/config/lib/builders.star
@@ -185,7 +185,7 @@
     # Default Xcode 15 for chromium iOS
     x15main = xcode_enum("15c500b"),
     # A newer Xcode 15 version used on beta bots.
-    x15betabots = xcode_enum("15e5178i"),
+    x15betabots = xcode_enum("15e5188j"),
     # in use by ios-webkit-tot
     x14wk = xcode_enum("14c18wk"),
 )
diff --git a/infra/config/lib/description_exceptions.star b/infra/config/lib/description_exceptions.star
index f5fb84ec..fe337b5 100644
--- a/infra/config/lib/description_exceptions.star
+++ b/infra/config/lib/description_exceptions.star
@@ -339,7 +339,6 @@
         "chromeos-amd64-generic-cfi-thin-lto-rel",
         "chromeos-amd64-generic-dbg",
         "chromeos-amd64-generic-lacros-dbg",
-        "chromeos-amd64-generic-rel (reclient)",
         "chromeos-amd64-generic-rel",
         "chromeos-arm-generic-dbg",
         "chromeos-arm-generic-rel",
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.star b/infra/config/subprojects/chromium/ci/chromium.android.star
index 076857d..0439c9f 100644
--- a/infra/config/subprojects/chromium/ci/chromium.android.star
+++ b/infra/config/subprojects/chromium/ci/chromium.android.star
@@ -962,7 +962,6 @@
     gn_args = gn_args.config(
         configs = [
             "android_builder_without_codecs",
-            "android_low_end",
             "cronet_android",
             "official_optimize",
             "release_builder",
@@ -1086,7 +1085,6 @@
     gn_args = gn_args.config(
         configs = [
             "android_builder_without_codecs",
-            "android_low_end",
             "cronet_android",
             "official_optimize",
             "release_builder",
@@ -1284,7 +1282,6 @@
     gn_args = gn_args.config(
         configs = [
             "android_builder_without_codecs",
-            "android_low_end",
             "cronet_android",
             "official_optimize",
             "release_builder",
diff --git a/infra/config/subprojects/chromium/ci/chromium.fuzz.star b/infra/config/subprojects/chromium/ci/chromium.fuzz.star
index a1757228..90a131a 100644
--- a/infra/config/subprojects/chromium/ci/chromium.fuzz.star
+++ b/infra/config/subprojects/chromium/ci/chromium.fuzz.star
@@ -1143,6 +1143,6 @@
     contact_team_email = "chrome-deet-core@google.com",
     # crbug.com/1175182: Temporarily increase timeout
     # crbug.com/1372531: Increase timeout again
-    execution_timeout = 6 * time.hour,
+    execution_timeout = 8 * time.hour,
     reclient_jobs = reclient.jobs.LOW_JOBS_FOR_CI,
 )
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star
index ace9b1aa..ca9fb9e 100644
--- a/infra/config/subprojects/chromium/ci/chromium.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -1981,90 +1981,6 @@
 )
 
 ci.builder(
-    name = "chromeos-amd64-generic-rel (reclient)",
-    builder_spec = builder_config.builder_spec(
-        gclient_config = builder_config.gclient_config(
-            config = "chromium",
-            apply_configs = ["chromeos"],
-        ),
-        chromium_config = builder_config.chromium_config(
-            config = "chromium",
-            apply_configs = ["mb"],
-            target_bits = 64,
-            target_platform = builder_config.target_platform.CHROMEOS,
-            cros_boards_with_qemu_images = [
-                "amd64-generic",
-                "amd64-generic-vm",
-            ],
-        ),
-        build_gs_bucket = "chromium-fyi-archive",
-    ),
-    gn_args = gn_args.config(
-        configs = [
-            "chromeos_device",
-            "dcheck_off",
-            "reclient",
-            "amd64-generic-vm",
-            "ozone_headless",
-            "use_fake_dbus_clients",
-            "also_build_lacros_chrome_for_architecture_amd64",
-        ],
-    ),
-    os = os.LINUX_DEFAULT,
-    console_view_entry = consoles.console_view_entry(
-        category = "cros x64",
-    ),
-    reclient_jobs = None,
-    reclient_rewrapper_env = {"RBE_cache_silo": "chromeos-amd64-generic-rel (reclient)"},
-)
-
-ci.builder(
-    name = "chromeos-amd64-generic-rel (reclient compare)",
-    description_html = "Verifies whether local and remote build artifacts are identical.",
-    builder_spec = builder_config.builder_spec(
-        gclient_config = builder_config.gclient_config(
-            config = "chromium",
-            apply_configs = ["chromeos"],
-        ),
-        chromium_config = builder_config.chromium_config(
-            config = "chromium",
-            apply_configs = ["mb"],
-            target_bits = 64,
-            target_platform = builder_config.target_platform.CHROMEOS,
-            cros_boards_with_qemu_images = [
-                "amd64-generic",
-                "amd64-generic-vm",
-            ],
-        ),
-        build_gs_bucket = "chromium-fyi-archive",
-    ),
-    gn_args = gn_args.config(
-        configs = [
-            "chromeos_device",
-            "dcheck_off",
-            "reclient",
-            "amd64-generic-vm",
-            "ozone_headless",
-            "use_fake_dbus_clients",
-            "also_build_lacros_chrome_for_architecture_amd64",
-        ],
-    ),
-    os = os.LINUX_DEFAULT,
-    console_view_entry = consoles.console_view_entry(
-        category = "cros x64",
-        short_name = "cmp",
-    ),
-    execution_timeout = 14 * time.hour,
-    reclient_ensure_verified = True,
-    reclient_jobs = None,
-    reclient_rewrapper_env = {
-        "RBE_compare": "true",
-        "RBE_num_local_reruns": "1",
-        "RBE_num_remote_reruns": "1",
-    },
-)
-
-ci.builder(
     name = "lacros-amd64-generic-rel (reclient)",
     builder_spec = builder_config.builder_spec(
         gclient_config = builder_config.gclient_config(
diff --git a/infra/config/targets/basic_suites.star b/infra/config/targets/basic_suites.star
index 6fed8615..cc49c89 100644
--- a/infra/config/targets/basic_suites.star
+++ b/infra/config/targets/basic_suites.star
@@ -779,7 +779,18 @@
 )
 
 targets.legacy_basic_suite(
-    name = "chromium_dev_desktop_gtests",
+    name = "chromium_dev_mac_gtests",
+    tests = {
+        "base_unittests": targets.legacy_test_config(),
+        "content_unittests": targets.legacy_test_config(),
+        "net_unittests": targets.legacy_test_config(),
+        "rust_gtest_interop_unittests": targets.legacy_test_config(),
+        "unit_tests": targets.legacy_test_config(),
+    },
+)
+
+targets.legacy_basic_suite(
+    name = "chromium_dev_win_gtests",
     tests = {
         "base_unittests": targets.legacy_test_config(),
         "content_browsertests": targets.legacy_test_config(
@@ -788,7 +799,11 @@
             ),
         ),
         "content_unittests": targets.legacy_test_config(),
-        "interactive_ui_tests": targets.legacy_test_config(),
+        "interactive_ui_tests": targets.legacy_test_config(
+            swarming = targets.swarming(
+                shards = 3,
+            ),
+        ),
         "net_unittests": targets.legacy_test_config(),
         "rust_gtest_interop_unittests": targets.legacy_test_config(),
         "unit_tests": targets.legacy_test_config(),
@@ -3703,7 +3718,12 @@
         "gin_unittests": targets.legacy_test_config(),
         "gl_unittests": targets.legacy_test_config(),
         "google_apis_unittests": targets.legacy_test_config(),
-        "gpu_unittests": targets.legacy_test_config(),
+        "gpu_unittests": targets.legacy_test_config(
+            args = [
+                "--test-launcher-bot-mode",
+                "--test-launcher-filter-file=testing/buildbot/filters/ios.gpu_unittests.filter",
+            ],
+        ),
         "gwp_asan_unittests": targets.legacy_test_config(),
         "ipc_tests": targets.legacy_test_config(),
         "latency_unittests": targets.legacy_test_config(),
diff --git a/infra/config/targets/lacros-version-skew-variants.json b/infra/config/targets/lacros-version-skew-variants.json
index 4f0e2a1d..be0093d 100644
--- a/infra/config/targets/lacros-version-skew-variants.json
+++ b/infra/config/targets/lacros-version-skew-variants.json
@@ -1,16 +1,16 @@
 {
   "LACROS_VERSION_SKEW_CANARY": {
     "args": [
-      "--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6286.0/test_ash_chrome"
+      "--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6287.0/test_ash_chrome"
     ],
-    "description": "Run with ash-chrome version 123.0.6286.0",
+    "description": "Run with ash-chrome version 123.0.6287.0",
     "identifier": "Lacros version skew testing ash canary",
     "swarming": {
       "cipd_packages": [
         {
           "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-          "location": "lacros_version_skew_tests_v123.0.6286.0",
-          "revision": "version:123.0.6286.0"
+          "location": "lacros_version_skew_tests_v123.0.6287.0",
+          "revision": "version:123.0.6287.0"
         }
       ]
     }
diff --git a/infra/config/targets/matrix_compound_suites.star b/infra/config/targets/matrix_compound_suites.star
index f3268f5..5dd31a1d 100644
--- a/infra/config/targets/matrix_compound_suites.star
+++ b/infra/config/targets/matrix_compound_suites.star
@@ -1376,6 +1376,9 @@
     name = "optimization_guide_linux_script_tests",
     basic_suites = {
         "model_validation_tests": targets.legacy_matrix_config(
+            mixins = [
+                "gce",
+            ],
             variants = [
                 "MODEL_VALIDATION_BASE",
                 "MODEL_VALIDATION_TRUNK",
@@ -1413,7 +1416,11 @@
 targets.legacy_matrix_compound_suite(
     name = "optimization_guide_win_gtests",
     basic_suites = {
-        "optimization_guide_nogpu_gtests": None,
+        "optimization_guide_nogpu_gtests": targets.legacy_matrix_config(
+            mixins = [
+                "gce",
+            ],
+        ),
         "optimization_guide_gpu_gtests": targets.legacy_matrix_config(
             # TODO(b:321865883): Add NVIDIA variant once deployment is stable.
             variants = [
@@ -1428,11 +1435,20 @@
     name = "optimization_guide_win_script_tests",
     basic_suites = {
         "model_validation_tests": targets.legacy_matrix_config(
+            mixins = [
+                "gce",
+            ],
             variants = [
                 "MODEL_VALIDATION_BASE",
                 "MODEL_VALIDATION_TRUNK",
             ],
         ),
+        "ondevice_stability_tests": targets.legacy_matrix_config(
+            variants = [
+                "AMD_RADEON_RX_5500_XT",
+                "INTEL_UHD_630",
+            ],
+        ),
     },
 )
 
diff --git a/infra/config/targets/mixins.star b/infra/config/targets/mixins.star
index a0af7aa..1d1368d 100644
--- a/infra/config/targets/mixins.star
+++ b/infra/config/targets/mixins.star
@@ -616,6 +616,15 @@
 )
 
 targets.mixin(
+    name = "gce",
+    swarming = targets.swarming(
+        dimensions = {
+            "gce": "1",
+        },
+    ),
+)
+
+targets.mixin(
     name = "gpu-swarming-pool",
     swarming = targets.swarming(
         dimensions = {
@@ -1679,12 +1688,12 @@
     name = "xcode_15_beta",
     args = [
         "--xcode-build-version",
-        "15e5178i",
+        "15e5188j",
     ],
     swarming = targets.swarming(
         named_caches = [
             swarming.cache(
-                name = "xcode_ios_15e5178i",
+                name = "xcode_ios_15e5188j",
                 path = "Xcode.app",
             ),
         ],
diff --git a/internal b/internal
index d77efc6..c0441ac 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit d77efc6fbf0b5265b6faa739fc7e5d9e9799e0d8
+Subproject commit c0441ac66681bb025fe45ca06acbd21a72b8f39e
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 879573d..be89e22 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -424,6 +424,12 @@
       <message name="IDS_IOS_BACK_FORWARD_SWIPE_IPH" desc="The text for the full screen in-product help for side swipe on the current tab to navigate.">
         You can swipe the edge to go back or forward.
       </message>
+      <message name="IDS_IOS_BACK_FORWARD_SWIPE_IPH_BACK_ONLY" desc="The text for the full screen in-product help for side swipe on the current tab to navigate.">
+        You can swipe the edge to go back.
+      </message>
+      <message name="IDS_IOS_BACK_FORWARD_SWIPE_IPH_FORWARD_ONLY" desc="The text for the full screen in-product help for side swipe on the current tab to navigate.">
+        You can swipe the edge to go forward.
+      </message>
       <message name="IDS_IOS_BADGE_INCOGNITO_HINT" desc="Button displayed when the user is in Incognito mode. [iOS only]">
         Current Webpage is on Incognito
       </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_BACK_FORWARD_SWIPE_IPH_BACK_ONLY.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_BACK_FORWARD_SWIPE_IPH_BACK_ONLY.png.sha1
new file mode 100644
index 0000000..21b7f8d4
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_BACK_FORWARD_SWIPE_IPH_BACK_ONLY.png.sha1
@@ -0,0 +1 @@
+8b24ffe244ce899526e5f362c03665a894d5e512
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_BACK_FORWARD_SWIPE_IPH_FORWARD_ONLY.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_BACK_FORWARD_SWIPE_IPH_FORWARD_ONLY.png.sha1
new file mode 100644
index 0000000..4f24a02
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_BACK_FORWARD_SWIPE_IPH_FORWARD_ONLY.png.sha1
@@ -0,0 +1 @@
+a98d5841a0bee06e311e54167152e63b280fa593
\ No newline at end of file
diff --git a/ios/chrome/browser/autofill/model/credit_card/credit_card_data.h b/ios/chrome/browser/autofill/model/credit_card/credit_card_data.h
index 6b71da2..3d5fc6a 100644
--- a/ios/chrome/browser/autofill/model/credit_card/credit_card_data.h
+++ b/ios/chrome/browser/autofill/model/credit_card/credit_card_data.h
@@ -31,7 +31,7 @@
 @property(readonly) autofill::CreditCard::RecordType recordType;
 
 // Initializes from the autofill credit card type, and an icon.
-- (instancetype)initWithCreditCard:(const autofill::CreditCard*)creditCard
+- (instancetype)initWithCreditCard:(const autofill::CreditCard&)creditCard
                               icon:(UIImage*)icon;
 
 @end
diff --git a/ios/chrome/browser/autofill/model/credit_card/credit_card_data.mm b/ios/chrome/browser/autofill/model/credit_card/credit_card_data.mm
index a363dbc..2200c7e 100644
--- a/ios/chrome/browser/autofill/model/credit_card/credit_card_data.mm
+++ b/ios/chrome/browser/autofill/model/credit_card/credit_card_data.mm
@@ -13,23 +13,23 @@
 
 @implementation CreditCardData
 
-- (instancetype)initWithCreditCard:(const autofill::CreditCard*)creditCard
+- (instancetype)initWithCreditCard:(const autofill::CreditCard&)creditCard
                               icon:(UIImage*)icon {
   if (self = [super init]) {
     _cardNameAndLastFourDigits =
-        base::SysUTF16ToNSString(creditCard->CardNameAndLastFourDigits());
+        base::SysUTF16ToNSString(creditCard.CardNameAndLastFourDigits());
     _cardDetails = base::SysUTF16ToNSString(
-        (creditCard->record_type() ==
+        (creditCard.record_type() ==
          autofill::CreditCard::RecordType::kVirtualCard)
             ? l10n_util::GetStringUTF16(
                   IDS_AUTOFILL_VIRTUAL_CARD_SUGGESTION_OPTION_VALUE)
-            : creditCard->AbbreviatedExpirationDateForDisplay(
+            : creditCard.AbbreviatedExpirationDateForDisplay(
                   /* with_prefix=*/false));
     _accessibleCardName = [self accessibleCardName:creditCard];
-    _backendIdentifier = base::SysUTF8ToNSString(creditCard->guid());
+    _backendIdentifier = base::SysUTF8ToNSString(creditCard.guid());
     if (base::FeatureList::IsEnabled(
             autofill::features::kAutofillEnableVirtualCards)) {
-      _recordType = creditCard->record_type();
+      _recordType = creditCard.record_type();
     }
 
     if (icon.size.width > 0.0 && icon.size.width < 40.0 && icon.scale > 1.0) {
@@ -48,13 +48,13 @@
 
 #pragma mark - Private
 
-- (NSString*)accessibleCardName:(const autofill::CreditCard*)creditCard {
+- (NSString*)accessibleCardName:(const autofill::CreditCard&)creditCard {
   // Get the card name. Prepend the card type if the card name doesn't already
   // start with the card type.
   NSString* cardType = base::SysUTF16ToNSString(
-      creditCard->GetRawInfo(autofill::CREDIT_CARD_TYPE));
+      creditCard.GetRawInfo(autofill::CREDIT_CARD_TYPE));
   NSString* cardAccessibleName =
-      base::SysUTF16ToNSString(creditCard->CardNameForAutofillDisplay());
+      base::SysUTF16ToNSString(creditCard.CardNameForAutofillDisplay());
   if (![cardAccessibleName hasPrefix:cardType]) {
     // If the card name doesn't already start with the card type, add the card
     // type at the beginning of the card name.
@@ -66,7 +66,7 @@
   // example, "1215" will become "1 2 1 5" and will read "one two one five"
   // instead of "one thousand two hundred and fifteen".
   NSString* cardLastDigits =
-      base::SysUTF16ToNSString(creditCard->LastFourDigits());
+      base::SysUTF16ToNSString(creditCard.LastFourDigits());
   NSMutableArray* digits = [[NSMutableArray alloc] init];
   if (cardLastDigits.length > 0) {
     for (NSUInteger i = 0; i < cardLastDigits.length; i++) {
@@ -83,7 +83,7 @@
 
   // Either prepend that the card is a virtual card OR append the expiration
   // date.
-  if (creditCard->record_type() ==
+  if (creditCard.record_type() ==
       autofill::CreditCard::RecordType::kVirtualCard) {
     cardAccessibleName = [@[ self.cardDetails, cardAccessibleName ]
         componentsJoinedByString:@" "];
diff --git a/ios/chrome/browser/autofill/model/personal_data_manager_factory.mm b/ios/chrome/browser/autofill/model/personal_data_manager_factory.mm
index 62851fb..0a34cc2a 100644
--- a/ios/chrome/browser/autofill/model/personal_data_manager_factory.mm
+++ b/ios/chrome/browser/autofill/model/personal_data_manager_factory.mm
@@ -99,7 +99,8 @@
       local_storage, account_storage, chrome_browser_state->GetPrefs(),
       GetApplicationContext()->GetLocalState(),
       IdentityManagerFactory::GetForBrowserState(chrome_browser_state),
-      history_service, sync_service, strike_database, autofill_image_fetcher);
+      history_service, sync_service, strike_database, autofill_image_fetcher,
+      /*shared_storage_handler=*/nullptr);
 
   return service;
 }
diff --git a/ios/chrome/browser/tips_notifications/model/tips_notification_client.h b/ios/chrome/browser/tips_notifications/model/tips_notification_client.h
index 23d3ee3f..bb6594281 100644
--- a/ios/chrome/browser/tips_notifications/model/tips_notification_client.h
+++ b/ios/chrome/browser/tips_notifications/model/tips_notification_client.h
@@ -86,6 +86,10 @@
   // be handled when there is a foreground scene.
   std::optional<TipsNotificationType> interacted_type_;
 
+  // Used to assert that asynchronous callback are invoked on the correct
+  // sequence.
+  SEQUENCE_CHECKER(sequence_checker_);
+
   base::WeakPtrFactory<TipsNotificationClient> weak_ptr_factory_{this};
 };
 
diff --git a/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm b/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm
index 8177392..f79ae19 100644
--- a/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm
+++ b/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm
@@ -67,6 +67,7 @@
 
 void TipsNotificationClient::HandleNotificationInteraction(
     UNNotificationResponse* response) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!IsTipsNotification(response.notification.request)) {
     return;
   }
@@ -87,6 +88,7 @@
 
 void TipsNotificationClient::HandleNotificationInteraction(
     TipsNotificationType type) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   switch (type) {
     case TipsNotificationType::kDefaultBrowser:
       ShowDefaultBrowserPromo();
@@ -102,6 +104,7 @@
 
 UIBackgroundFetchResult TipsNotificationClient::HandleNotificationReception(
     NSDictionary<NSString*, id>* notification) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return UIBackgroundFetchResultNoData;
 }
 
@@ -111,11 +114,13 @@
 }
 
 void TipsNotificationClient::OnSceneActiveForegroundBrowserReady() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   OnSceneActiveForegroundBrowserReady(base::DoNothing());
 }
 
 void TipsNotificationClient::OnSceneActiveForegroundBrowserReady(
     base::OnceClosure closure) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (interacted_type_.has_value()) {
     HandleNotificationInteraction(interacted_type_.value());
     interacted_type_ = std::nullopt;
@@ -134,6 +139,7 @@
 
 void TipsNotificationClient::GetPendingRequest(
     GetPendingRequestCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto completion = base::CallbackToBlock(base::BindPostTask(
       base::SequencedTaskRunner::GetCurrentDefault(),
       base::BindOnce(&NotificationWithIdentifier, kTipsNotificationId)
@@ -144,6 +150,7 @@
 }
 
 void TipsNotificationClient::ClearNotification(base::OnceClosure callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   GetPendingRequest(
       base::BindOnce(&TipsNotificationClient::OnNotificationCleared,
                      weak_ptr_factory_.GetWeakPtr())
@@ -152,6 +159,7 @@
 
 void TipsNotificationClient::OnNotificationCleared(
     UNNotificationRequest* request) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!request) {
     return;
   }
@@ -167,6 +175,7 @@
 }
 
 void TipsNotificationClient::MaybeRequestNotification() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!IsFirstRunRecent(base::Days(14))) {
     return;
   }
@@ -195,6 +204,7 @@
 }
 
 void TipsNotificationClient::RequestNotification(TipsNotificationType type) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   UNNotificationRequest* request = TipsNotificationRequest(type);
 
   auto completion = base::CallbackToBlock(base::BindPostTask(
@@ -209,6 +219,7 @@
 
 void TipsNotificationClient::OnNotificationRequested(TipsNotificationType type,
                                                      NSError* error) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!error) {
     MarkNotificationTypeSent(type);
   }
@@ -217,6 +228,7 @@
 }
 
 bool TipsNotificationClient::ShouldSendNotification(TipsNotificationType type) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   switch (type) {
     case TipsNotificationType::kDefaultBrowser:
       return !IsChromeLikelyDefaultBrowser();
@@ -228,6 +240,7 @@
 }
 
 bool TipsNotificationClient::ShouldSendWhatsNew() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   Browser* browser = GetSceneLevelForegroundActiveBrowser();
   feature_engagement::Tracker* tracker =
       feature_engagement::TrackerFactory::GetForBrowserState(
@@ -237,6 +250,7 @@
 }
 
 bool TipsNotificationClient::ShouldSendSignin() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   Browser* browser = GetSceneLevelForegroundActiveBrowser();
   ChromeBrowserState* browser_state = browser->GetBrowserState();
   AuthenticationService* auth_service =
@@ -247,22 +261,26 @@
 }
 
 bool TipsNotificationClient::IsSceneLevelForegroundActive() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return GetSceneLevelForegroundActiveBrowser() != nullptr;
 }
 
 void TipsNotificationClient::ShowDefaultBrowserPromo() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   Browser* browser = GetSceneLevelForegroundActiveBrowser();
   [HandlerForProtocol(browser->GetCommandDispatcher(), PromosManagerCommands)
       maybeDisplayDefaultBrowserPromo];
 }
 
 void TipsNotificationClient::ShowWhatsNew() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   raw_ptr<Browser> browser = GetSceneLevelForegroundActiveBrowser();
   [HandlerForProtocol(browser->GetCommandDispatcher(),
                       BrowserCoordinatorCommands) showWhatsNew];
 }
 
 void TipsNotificationClient::ShowSignin() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   Browser* browser = GetSceneLevelForegroundActiveBrowser();
   AuthenticationOperation operation = AuthenticationOperation::kSigninAndSync;
   if (base::FeatureList::IsEnabled(
@@ -290,6 +308,7 @@
 
 void TipsNotificationClient::MarkNotificationTypeSent(
     TipsNotificationType type) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   PrefService* local_state = GetApplicationContext()->GetLocalState();
   int sent_bitfield = local_state->GetInteger(kTipsNotificationsSentPref);
   sent_bitfield |= 1 << int(type);
@@ -298,6 +317,7 @@
 
 void TipsNotificationClient::MarkNotificationTypeNotSent(
     TipsNotificationType type) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   PrefService* local_state = GetApplicationContext()->GetLocalState();
   int sent_bitfield = local_state->GetInteger(kTipsNotificationsSentPref);
   sent_bitfield &= ~(1 << int(type));
diff --git a/ios/chrome/browser/ui/autofill/bottom_sheet/payments_suggestion_bottom_sheet_mediator.mm b/ios/chrome/browser/ui/autofill/bottom_sheet/payments_suggestion_bottom_sheet_mediator.mm
index b28d2526..08638505 100644
--- a/ios/chrome/browser/ui/autofill/bottom_sheet/payments_suggestion_bottom_sheet_mediator.mm
+++ b/ios/chrome/browser/ui/autofill/bottom_sheet/payments_suggestion_bottom_sheet_mediator.mm
@@ -194,13 +194,13 @@
           autofill::CreditCard::CreateVirtualCard(*creditCard);
       [creditCardData
           addObject:[[CreditCardData alloc]
-                        initWithCreditCard:&virtualCard
+                        initWithCreditCard:virtualCard
                                       icon:[self
                                                iconForCreditCard:creditCard]]];
     }
     [creditCardData
         addObject:[[CreditCardData alloc]
-                      initWithCreditCard:creditCard
+                      initWithCreditCard:*creditCard
                                     icon:[self iconForCreditCard:creditCard]]];
     hasNonLocalCard |= !autofill::IsCreditCardLocal(*creditCard);
   }
diff --git a/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm b/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm
index ca986e6..755a95c 100644
--- a/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm
+++ b/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm
@@ -29,7 +29,7 @@
   CHECK(controller_);
   CHECK(personal_data_manager_);
   credit_card_data_ =
-      [[CreditCardData alloc] initWithCreditCard:&(controller_->GetCreditCard())
+      [[CreditCardData alloc] initWithCreditCard:controller_->GetCreditCard()
                                             icon:GetCardIcon()];
 }
 
diff --git a/ios/chrome/browser/ui/bubble/bubble_presenter.mm b/ios/chrome/browser/ui/bubble/bubble_presenter.mm
index f3cb2418..fcfe31c 100644
--- a/ios/chrome/browser/ui/bubble/bubble_presenter.mm
+++ b/ios/chrome/browser/ui/bubble/bubble_presenter.mm
@@ -46,6 +46,7 @@
 #import "ios/chrome/common/ui/util/ui_util.h"
 #import "ios/chrome/grit/ios_branded_strings.h"
 #import "ios/chrome/grit/ios_strings.h"
+#import "ios/web/public/navigation/navigation_manager.h"
 #import "ios/web/public/ui/crw_web_view_proxy.h"
 #import "ios/web/public/ui/crw_web_view_scroll_view_proxy.h"
 #import "ios/web/public/web_state.h"
@@ -619,18 +620,37 @@
   if (!userEligible) {
     return;
   }
+
+  web::WebState* currentWebState = self.webStateList->GetActiveWebState();
+  if (currentWebState->GetVisibleURL() == kChromeUINewTabURL) {
+    return;
+  }
+
+  // Retrieve swipe-able directions.
+  const web::NavigationManager* navigationManager =
+      currentWebState->GetNavigationManager();
+  BOOL back = navigationManager->CanGoBack();
+  BOOL forward = navigationManager->CanGoForward();
+  int textId = IDS_IOS_BACK_FORWARD_SWIPE_IPH_BACK_ONLY;
+  if (forward) {
+    textId = back ? IDS_IOS_BACK_FORWARD_SWIPE_IPH
+                  : IDS_IOS_BACK_FORWARD_SWIPE_IPH_FORWARD_ONLY;
+  }
+
   __weak BubblePresenter* weakSelf = self;
-  NSString* text = l10n_util::GetNSString(IDS_IOS_BACK_FORWARD_SWIPE_IPH);
   ProceduralBlock resetSwipeBackForwardGestureIPH = ^{
     weakSelf.swipeBackForwardGestureIPH = nil;
   };
   self.swipeBackForwardGestureIPH = [self
       presentGestureInProductHelpForFeature:backForwardSwipeFeature
-                                  direction:BubbleArrowDirectionLeading
-                                       text:text
+                                  direction:back ? BubbleArrowDirectionLeading
+                                                 : BubbleArrowDirectionTrailing
+                                       text:l10n_util::GetNSString(textId)
                               dismissAction:resetSwipeBackForwardGestureIPH];
-  self.swipeBackForwardGestureIPH.animationRepeatCount = 4;
-  self.swipeBackForwardGestureIPH.bidirectional = YES;
+  if (back && forward) {
+    self.swipeBackForwardGestureIPH.animationRepeatCount = 4;
+    self.swipeBackForwardGestureIPH.bidirectional = YES;
+  }
   [self.swipeBackForwardGestureIPH startAnimation];
 }
 
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index f17c9cb..1612978 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -118,7 +118,6 @@
     "//ios/chrome/browser/ui/content_suggestions/set_up_list:default_browser_promo",
     "//ios/chrome/browser/ui/content_suggestions/set_up_list:utils",
     "//ios/chrome/browser/ui/content_suggestions/tab_resumption",
-    "//ios/chrome/browser/ui/content_suggestions/tab_resumption:helper",
     "//ios/chrome/browser/ui/favicon",
     "//ios/chrome/browser/ui/menu",
     "//ios/chrome/browser/ui/ntp",
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h
index 51cae55..6c2c4ae5 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h
@@ -5,12 +5,6 @@
 #ifndef IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_COMMANDS_H_
 #define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_COMMANDS_H_
 
-@class CollectionViewItem;
-class GURL;
-@class QuerySuggestionConfig;
-enum class SafetyCheckItemType;
-@class TabResumptionItem;
-
 // Commands protocol allowing the ContentSuggestions ViewControllers to interact
 // with the coordinator layer, and from there to the rest of the application.
 @protocol ContentSuggestionsCommands
@@ -19,9 +13,6 @@
 // user to the last opened tab.
 - (void)openMostRecentTab;
 
-// Opens the displayed tab resumption item.
-- (void)openTabResumptionItem:(TabResumptionItem*)item;
-
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_COMMANDS_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
index 210580c5..a2ae19a 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
@@ -75,9 +75,9 @@
 #import "ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_mediator.h"
 #import "ios/chrome/browser/ui/content_suggestions/set_up_list/utils.h"
 #import "ios/chrome/browser/ui/content_suggestions/start_suggest_service_factory.h"
-#import "ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_helper.h"
 #import "ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_helper_delegate.h"
 #import "ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_item.h"
+#import "ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_mediator.h"
 #import "ios/chrome/browser/ui/favicon/favicon_attributes_provider.h"
 #import "ios/chrome/browser/ui/ntp/metrics/home_metrics.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_feature.h"
@@ -86,7 +86,6 @@
 #import "ios/chrome/browser/ui/start_surface/start_surface_util.h"
 #import "ios/chrome/browser/ui/whats_new/whats_new_util.h"
 #import "ios/chrome/browser/url_loading/model/url_loading_browser_agent.h"
-#import "ios/chrome/browser/url_loading/model/url_loading_params.h"
 #import "ios/chrome/grit/ios_strings.h"
 #import "ui/base/l10n/l10n_util_mac.h"
 
@@ -133,8 +132,6 @@
 @implementation ContentSuggestionsMediator {
   // Local State prefs.
   raw_ptr<PrefService> _localState;
-  // Helper class for the tab resumption tile.
-  std::unique_ptr<TabResumptionHelper> _tabResumptionHelper;
   // The latest module ranking returned from the SegmentationService.
   NSArray<NSNumber*>* _magicStackOrderFromSegmentation;
   // YES if the module ranking has been received from the SegmentationService.
@@ -146,6 +143,7 @@
   NSArray<NSNumber*>* _latestMagicStackOrder;
   MostVisitedTilesMediator* _mostVisitedTilesMediator;
   SetUpListMediator* _setUpListMediator;
+  TabResumptionMediator* _tabResumptionMediator;
 }
 
 #pragma mark - Public
@@ -163,6 +161,7 @@
                           browser:(Browser*)browser {
   self = [super init];
   if (self) {
+    _browser = browser;
     _localState = GetApplicationContext()->GetLocalState();
     _articleForYouEnabled =
         prefService->FindPreference(prefs::kArticlesForYouEnabled);
@@ -190,13 +189,13 @@
     }
 
     if (IsTabResumptionEnabled()) {
-      _tabResumptionHelper = std::make_unique<TabResumptionHelper>(
-          browser, identityManager, _localState);
-      _tabResumptionHelper->SetDelegate(self);
+      _tabResumptionMediator =
+          [[TabResumptionMediator alloc] initWithLocalState:_localState
+                                                prefService:prefService
+                                            identityManager:identityManager
+                                                    browser:_browser];
+      _tabResumptionMediator.delegate = self;
     }
-
-    _browser = browser;
-
   }
 
   return self;
@@ -212,10 +211,8 @@
   _setUpListMediator = nil;
   [_mostVisitedTilesMediator disconnect];
   _mostVisitedTilesMediator = nil;
-  if (_tabResumptionHelper) {
-    _tabResumptionHelper->SetDelegate(nullptr);
-    _tabResumptionHelper = nil;
-  }
+  [_tabResumptionMediator disconnect];
+  _tabResumptionMediator = nil;
   _localState = nullptr;
 }
 
@@ -321,31 +318,6 @@
   webStateList->ActivateWebStateAt(index);
 }
 
-- (void)openTabResumptionItem:(TabResumptionItem*)item {
-  [self.contentSuggestionsMetricsRecorder recordTabResumptionTabOpened];
-  tab_resumption_prefs::SetTabResumptionLastOpenedTabURL(
-      item.tabURL, self.browser->GetBrowserState()->GetPrefs());
-  [self logMagicStackEngagementForType:ContentSuggestionsModuleType::
-                                           kTabResumption];
-
-  switch (item.itemType) {
-    case TabResumptionItemType::kLastSyncedTab:
-      [self.NTPMetricsDelegate distantTabResumptionOpened];
-      _tabResumptionHelper->OpenDistantTab();
-      break;
-    case TabResumptionItemType::kMostRecentTab: {
-      [self.NTPMetricsDelegate recentTabTileOpened];
-      web::NavigationManager::WebLoadParams webLoadParams =
-          web::NavigationManager::WebLoadParams(item.tabURL);
-      UrlLoadParams params = UrlLoadParams::SwitchToTab(webLoadParams);
-      params.web_params.transition_type = ui::PAGE_TRANSITION_AUTO_BOOKMARK;
-      UrlLoadingBrowserAgent::FromBrowser(self.browser)->Load(params);
-      break;
-    }
-  }
-  [self removeTabResumptionModule];
-}
-
 #pragma mark - ParcelTrackingMediatorDelegate
 
 - (void)newParcelsAvailable {
@@ -427,7 +399,7 @@
     return;
   }
 
-  [self showTabResumptionWithItem:_tabResumptionHelper->GetTabResumptionItem()];
+  [self showTabResumptionWithItem:_tabResumptionMediator.itemConfig];
 }
 
 - (void)removeTabResumptionModule {
@@ -453,11 +425,9 @@
       _latestMagicStackOrder = [self magicStackOrder];
       [self.consumer setMagicStackOrder:_latestMagicStackOrder];
     }
-    if (IsTabResumptionEnabled() &&
-        _tabResumptionHelper->GetTabResumptionItem()) {
-      TabResumptionItem* item = _tabResumptionHelper->GetTabResumptionItem();
-      item.commandHandler = self;
-      [self.consumer showTabResumptionWithItem:item];
+    if (IsTabResumptionEnabled() && _tabResumptionMediator.itemConfig) {
+      [self.consumer
+          showTabResumptionWithItem:_tabResumptionMediator.itemConfig];
     }
   }
   if (self.returnToRecentTabItem) {
@@ -484,7 +454,8 @@
       !safety_check_prefs::IsSafetyCheckInMagicStackDisabled(_localState) &&
       self.safetyCheckMediator.safetyCheckState.runningState ==
           RunningSafetyCheckState::kDefault) {
-    //    _safetyCheckState.commandhandler = self.presentationDelegate;
+    self.safetyCheckMediator.safetyCheckState.commandhandler =
+        self.presentationDelegate;
     [self.consumer showSafetyCheck:self.safetyCheckMediator.safetyCheckState];
   }
   if (IsIOSParcelTrackingEnabled() &&
@@ -519,7 +490,7 @@
   NSMutableArray* magicStackModules = [NSMutableArray array];
   if (IsTabResumptionEnabled() &&
       !tab_resumption_prefs::IsTabResumptionDisabled(_localState) &&
-      _tabResumptionHelper->GetTabResumptionItem()) {
+      _tabResumptionMediator.itemConfig) {
     [magicStackModules
         addObject:@(int(ContentSuggestionsModuleType::kTabResumption))];
   }
@@ -579,7 +550,7 @@
       case ContentSuggestionsModuleType::kTabResumption:
         if (!IsTabResumptionEnabled() ||
             tab_resumption_prefs::IsTabResumptionDisabled(_localState) ||
-            !_tabResumptionHelper->GetTabResumptionItem()) {
+            !_tabResumptionMediator.itemConfig) {
           break;
         }
         // If ShouldHideIrrelevantModules() is enabled and it is not ranked as
@@ -778,7 +749,6 @@
     return;
   }
 
-  item.commandHandler = self;
   _latestMagicStackOrder =
       base::FeatureList::IsEnabled(
           segmentation_platform::features::kSegmentationPlatformIosModuleRanker)
@@ -850,11 +820,14 @@
 - (void)setNTPMetricsDelegate:(id<NewTabPageMetricsDelegate>)delegate {
   _NTPMetricsDelegate = delegate;
   _mostVisitedTilesMediator.NTPMetricsDelegate = delegate;
+  _tabResumptionMediator.NTPMetricsDelegate = delegate;
 }
 
 - (void)setContentSuggestionsMetricsRecorder:
     (ContentSuggestionsMetricsRecorder*)contentSuggestionsMetricsRecorder {
   _contentSuggestionsMetricsRecorder = contentSuggestionsMetricsRecorder;
+  _tabResumptionMediator.contentSuggestionsMetricsRecorder =
+      contentSuggestionsMetricsRecorder;
 }
 
 - (BOOL)contentSuggestionsEnabled {
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recorder.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recorder.mm
index d13ed54a..ab21fe1f 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recorder.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recorder.mm
@@ -22,6 +22,7 @@
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_constants.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_constants.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_constants.h"
+#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recorder.h"
 #import "ios/chrome/browser/ui/content_suggestions/set_up_list/utils.h"
 #import "ios/chrome/browser/ui/favicon/favicon_attributes_with_payload.h"
 
diff --git a/ios/chrome/browser/ui/content_suggestions/tab_resumption/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/tab_resumption/BUILD.gn
index 5d1d28f..48da7ae4 100644
--- a/ios/chrome/browser/ui/content_suggestions/tab_resumption/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/tab_resumption/BUILD.gn
@@ -4,9 +4,12 @@
 
 source_set("tab_resumption") {
   sources = [
+    "tab_resumption_commands.h",
     "tab_resumption_helper_delegate.h",
     "tab_resumption_item.h",
     "tab_resumption_item.mm",
+    "tab_resumption_mediator.h",
+    "tab_resumption_mediator.mm",
     "tab_resumption_view.h",
     "tab_resumption_view.mm",
   ]
@@ -14,30 +17,12 @@
     ":constants",
     "//base",
     "//components/sessions:session_id",
-    "//components/url_formatter",
-    "//ios/chrome/app/strings",
-    "//ios/chrome/browser/shared/ui/symbols",
-    "//ios/chrome/browser/shared/ui/util",
-    "//ios/chrome/browser/ui/content_suggestions:constants",
-    "//ios/chrome/browser/ui/content_suggestions:public",
-    "//ios/chrome/browser/ui/content_suggestions/magic_stack:public",
-    "//ios/chrome/common/ui/colors",
-    "//ios/chrome/common/ui/util",
-    "//ui/base",
-    "//url",
-  ]
-}
-
-source_set("helper") {
-  sources = [
-    "tab_resumption_helper.h",
-    "tab_resumption_helper.mm",
-  ]
-  deps = [
-    ":tab_resumption",
-    "//base",
+    "//components/signin/public/identity_manager/objc",
+    "//components/sync/base",
     "//components/sync/service",
     "//components/sync_sessions",
+    "//components/url_formatter",
+    "//ios/chrome/app/strings",
     "//ios/chrome/browser/favicon/model",
     "//ios/chrome/browser/metrics/model:metrics_internal",
     "//ios/chrome/browser/ntp/model",
@@ -48,13 +33,26 @@
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/features",
+    "//ios/chrome/browser/shared/ui/symbols",
+    "//ios/chrome/browser/shared/ui/util",
     "//ios/chrome/browser/sync/model",
     "//ios/chrome/browser/synced_sessions/model",
     "//ios/chrome/browser/tabs/model:tab_sync_util",
+    "//ios/chrome/browser/ui/content_suggestions:constants",
+    "//ios/chrome/browser/ui/content_suggestions:metrics",
+    "//ios/chrome/browser/ui/content_suggestions:public",
+    "//ios/chrome/browser/ui/content_suggestions/magic_stack:public",
+    "//ios/chrome/browser/ui/ntp",
     "//ios/chrome/browser/ui/start_surface",
     "//ios/chrome/browser/ui/start_surface:feature_flags",
+    "//ios/chrome/browser/url_loading/model",
+    "//ios/chrome/browser/url_loading/model:url_loading_params_header",
+    "//ios/chrome/common/ui/colors",
     "//ios/chrome/common/ui/favicon",
     "//ios/chrome/common/ui/favicon:favicon_constants",
+    "//ios/chrome/common/ui/util",
+    "//ui/base",
+    "//url",
   ]
 }
 
diff --git a/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_commands.h b/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_commands.h
new file mode 100644
index 0000000..3a06413
--- /dev/null
+++ b/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_commands.h
@@ -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.
+
+#ifndef IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_TAB_RESUMPTION_TAB_RESUMPTION_COMMANDS_H_
+#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_TAB_RESUMPTION_TAB_RESUMPTION_COMMANDS_H_
+
+@class TabResumptionItem;
+
+// Command protocol for events for the Tab Resumption module.
+@protocol TabResumptionCommands
+
+// Opens the displayed tab resumption item.
+- (void)openTabResumptionItem:(TabResumptionItem*)item;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_TAB_RESUMPTION_TAB_RESUMPTION_COMMANDS_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_helper.h b/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_helper.h
deleted file mode 100644
index 8b191da..0000000
--- a/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_helper.h
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_TAB_RESUMPTION_TAB_RESUMPTION_HELPER_H_
-#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_TAB_RESUMPTION_TAB_RESUMPTION_HELPER_H_
-
-#import "base/callback_list.h"
-#import "base/ios/block_types.h"
-#import "base/scoped_observation.h"
-#import "components/sessions/core/session_id.h"
-#include "components/signin/public/identity_manager/identity_manager.h"
-#include "components/sync/service/sync_service_observer.h"
-#import "ios/chrome/browser/favicon/model/favicon_loader.h"
-#import "ios/chrome/browser/ui/start_surface/start_surface_recent_tab_browser_agent.h"
-#import "url/gurl.h"
-
-class Browser;
-class FaviconLoader;
-class PrefService;
-class StartSurfaceRecentTabBrowserAgent;
-@class TabResumptionItem;
-@protocol TabResumptionHelperDelegate;
-
-namespace base {
-class Time;
-}  // namespace base
-
-namespace syncer {
-class SyncService;
-}  // namespace syncer
-
-namespace sync_sessions {
-class SessionSyncService;
-}  // namespace sync_sessions
-
-namespace synced_sessions {
-struct DistantSession;
-struct DistantTab;
-}  // namespace synced_sessions
-
-namespace web {
-class WebState;
-}  // namespace web
-
-// Helper class to control the tab resumption feature.
-class TabResumptionHelper : public signin::IdentityManager::Observer,
-                            public syncer::SyncServiceObserver,
-                            public StartSurfaceRecentTabObserver {
- public:
-  TabResumptionHelper(const TabResumptionHelper&) = delete;
-  TabResumptionHelper& operator=(const TabResumptionHelper&) = delete;
-
-  explicit TabResumptionHelper(Browser* browser,
-                               signin::IdentityManager* identity_manager,
-                               PrefService* local_state);
-
-  ~TabResumptionHelper() override;
-
-  // Tries to fetch the last available TabResumptionItem.
-  void LastTabResumptionItem();
-
-  // Opens the last synced tab from another device.
-  void OpenDistantTab();
-
-  // Sets the delegate for this helper.
-  void SetDelegate(id<TabResumptionHelperDelegate> delegate);
-
-  TabResumptionItem* GetTabResumptionItem() { return tab_resumption_item_; }
-
- private:
-  // Handles signal that the synced session has changed.
-  void ForeignSessionsChanged();
-
-  // Fetches the favicon for the given `item`.
-  void FetchFaviconForItem(TabResumptionItem* item,
-                           FaviconLoader* favicon_loader);
-
-  // Handles the result of a favicon fetch.
-  void OnFaviconForPageUrl(TabResumptionItem* item,
-                           FaviconAttributes* attributes);
-
-  // Creates a TabResumptionItem corresponding to the last active distant tab.
-  void LastSyncedTabItemFromLastActiveDistantTab(
-      const synced_sessions::DistantSession* session,
-      const synced_sessions::DistantTab* tab,
-      FaviconLoader* favicon_loader);
-
-  // Creates a TabResumptionItem corresponding to the last synced tab.
-  void MostRecentTabItemFromWebState(web::WebState* web_state,
-                                     base::Time opened_time,
-                                     FaviconLoader* favicon_loader);
-
-  // syncer::SyncServiceObserver.
-  void OnStateChanged(syncer::SyncService* sync) override;
-  // signin::IdentityManager::Observer.
-  void OnPrimaryAccountChanged(
-      const signin::PrimaryAccountChangeEvent& event_details) override;
-  // StartSurfaceBrowserAgentObserver.
-  void MostRecentTabRemoved(web::WebState* web_state) override;
-  void MostRecentTabFaviconUpdated(web::WebState* web_state,
-                                   UIImage* image) override {}
-  void MostRecentTabTitleUpdated(web::WebState* web_state,
-                                 const std::u16string& title) override {}
-
-  // Bool that tracks if a most recent tab item can be displayed.
-  bool can_show_most_recent_item_ = true;
-  // Last distant tab resumption item URL.
-  GURL last_distant_item_url_;
-
-  // Tab identifier of the last distant tab resumption item.
-  SessionID tab_id_ = SessionID::InvalidValue();
-  // Session tag of the last distant tab resumption item.
-  std::string session_tag_;
-
-  // The owning Browser.
-  raw_ptr<Browser> browser_ = nullptr;
-  raw_ptr<PrefService> local_state_ = nullptr;
-  // Loads favicons.
-  raw_ptr<FaviconLoader> favicon_loader_ = nullptr;
-  // Browser Agent that manages the most recent WebState.
-  raw_ptr<StartSurfaceRecentTabBrowserAgent> recent_tab_browser_agent_ =
-      nullptr;
-  // KeyedService responsible session sync.
-  raw_ptr<sync_sessions::SessionSyncService> session_sync_service_ = nullptr;
-  // KeyedService responsible for sync state.
-  raw_ptr<syncer::SyncService> sync_service_ = nullptr;
-  // CallbackListSubscription for the SessionSyncService method.
-  base::CallbackListSubscription foreign_session_updated_subscription_;
-  // The latest state of the item config for the Tab Resumption module.
-  TabResumptionItem* tab_resumption_item_ = nullptr;
-  base::ScopedObservation<syncer::SyncService, syncer::SyncServiceObserver>
-      scoped_observation_{this};
-  base::ScopedObservation<signin::IdentityManager,
-                          signin::IdentityManager::Observer>
-      identity_manager_observer_{this};
-  base::ScopedObservation<StartSurfaceRecentTabBrowserAgent,
-                          StartSurfaceRecentTabObserver>
-      start_surface_recent_tab_observer_{this};
-  // The delegate for this helper class.
-  __weak id<TabResumptionHelperDelegate> delegate_ = nullptr;
-  base::WeakPtrFactory<TabResumptionHelper> weak_ptr_factory_{this};
-};
-
-#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_TAB_RESUMPTION_TAB_RESUMPTION_HELPER_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_helper.mm b/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_helper.mm
deleted file mode 100644
index fa02208..0000000
--- a/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_helper.mm
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_helper.h"
-
-#import "base/apple/foundation_util.h"
-#import "base/strings/sys_string_conversions.h"
-#import "components/sync/base/user_selectable_type.h"
-#import "components/sync/service/sync_service.h"
-#import "components/sync/service/sync_user_settings.h"
-#import "components/sync_sessions/open_tabs_ui_delegate.h"
-#import "components/sync_sessions/session_sync_service.h"
-#import "ios/chrome/browser/favicon/model/favicon_loader.h"
-#import "ios/chrome/browser/favicon/model/ios_chrome_favicon_loader_factory.h"
-#import "ios/chrome/browser/metrics/model/new_tab_page_uma.h"
-#import "ios/chrome/browser/ntp/model/new_tab_page_tab_helper.h"
-#import "ios/chrome/browser/ntp_tiles/model/tab_resumption/tab_resumption_prefs.h"
-#import "ios/chrome/browser/sessions/session_util.h"
-#import "ios/chrome/browser/shared/model/browser/browser.h"
-#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
-#import "ios/chrome/browser/shared/model/url/chrome_url_constants.h"
-#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
-#import "ios/chrome/browser/shared/model/web_state_list/web_state_opener.h"
-#import "ios/chrome/browser/shared/public/features/features.h"
-#import "ios/chrome/browser/sync/model/session_sync_service_factory.h"
-#import "ios/chrome/browser/sync/model/sync_service_factory.h"
-#import "ios/chrome/browser/synced_sessions/model/distant_session.h"
-#import "ios/chrome/browser/synced_sessions/model/distant_tab.h"
-#import "ios/chrome/browser/synced_sessions/model/synced_sessions.h"
-#import "ios/chrome/browser/tabs/model/tab_sync_util.h"
-#import "ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_helper_delegate.h"
-#import "ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_item.h"
-#import "ios/chrome/browser/ui/start_surface/start_surface_features.h"
-#import "ios/chrome/browser/ui/start_surface/start_surface_recent_tab_browser_agent.h"
-#import "ios/chrome/browser/ui/start_surface/start_surface_util.h"
-#import "ios/chrome/common/ui/favicon/favicon_attributes.h"
-#import "ios/chrome/common/ui/favicon/favicon_constants.h"
-
-namespace {
-
-// The key to store the timestamp when the scene enters into background.
-NSString* kStartSurfaceSceneEnterIntoBackgroundTime =
-    @"StartSurfaceSceneEnterIntoBackgroundTime";
-
-}  // namespace
-
-TabResumptionHelper::TabResumptionHelper(
-    Browser* browser,
-    signin::IdentityManager* identity_manager,
-    PrefService* local_state)
-    : browser_(browser), local_state_(local_state) {
-  CHECK(browser_);
-  CHECK(IsTabResumptionEnabled());
-
-  ChromeBrowserState* browser_state = browser_->GetBrowserState();
-  session_sync_service_ =
-      SessionSyncServiceFactory::GetForBrowserState(browser_state);
-  sync_service_ = SyncServiceFactory::GetForBrowserState(browser_state);
-  favicon_loader_ =
-      IOSChromeFaviconLoaderFactory::GetForBrowserState(browser_state);
-  recent_tab_browser_agent_ =
-      StartSurfaceRecentTabBrowserAgent::FromBrowser(browser_);
-  start_surface_recent_tab_observer_.Observe(recent_tab_browser_agent_);
-
-  if (!IsTabResumptionEnabledForMostRecentTabOnly()) {
-    foreign_session_updated_subscription_ =
-        session_sync_service_->SubscribeToForeignSessionsChanged(
-            base::BindRepeating(&TabResumptionHelper::ForeignSessionsChanged,
-                                base::Unretained(this)));
-    scoped_observation_.Observe(sync_service_);
-    identity_manager_observer_.Observe(identity_manager);
-  }
-}
-
-TabResumptionHelper::~TabResumptionHelper() = default;
-
-#pragma mark - Public methods
-
-void TabResumptionHelper::LastTabResumptionItem() {
-  if (tab_resumption_prefs::IsTabResumptionDisabled(local_state_)) {
-    return;
-  }
-
-  session_tag_ = "";
-  tab_id_ = SessionID::InvalidValue();
-
-  if (!IsTabResumptionEnabledForMostRecentTabOnly()) {
-    // If sync is enabled and `GetOpenTabsUIDelegate()` returns nullptr, that
-    // means the `session_sync_service_` is not fully operational.
-    if (sync_service_->IsSyncFeatureEnabled() &&
-        !session_sync_service_->GetOpenTabsUIDelegate()) {
-      return;
-    }
-  }
-
-  base::Time most_recent_tab_opened_time = base::Time::UnixEpoch();
-  base::Time last_synced_tab_synced_time = base::Time::UnixEpoch();
-
-  web::WebState* most_recent_tab = recent_tab_browser_agent_->most_recent_tab();
-  if (most_recent_tab) {
-    SceneState* scene = browser_->GetSceneState();
-    NSDate* most_recent_tab_date = base::apple::ObjCCastStrict<NSDate>(
-        [scene sessionObjectForKey:kStartSurfaceSceneEnterIntoBackgroundTime]);
-    if (most_recent_tab_date != nil) {
-      most_recent_tab_opened_time =
-          base::Time::FromNSDate(most_recent_tab_date);
-    }
-  }
-
-  const synced_sessions::DistantSession* session = nullptr;
-  const synced_sessions::DistantTab* tab = nullptr;
-  auto const synced_sessions =
-      std::make_unique<synced_sessions::SyncedSessions>(session_sync_service_);
-  if (!IsTabResumptionEnabledForMostRecentTabOnly()) {
-    LastActiveDistantTab last_distant_tab = GetLastActiveDistantTab(
-        synced_sessions.get(), TabResumptionForXDevicesTimeThreshold());
-    if (last_distant_tab.tab) {
-      tab = last_distant_tab.tab;
-      if (last_distant_item_url_ != tab->virtual_url) {
-        last_distant_item_url_ = tab->virtual_url;
-        session = last_distant_tab.session;
-        last_synced_tab_synced_time = tab->last_active_time;
-      }
-    }
-  }
-
-  web::WebState* active_web_state =
-      browser_->GetWebStateList()->GetActiveWebState();
-  bool can_show_most_recent_item =
-      NewTabPageTabHelper::FromWebState(active_web_state)
-          ->ShouldShowStartSurface();
-  // If both times have not been updated, that means there is no item to return.
-  if (most_recent_tab_opened_time == base::Time::UnixEpoch() &&
-      last_synced_tab_synced_time == base::Time::UnixEpoch()) {
-    return;
-  } else if (last_synced_tab_synced_time > most_recent_tab_opened_time) {
-    CHECK(!IsTabResumptionEnabledForMostRecentTabOnly());
-    LastSyncedTabItemFromLastActiveDistantTab(session, tab, favicon_loader_);
-    session_tag_ = session->tag;
-    tab_id_ = tab->tab_id;
-  } else if (can_show_most_recent_item) {
-    MostRecentTabItemFromWebState(most_recent_tab, most_recent_tab_opened_time,
-                                  favicon_loader_);
-  }
-}
-
-void TabResumptionHelper::OpenDistantTab() {
-  ChromeBrowserState* browser_state = browser_->GetBrowserState();
-  WebStateList* web_state_list = browser_->GetWebStateList();
-
-  sync_sessions::OpenTabsUIDelegate* open_tabs_delegate =
-      SessionSyncServiceFactory::GetForBrowserState(browser_state)
-          ->GetOpenTabsUIDelegate();
-
-  const sessions::SessionTab* session_tab = nullptr;
-  if (open_tabs_delegate->GetForeignTab(session_tag_, tab_id_, &session_tab)) {
-    bool is_ntp = web_state_list->GetActiveWebState()->GetVisibleURL() ==
-                  kChromeUINewTabURL;
-    new_tab_page_uma::RecordNTPAction(
-        browser_state->IsOffTheRecord(), is_ntp,
-        new_tab_page_uma::ACTION_OPENED_FOREIGN_SESSION);
-
-    std::unique_ptr<web::WebState> web_state =
-        session_util::CreateWebStateWithNavigationEntries(
-            browser_state, session_tab->current_navigation_index,
-            session_tab->navigations);
-    web_state_list->ReplaceWebStateAt(web_state_list->active_index(),
-                                      std::move(web_state));
-  }
-}
-
-void TabResumptionHelper::SetDelegate(
-    id<TabResumptionHelperDelegate> delegate) {
-  delegate_ = delegate;
-  if (delegate_) {
-    LastTabResumptionItem();
-  }
-}
-
-void TabResumptionHelper::OnStateChanged(syncer::SyncService* sync) {
-  // If tabs are not synced, hide the tab resumption tile.
-  if (!sync_service_->GetUserSettings()->GetSelectedTypes().Has(
-          syncer::UserSelectableType::kTabs)) {
-    [delegate_ removeTabResumptionModule];
-  }
-}
-
-void TabResumptionHelper::OnPrimaryAccountChanged(
-    const signin::PrimaryAccountChangeEvent& event_details) {
-  switch (event_details.GetEventTypeFor(signin::ConsentLevel::kSignin)) {
-    case signin::PrimaryAccountChangeEvent::Type::kCleared: {
-      // If the user is signed out, remove the tab resumption tile.
-      [delegate_ removeTabResumptionModule];
-      break;
-    }
-    default:
-      break;
-  }
-}
-
-#pragma mark - Private
-
-void TabResumptionHelper::ForeignSessionsChanged() {
-  LastTabResumptionItem();
-}
-
-void TabResumptionHelper::FetchFaviconForItem(TabResumptionItem* item,
-                                              FaviconLoader* favicon_loader) {
-  favicon_loader->FaviconForPageUrl(
-      item.tabURL, kDesiredSmallFaviconSizePt, kMinFaviconSizePt,
-      /*fallback_to_google_server=*/true,
-      base::CallbackToBlock(
-          base::BindRepeating(&TabResumptionHelper::OnFaviconForPageUrl,
-                              weak_ptr_factory_.GetWeakPtr(), item)));
-}
-
-void TabResumptionHelper::OnFaviconForPageUrl(TabResumptionItem* item,
-                                              FaviconAttributes* attributes) {
-  if (!attributes.usesDefaultImage) {
-    item.faviconImage = attributes.faviconImage;
-    tab_resumption_item_ = item;
-    [delegate_ tabResumptionHelperDidReceiveItem];
-  }
-}
-
-void TabResumptionHelper::LastSyncedTabItemFromLastActiveDistantTab(
-    const synced_sessions::DistantSession* session,
-    const synced_sessions::DistantTab* tab,
-    FaviconLoader* favicon_loader) {
-  CHECK(!IsTabResumptionEnabledForMostRecentTabOnly());
-
-  TabResumptionItem* tab_resumption_item = [[TabResumptionItem alloc]
-      initWithItemType:TabResumptionItemType::kLastSyncedTab];
-  tab_resumption_item.sessionName = base::SysUTF8ToNSString(session->name);
-  tab_resumption_item.tabTitle = base::SysUTF16ToNSString(tab->title);
-  tab_resumption_item.syncedTime = tab->last_active_time;
-  tab_resumption_item.tabURL = tab->virtual_url;
-
-  // Fetch the favicon.
-  FetchFaviconForItem(tab_resumption_item, favicon_loader);
-}
-
-void TabResumptionHelper::MostRecentTabItemFromWebState(
-    web::WebState* web_state,
-    base::Time opened_time,
-    FaviconLoader* favicon_loader) {
-  TabResumptionItem* tab_resumption_item = [[TabResumptionItem alloc]
-      initWithItemType:TabResumptionItemType::kMostRecentTab];
-  tab_resumption_item.tabTitle =
-      base::SysUTF16ToNSString(web_state->GetTitle());
-  tab_resumption_item.syncedTime = opened_time;
-  tab_resumption_item.tabURL = web_state->GetLastCommittedURL();
-
-  // Fetch the favicon.
-  FetchFaviconForItem(tab_resumption_item, favicon_loader);
-}
-
-void TabResumptionHelper::MostRecentTabRemoved(web::WebState* web_state) {
-  if (tab_resumption_item_ && tab_resumption_item_.itemType == kMostRecentTab) {
-    [delegate_ removeTabResumptionModule];
-  }
-}
diff --git a/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_helper_delegate.h b/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_helper_delegate.h
index 7d2ed48..47260f5 100644
--- a/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_helper_delegate.h
+++ b/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_helper_delegate.h
@@ -7,6 +7,8 @@
 
 #import <Foundation/Foundation.h>
 
+enum class ContentSuggestionsModuleType;
+
 // Delegate handling events from the TabResumptionHelper.
 @protocol TabResumptionHelperDelegate
 
@@ -16,6 +18,9 @@
 // Signals that the Tab Resumption module should be removed.
 - (void)removeTabResumptionModule;
 
+// Logs a user Magic Stack engagement for module `type`.
+- (void)logMagicStackEngagementForType:(ContentSuggestionsModuleType)type;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_TAB_RESUMPTION_TAB_RESUMPTION_HELPER_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_item.h b/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_item.h
index e2fbad9..d6b1aa6 100644
--- a/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_item.h
+++ b/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_item.h
@@ -9,7 +9,7 @@
 
 #import "ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module.h"
 
-@protocol ContentSuggestionsCommands;
+@protocol TabResumptionCommands;
 
 namespace base {
 class Time;
@@ -47,7 +47,7 @@
 @property(nonatomic, strong) UIImage* faviconImage;
 
 // Command handler for user actions.
-@property(nonatomic, weak) id<ContentSuggestionsCommands> commandHandler;
+@property(nonatomic, weak) id<TabResumptionCommands> commandHandler;
 
 // The Item's designated initializer.
 - (instancetype)initWithItemType:(TabResumptionItemType)itemType
diff --git a/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_mediator.h b/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_mediator.h
new file mode 100644
index 0000000..85bb9ed3
--- /dev/null
+++ b/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_mediator.h
@@ -0,0 +1,50 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_TAB_RESUMPTION_TAB_RESUMPTION_MEDIATOR_H_
+#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_TAB_RESUMPTION_TAB_RESUMPTION_MEDIATOR_H_
+
+#import <UIKit/UIKit.h>
+
+class Browser;
+@class ContentSuggestionsMetricsRecorder;
+@protocol NewTabPageMetricsDelegate;
+class PrefService;
+@protocol TabResumptionHelperDelegate;
+@class TabResumptionItem;
+
+namespace signin {
+class IdentityManager;
+}
+
+// Mediator for managing the state of the TabResumption Magic Stack module.
+@interface TabResumptionMediator : NSObject
+
+// The latest state of the item config for the Tab Resumption module.
+@property(nonatomic, strong, readonly) TabResumptionItem* itemConfig;
+
+// The delegate for this helper class.
+@property(nonatomic, weak) id<TabResumptionHelperDelegate> delegate;
+
+// Delegate for reporting content suggestions actions to the NTP metrics
+// recorder.
+@property(nonatomic, weak) id<NewTabPageMetricsDelegate> NTPMetricsDelegate;
+
+// Recorder for content suggestions metrics.
+@property(nonatomic, weak)
+    ContentSuggestionsMetricsRecorder* contentSuggestionsMetricsRecorder;
+
+// Default initializer.
+- (instancetype)initWithLocalState:(PrefService*)localState
+                       prefService:(PrefService*)prefService
+                   identityManager:(signin::IdentityManager*)identityManager
+                           browser:(Browser*)browser NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+- (void)disconnect;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_TAB_RESUMPTION_TAB_RESUMPTION_MEDIATOR_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_mediator.mm b/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_mediator.mm
new file mode 100644
index 0000000..043e3b1
--- /dev/null
+++ b/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_mediator.mm
@@ -0,0 +1,378 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_mediator.h"
+
+#import "base/apple/foundation_util.h"
+#import "base/memory/raw_ptr.h"
+#import "base/strings/sys_string_conversions.h"
+#import "components/sessions/core/session_id.h"
+#import "components/signin/public/identity_manager/objc/identity_manager_observer_bridge.h"
+#import "components/sync/base/user_selectable_type.h"
+#import "components/sync/service/sync_service.h"
+#import "components/sync/service/sync_user_settings.h"
+#import "components/sync_sessions/open_tabs_ui_delegate.h"
+#import "components/sync_sessions/session_sync_service.h"
+#import "ios/chrome/browser/favicon/model/favicon_loader.h"
+#import "ios/chrome/browser/favicon/model/ios_chrome_favicon_loader_factory.h"
+#import "ios/chrome/browser/metrics/model/new_tab_page_uma.h"
+#import "ios/chrome/browser/ntp/model/new_tab_page_tab_helper.h"
+#import "ios/chrome/browser/ntp_tiles/model/tab_resumption/tab_resumption_prefs.h"
+#import "ios/chrome/browser/sessions/session_util.h"
+#import "ios/chrome/browser/shared/model/browser/browser.h"
+#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/shared/model/url/chrome_url_constants.h"
+#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
+#import "ios/chrome/browser/shared/model/web_state_list/web_state_opener.h"
+#import "ios/chrome/browser/shared/public/features/features.h"
+#import "ios/chrome/browser/sync/model/session_sync_service_factory.h"
+#import "ios/chrome/browser/sync/model/sync_observer_bridge.h"
+#import "ios/chrome/browser/sync/model/sync_service_factory.h"
+#import "ios/chrome/browser/synced_sessions/model/distant_session.h"
+#import "ios/chrome/browser/synced_sessions/model/distant_tab.h"
+#import "ios/chrome/browser/synced_sessions/model/synced_sessions.h"
+#import "ios/chrome/browser/synced_sessions/model/synced_sessions_bridge.h"
+#import "ios/chrome/browser/tabs/model/tab_sync_util.h"
+#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_constants.h"
+#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recorder.h"
+#import "ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_commands.h"
+#import "ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_helper_delegate.h"
+#import "ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_item.h"
+#import "ios/chrome/browser/ui/ntp/new_tab_page_metrics_delegate.h"
+#import "ios/chrome/browser/ui/start_surface/start_surface_features.h"
+#import "ios/chrome/browser/ui/start_surface/start_surface_recent_tab_browser_agent.h"
+#import "ios/chrome/browser/ui/start_surface/start_surface_recent_tab_removal_observer_bridge.h"
+#import "ios/chrome/browser/ui/start_surface/start_surface_util.h"
+#import "ios/chrome/browser/url_loading/model/url_loading_browser_agent.h"
+#import "ios/chrome/browser/url_loading/model/url_loading_params.h"
+#import "ios/chrome/common/ui/favicon/favicon_attributes.h"
+#import "ios/chrome/common/ui/favicon/favicon_constants.h"
+
+namespace {
+
+// The key to store the timestamp when the scene enters into background.
+NSString* kStartSurfaceSceneEnterIntoBackgroundTime =
+    @"StartSurfaceSceneEnterIntoBackgroundTime";
+
+}  // namespace
+
+@interface TabResumptionMediator () <StartSurfaceRecentTabObserving,
+                                     SyncedSessionsObserver,
+                                     IdentityManagerObserverBridgeDelegate,
+                                     SyncObserverModelBridge,
+                                     TabResumptionCommands>
+// readwrite override.
+@property(nonatomic, strong, readwrite) TabResumptionItem* itemConfig;
+
+@end
+
+@implementation TabResumptionMediator {
+  // Last distant tab resumption item URL.
+  GURL _lastDistinctItemURL;
+  // Tab identifier of the last distant tab resumption item.
+  std::optional<SessionID> _tabId;
+  // Session tag of the last distant tab resumption item.
+  std::string _sessionTag;
+  BOOL _isOffTheRecord;
+
+  // The owning Browser.
+  raw_ptr<Browser> _browser;
+  raw_ptr<PrefService> _localState;
+  raw_ptr<PrefService> _browserStatePrefs;
+  SceneState* _sceneState;
+  // Loads favicons.
+  raw_ptr<FaviconLoader> _faviconLoader;
+  // Browser Agent that manages the most recent WebState.
+  raw_ptr<StartSurfaceRecentTabBrowserAgent> _recentTabBrowserAgent;
+  // KeyedService responsible session sync.
+  raw_ptr<sync_sessions::SessionSyncService> _sessionSyncService;
+  // KeyedService responsible for sync state.
+  raw_ptr<syncer::SyncService> _syncService;
+  raw_ptr<UrlLoadingBrowserAgent> _URLLoadingBrowserAgent;
+  raw_ptr<WebStateList> _webStateList;
+  // Observer bridge for mediator to listen to
+  // StartSurfaceRecentTabObserverBridge.
+  std::unique_ptr<StartSurfaceRecentTabObserverBridge> _startSurfaceObserver;
+
+  std::unique_ptr<SyncObserverBridge> _syncObserverModelBridge;
+  // Observer for changes to the user's identity state.
+  std::unique_ptr<signin::IdentityManagerObserverBridge>
+      _identityManagerObserverBridge;
+  std::unique_ptr<synced_sessions::SyncedSessionsObserverBridge>
+      _syncedSessionsObserverBridge;
+}
+
+- (instancetype)initWithLocalState:(PrefService*)localState
+                       prefService:(PrefService*)prefService
+                   identityManager:(signin::IdentityManager*)identityManager
+                           browser:(Browser*)browser {
+  self = [super init];
+  if (self) {
+    CHECK(IsTabResumptionEnabled());
+    _localState = localState;
+    _browserStatePrefs = prefService;
+    _browser = browser;
+    _tabId = SessionID::InvalidValue();
+    _sceneState = _browser->GetSceneState();
+    _webStateList = _browser->GetWebStateList();
+    _isOffTheRecord = _browser->GetBrowserState()->IsOffTheRecord();
+
+    ChromeBrowserState* browserState = _browser->GetBrowserState();
+    _sessionSyncService =
+        SessionSyncServiceFactory::GetForBrowserState(browserState);
+    _syncService = SyncServiceFactory::GetForBrowserState(browserState);
+    _faviconLoader =
+        IOSChromeFaviconLoaderFactory::GetForBrowserState(browserState);
+    _recentTabBrowserAgent =
+        StartSurfaceRecentTabBrowserAgent::FromBrowser(_browser);
+    _URLLoadingBrowserAgent = UrlLoadingBrowserAgent::FromBrowser(_browser);
+    _startSurfaceObserver =
+        std::make_unique<StartSurfaceRecentTabObserverBridge>(self);
+    StartSurfaceRecentTabBrowserAgent::FromBrowser(_browser)->AddObserver(
+        _startSurfaceObserver.get());
+
+    if (!IsTabResumptionEnabledForMostRecentTabOnly()) {
+      _syncedSessionsObserverBridge.reset(
+          new synced_sessions::SyncedSessionsObserverBridge(
+              self, _sessionSyncService));
+
+      _syncObserverModelBridge.reset(
+          new SyncObserverBridge(self, _syncService));
+      _identityManagerObserverBridge.reset(
+          new signin::IdentityManagerObserverBridge(identityManager, self));
+    }
+  }
+  return self;
+}
+
+- (void)disconnect {
+  _syncedSessionsObserverBridge.reset();
+  if (_startSurfaceObserver) {
+    _recentTabBrowserAgent->RemoveObserver(_startSurfaceObserver.get());
+    _startSurfaceObserver.reset();
+  }
+  _recentTabBrowserAgent = nullptr;
+  _syncObserverModelBridge.reset();
+  _identityManagerObserverBridge.reset();
+}
+
+#pragma mark - Public methods
+
+- (void)openTabResumptionItem:(TabResumptionItem*)item {
+  [self.contentSuggestionsMetricsRecorder recordTabResumptionTabOpened];
+  tab_resumption_prefs::SetTabResumptionLastOpenedTabURL(item.tabURL,
+                                                         _browserStatePrefs);
+  [self.delegate logMagicStackEngagementForType:ContentSuggestionsModuleType::
+                                                    kTabResumption];
+
+  switch (item.itemType) {
+    case TabResumptionItemType::kLastSyncedTab:
+      [self.NTPMetricsDelegate distantTabResumptionOpened];
+      [self openDistantTab];
+      break;
+    case TabResumptionItemType::kMostRecentTab: {
+      [self.NTPMetricsDelegate recentTabTileOpened];
+      web::NavigationManager::WebLoadParams webLoadParams =
+          web::NavigationManager::WebLoadParams(item.tabURL);
+      UrlLoadParams params = UrlLoadParams::SwitchToTab(webLoadParams);
+      params.web_params.transition_type = ui::PAGE_TRANSITION_AUTO_BOOKMARK;
+      _URLLoadingBrowserAgent->Load(params);
+      break;
+    }
+  }
+  [self.delegate removeTabResumptionModule];
+}
+
+- (void)openDistantTab {
+  ChromeBrowserState* browserState = _browser->GetBrowserState();
+  sync_sessions::OpenTabsUIDelegate* openTabsDelegate =
+      SessionSyncServiceFactory::GetForBrowserState(browserState)
+          ->GetOpenTabsUIDelegate();
+  const sessions::SessionTab* sessionTab = nullptr;
+  if (openTabsDelegate->GetForeignTab(_sessionTag, _tabId.value(),
+                                      &sessionTab)) {
+    bool isNTP = _webStateList->GetActiveWebState()->GetVisibleURL() ==
+                 kChromeUINewTabURL;
+    new_tab_page_uma::RecordNTPAction(
+        _isOffTheRecord, isNTP,
+        new_tab_page_uma::ACTION_OPENED_FOREIGN_SESSION);
+
+    std::unique_ptr<web::WebState> webState =
+        session_util::CreateWebStateWithNavigationEntries(
+            browserState, sessionTab->current_navigation_index,
+            sessionTab->navigations);
+    _webStateList->ReplaceWebStateAt(_webStateList->active_index(),
+                                     std::move(webState));
+  }
+}
+
+- (void)setDelegate:(id<TabResumptionHelperDelegate>)delegate {
+  _delegate = delegate;
+  if (_delegate) {
+    [self fetchLastTabResumptionItem];
+  }
+}
+
+#pragma mark - SyncObserverBridge
+
+- (void)onSyncStateChanged {
+  // If tabs are not synced, hide the tab resumption tile.
+  if (!_syncService->GetUserSettings()->GetSelectedTypes().Has(
+          syncer::UserSelectableType::kTabs)) {
+    [self.delegate removeTabResumptionModule];
+  }
+}
+
+#pragma mark - IdentityManagerObserverBridgeDelegate
+
+- (void)onPrimaryAccountChanged:
+    (const signin::PrimaryAccountChangeEvent&)event {
+  switch (event.GetEventTypeFor(signin::ConsentLevel::kSignin)) {
+    case signin::PrimaryAccountChangeEvent::Type::kCleared: {
+      // If the user is signed out, remove the tab resumption tile.
+      [self.delegate removeTabResumptionModule];
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+#pragma mark - SyncedSessionsObserver
+
+- (void)onForeignSessionsChanged {
+  [self fetchLastTabResumptionItem];
+}
+
+#pragma mark - StartSurfaceRecentTabObserving
+
+- (void)mostRecentTabWasRemoved:(web::WebState*)webState {
+  if (self.itemConfig && self.itemConfig.itemType == kMostRecentTab) {
+    [_delegate removeTabResumptionModule];
+  }
+}
+
+- (void)mostRecentTab:(web::WebState*)webState
+    faviconUpdatedWithImage:(UIImage*)image {
+}
+
+- (void)mostRecentTab:(web::WebState*)webState
+      titleWasUpdated:(NSString*)title {
+}
+
+#pragma mark - Private
+
+- (void)fetchLastTabResumptionItem {
+  if (tab_resumption_prefs::IsTabResumptionDisabled(_localState)) {
+    return;
+  }
+
+  _sessionTag = "";
+  _tabId = SessionID::InvalidValue();
+
+  if (!IsTabResumptionEnabledForMostRecentTabOnly()) {
+    // If sync is enabled and `GetOpenTabsUIDelegate()` returns nullptr, that
+    // means the `_sessionSyncService` is not fully operational.
+    if (_syncService->IsSyncFeatureEnabled() &&
+        !_sessionSyncService->GetOpenTabsUIDelegate()) {
+      return;
+    }
+  }
+
+  base::Time mostRecentTabOpenedTime = base::Time::UnixEpoch();
+  base::Time lastSyncedTabSyncedTime = base::Time::UnixEpoch();
+
+  web::WebState* mostRecentTab = _recentTabBrowserAgent->most_recent_tab();
+  if (mostRecentTab) {
+    NSDate* mostRecentTabDate = base::apple::ObjCCastStrict<NSDate>([_sceneState
+        sessionObjectForKey:kStartSurfaceSceneEnterIntoBackgroundTime]);
+    if (mostRecentTabDate != nil) {
+      mostRecentTabOpenedTime = base::Time::FromNSDate(mostRecentTabDate);
+    }
+  }
+
+  const synced_sessions::DistantSession* session = nullptr;
+  const synced_sessions::DistantTab* tab = nullptr;
+  auto const syncedSessions =
+      std::make_unique<synced_sessions::SyncedSessions>(_sessionSyncService);
+  if (!IsTabResumptionEnabledForMostRecentTabOnly()) {
+    LastActiveDistantTab lastDistantTab = GetLastActiveDistantTab(
+        syncedSessions.get(), TabResumptionForXDevicesTimeThreshold());
+    if (lastDistantTab.tab) {
+      tab = lastDistantTab.tab;
+      if (_lastDistinctItemURL != tab->virtual_url) {
+        _lastDistinctItemURL = tab->virtual_url;
+        session = lastDistantTab.session;
+        lastSyncedTabSyncedTime = tab->last_active_time;
+      }
+    }
+  }
+
+  web::WebState* activeWebState = _webStateList->GetActiveWebState();
+  bool canShowMostRecentItem = NewTabPageTabHelper::FromWebState(activeWebState)
+                                   ->ShouldShowStartSurface();
+  // If both times have not been updated, that means there is no item to return.
+  if (mostRecentTabOpenedTime == base::Time::UnixEpoch() &&
+      lastSyncedTabSyncedTime == base::Time::UnixEpoch()) {
+    return;
+  } else if (lastSyncedTabSyncedTime > mostRecentTabOpenedTime) {
+    CHECK(!IsTabResumptionEnabledForMostRecentTabOnly());
+    [self fetchLastSyncedTabItemFromLastActiveDistantTab:tab session:session];
+    _sessionTag = session->tag;
+    _tabId = tab->tab_id;
+  } else if (canShowMostRecentItem) {
+    [self fetchMostRecentTabItemFromWebState:mostRecentTab
+                                  openedTime:mostRecentTabOpenedTime];
+  }
+}
+
+- (void)fetchFaviconForItem:(TabResumptionItem*)item {
+  __weak TabResumptionMediator* weakSelf = self;
+  _faviconLoader->FaviconForPageUrl(
+      item.tabURL, kDesiredSmallFaviconSizePt, kMinFaviconSizePt,
+      /*fallback_to_google_server=*/true, ^(FaviconAttributes* attributes) {
+        TabResumptionMediator* strongSelf = weakSelf;
+        if (strongSelf && !attributes.usesDefaultImage) {
+          item.faviconImage = attributes.faviconImage;
+          strongSelf.itemConfig = item;
+          [strongSelf.delegate tabResumptionHelperDidReceiveItem];
+        }
+      });
+}
+
+- (void)fetchLastSyncedTabItemFromLastActiveDistantTab:
+            (const synced_sessions::DistantTab*)tab
+                                               session:(const synced_sessions::
+                                                            DistantSession*)
+                                                           session {
+  CHECK(!IsTabResumptionEnabledForMostRecentTabOnly());
+
+  TabResumptionItem* item = [[TabResumptionItem alloc]
+      initWithItemType:TabResumptionItemType::kLastSyncedTab];
+  item.sessionName = base::SysUTF8ToNSString(session->name);
+  item.tabTitle = base::SysUTF16ToNSString(tab->title);
+  item.syncedTime = tab->last_active_time;
+  item.tabURL = tab->virtual_url;
+  item.commandHandler = self;
+
+  // Fetch the favicon.
+  [self fetchFaviconForItem:item];
+}
+
+// Creates a TabResumptionItem corresponding to the last synced tab then
+// asynchronously invokes `item_block_handler` and exits.
+- (void)fetchMostRecentTabItemFromWebState:(web::WebState*)webState
+                                openedTime:(base::Time)openedTime {
+  TabResumptionItem* item = [[TabResumptionItem alloc]
+      initWithItemType:TabResumptionItemType::kMostRecentTab];
+  item.tabTitle = base::SysUTF16ToNSString(webState->GetTitle());
+  item.syncedTime = openedTime;
+  item.tabURL = webState->GetLastCommittedURL();
+  item.commandHandler = self;
+
+  // Fetch the favicon.
+  [self fetchFaviconForItem:item];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_view.h b/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_view.h
index 5dae2fc..984c9f4 100644
--- a/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_view.h
+++ b/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_view.h
@@ -8,7 +8,7 @@
 #import <UIKit/UIKit.h>
 
 @class TabResumptionItem;
-@protocol ContentSuggestionsCommands;
+@protocol TabResumptionCommands;
 
 // A view that displays a tab resumption item in the Magic Stack.
 @interface TabResumptionView : UIView
@@ -17,7 +17,7 @@
 - (instancetype)initWithItem:(TabResumptionItem*)item;
 
 // The handler that receives TabResumptionView's events.
-@property(nonatomic, weak) id<ContentSuggestionsCommands> commandHandler;
+@property(nonatomic, weak) id<TabResumptionCommands> commandHandler;
 
 @end
 
diff --git a/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_view.mm b/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_view.mm
index 0a3f0bdc..3315e91a 100644
--- a/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_view.mm
+++ b/ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_view.mm
@@ -11,7 +11,7 @@
 #import "components/url_formatter/elide_url.h"
 #import "ios/chrome/browser/shared/ui/symbols/symbols.h"
 #import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
-#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h"
+#import "ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_commands.h"
 #import "ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_constants.h"
 #import "ios/chrome/browser/ui/content_suggestions/tab_resumption/tab_resumption_item.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
diff --git a/ios/web_view/internal/autofill/web_view_personal_data_manager_factory.mm b/ios/web_view/internal/autofill/web_view_personal_data_manager_factory.mm
index 0075c57..6fb79c9 100644
--- a/ios/web_view/internal/autofill/web_view_personal_data_manager_factory.mm
+++ b/ios/web_view/internal/autofill/web_view_personal_data_manager_factory.mm
@@ -66,7 +66,7 @@
       ApplicationContext::GetInstance()->GetLocalState(),
       WebViewIdentityManagerFactory::GetForBrowserState(browser_state),
       /*history_service=*/nullptr, sync_service, /*strike_database=*/nullptr,
-      /*image_fetcher=*/nullptr);
+      /*image_fetcher=*/nullptr, /*shared_storage_handler=*/nullptr);
   return service;
 }
 
diff --git a/ios_internal b/ios_internal
index 1d21e57..9a183ec 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 1d21e57550bc1ef3e6578f7ad5c0d2f2cd2d08ff
+Subproject commit 9a183ec88b60cfbeef16ebd32e27a83f9a06126a
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index d3c7358..c7dd7d4 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -608,7 +608,7 @@
 BASE_FEATURE(kUseMultiPlaneFormatForHardwareVideo,
              "UseMultiPlaneFormatForHardwareVideo",
 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_FUCHSIA) || \
-    BUILDFLAG(IS_LINUX)
+    BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
              base::FEATURE_ENABLED_BY_DEFAULT
 #else
              base::FEATURE_DISABLED_BY_DEFAULT
@@ -619,7 +619,8 @@
 // software video decoders.
 BASE_FEATURE(kUseMultiPlaneFormatForSoftwareVideo,
              "UseMultiPlaneFormatForSoftwareVideo",
-#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_FUCHSIA)
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_FUCHSIA) || \
+    BUILDFLAG(IS_WIN)
              base::FEATURE_ENABLED_BY_DEFAULT
 #else
              base::FEATURE_DISABLED_BY_DEFAULT
diff --git a/media/gpu/v4l2/stateless/v4l2_stateless_video_decoder.cc b/media/gpu/v4l2/stateless/v4l2_stateless_video_decoder.cc
index 7571f72b..8e7f583 100644
--- a/media/gpu/v4l2/stateless/v4l2_stateless_video_decoder.cc
+++ b/media/gpu/v4l2/stateless/v4l2_stateless_video_decoder.cc
@@ -219,26 +219,7 @@
 
   decoder_->Reset();
 
-  // Drop all of the queued, but unprocessed frames on the floor. In a reset
-  // the expectation is all frames that are currently queued are disposed of
-  // without completing the decode process.
-
-  // First clear the request that is being processed.
-  if (current_decode_request_) {
-    std::move(current_decode_request_->decode_cb)
-        .Run(DecoderStatus::Codes::kAborted);
-    current_decode_request_ = std::nullopt;
-  }
-
-  // Then clear out all of the ones that are queued up.
-  while (!decode_request_queue_.empty()) {
-    auto& request = decode_request_queue_.front();
-    std::move(request.decode_cb).Run(DecoderStatus::Codes::kAborted);
-    decode_request_queue_.pop();
-  }
-
-  // Remove all outstanding buffers waiting to be sent to the display.
-  display_queue_ = {};
+  ClearPendingRequests(DecoderStatus::Codes::kAborted);
 
   // If the reset happened in the middle of a flush the flush will not be
   // completed.
@@ -642,6 +623,30 @@
   }
 }
 
+void V4L2StatelessVideoDecoder::ClearPendingRequests(DecoderStatus status) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
+  DVLOGF(4);
+  // Drop all of the queued, but unprocessed frames on the floor. In a reset
+  // the expectation is all frames that are currently queued are disposed of
+  // without completing the decode process.
+
+  // First clear the request that is being processed.
+  if (current_decode_request_) {
+    std::move(current_decode_request_->decode_cb).Run(status);
+    current_decode_request_ = std::nullopt;
+  }
+
+  // Then clear out all of the ones that are queued up.
+  while (!decode_request_queue_.empty()) {
+    auto& request = decode_request_queue_.front();
+    std::move(request.decode_cb).Run(status);
+    decode_request_queue_.pop();
+  }
+
+  // Remove all outstanding buffers waiting to be sent to the display.
+  display_queue_ = {};
+}
+
 void V4L2StatelessVideoDecoder::ServiceDecodeRequestQueue() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
   DVLOGF(4);
@@ -732,8 +737,8 @@
         return;
       case AcceleratedVideoDecoder::kDecodeError:
         VLOGF(1) << "AcceleratedVideoDecoder::kDecodeError.";
-        done = true;
-        break;
+        ClearPendingRequests(DecoderStatus::Codes::kPlatformDecodeFailure);
+        return;
       case AcceleratedVideoDecoder::kTryAgain:
         // Will be needed for h.264 CENCv1
         NOTIMPLEMENTED() << "AcceleratedVideoDecoder::kTryAgain";
@@ -742,13 +747,9 @@
   } while (!done);
 
   if (current_decode_request_) {
-    const DecoderStatus::Codes decode_status =
-        (decode_result == AcceleratedVideoDecoder::kDecodeError)
-            ? DecoderStatus::Codes::kPlatformDecodeFailure
-            : DecoderStatus::Codes::kOk;
     base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
         FROM_HERE, base::BindOnce(std::move(current_decode_request_->decode_cb),
-                                  decode_status));
+                                  DecoderStatus::Codes::kOk));
     current_decode_request_ = std::nullopt;
   }
 }
diff --git a/media/gpu/v4l2/stateless/v4l2_stateless_video_decoder.h b/media/gpu/v4l2/stateless/v4l2_stateless_video_decoder.h
index 37918eb..d869811 100644
--- a/media/gpu/v4l2/stateless/v4l2_stateless_video_decoder.h
+++ b/media/gpu/v4l2/stateless/v4l2_stateless_video_decoder.h
@@ -127,6 +127,9 @@
   // buffer after it is done being used.
   void EnqueueDecodedOutputBufferByFrameID(uint64_t frame_id);
 
+  // Empty out the |decode_request_queue_| and |display_queue_|.
+  void ClearPendingRequests(DecoderStatus status);
+
   // Match up frames that have been decoded and are sitting in the
   // |output_queue_| with |display_queue_| which holds the frames in display
   // order.
diff --git a/net/base/network_delegate.cc b/net/base/network_delegate.cc
index 6aad5c0..aaac8ed3 100644
--- a/net/base/network_delegate.cc
+++ b/net/base/network_delegate.cc
@@ -185,6 +185,30 @@
 }
 
 // static
+void NetworkDelegate::ExcludeAllCookiesExceptPartitioned(
+    net::CookieInclusionStatus::ExclusionReason reason,
+    net::CookieAccessResultList& maybe_included_cookies,
+    net::CookieAccessResultList& excluded_cookies,
+    bool are_cookies_disabled) {
+  // If cookies are not universally disabled, we will preserve partitioned
+  // cookies
+  const auto to_be_moved = base::ranges::stable_partition(
+      maybe_included_cookies,
+      [&are_cookies_disabled](const net::CookieWithAccessResult& cookie) {
+        return !are_cookies_disabled && cookie.cookie.IsPartitioned();
+      });
+  excluded_cookies.insert(
+      excluded_cookies.end(), std::make_move_iterator(to_be_moved),
+      std::make_move_iterator(maybe_included_cookies.end()));
+  maybe_included_cookies.erase(to_be_moved, maybe_included_cookies.end());
+
+  // Add the ExclusionReason for all excluded cookies.
+  for (net::CookieWithAccessResult& cookie : excluded_cookies) {
+    cookie.access_result.status.AddExclusionReason(reason);
+  }
+}
+
+// static
 void NetworkDelegate::MoveExcludedCookies(
     net::CookieAccessResultList& maybe_included_cookies,
     net::CookieAccessResultList& excluded_cookies) {
diff --git a/net/base/network_delegate.h b/net/base/network_delegate.h
index d1bd6c2..1aad3402 100644
--- a/net/base/network_delegate.h
+++ b/net/base/network_delegate.h
@@ -130,6 +130,15 @@
       net::CookieAccessResultList& maybe_included_cookies,
       net::CookieAccessResultList& excluded_cookies);
 
+  // Does the same as ExcludeAllCookies but will still include
+  // cookies that are partitioned if cookies are not disabled
+  // globally.
+  static void ExcludeAllCookiesExceptPartitioned(
+      net::CookieInclusionStatus::ExclusionReason reason,
+      net::CookieAccessResultList& maybe_included_cookies,
+      net::CookieAccessResultList& excluded_cookies,
+      bool are_cookies_disabled);
+
   // Moves any cookie in `maybe_included_cookies` that has an ExclusionReason
   // into `excluded_cookies`.
   static void MoveExcludedCookies(
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json
index e48d4c9d8..9f688b2 100644
--- a/net/http/transport_security_state_static.json
+++ b/net/http/transport_security_state_static.json
@@ -112519,7 +112519,6 @@
     { "name": "salesdock.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "salesdock.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "salva.ovh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "salvageforgood.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "salz-und-sinn.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "same.lol", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sammich.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
diff --git a/services/accessibility/android/ax_tree_source_android.cc b/services/accessibility/android/ax_tree_source_android.cc
index ec554234..c10abe6 100644
--- a/services/accessibility/android/ax_tree_source_android.cc
+++ b/services/accessibility/android/ax_tree_source_android.cc
@@ -291,7 +291,7 @@
   }
 
   for (const int32_t update_id : update_ids) {
-    current_tree_serializer_->MarkSubtreeDirty(GetFromId(update_id));
+    current_tree_serializer_->MarkSubtreeDirty(update_id);
   }
 
   std::vector<ui::AXTreeUpdate> updates;
diff --git a/services/network/network_service_network_delegate.cc b/services/network/network_service_network_delegate.cc
index 15d3a181..5257248 100644
--- a/services/network/network_service_network_delegate.cc
+++ b/services/network/network_service_network_delegate.cc
@@ -206,18 +206,29 @@
   URLLoader* url_loader = URLLoader::ForRequest(request);
   if (url_loader) {
     allowed =
-        url_loader->AllowCookies(request.url(), request.site_for_cookies());
+        url_loader->AllowFullCookies(request.url(), request.site_for_cookies());
+    if (!allowed) {
+      // AllowFullCookies returns false if "all" cookies are not allowed by a
+      // URL but it could still be the case that cookies are allowed, but it was
+      // 3PCs that were not allowed. If that is the case, we should still
+      // preserve partitioned cookies.
+      ExcludeAllCookiesExceptPartitioned(
+          net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES,
+          maybe_included_cookies, excluded_cookies,
+          url_loader->CookiesDisabled());
+    }
 #if BUILDFLAG(ENABLE_WEBSOCKETS)
   } else {
     WebSocket* web_socket = WebSocket::ForRequest(request);
     if (web_socket) {
       allowed = web_socket->AllowCookies(request.url());
+      if (!allowed) {
+        ExcludeAllCookies(net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES,
+                          maybe_included_cookies, excluded_cookies);
+      }
     }
 #endif  // BUILDFLAG(ENABLE_WEBSOCKETS)
   }
-  if (!allowed)
-    ExcludeAllCookies(net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES,
-                      maybe_included_cookies, excluded_cookies);
 
   return allowed;
 }
@@ -238,8 +249,10 @@
   // The remaining checks do not consider setting overrides since they enforce
   // explicit disablement via Android Webview APIs.
   URLLoader* url_loader = URLLoader::ForRequest(request);
-  if (url_loader)
-    return url_loader->AllowCookies(request.url(), request.site_for_cookies());
+  if (url_loader) {
+    return url_loader->AllowCookie(cookie, request.url(),
+                                   request.site_for_cookies());
+  }
 #if BUILDFLAG(ENABLE_WEBSOCKETS)
   WebSocket* web_socket = WebSocket::ForRequest(request);
   if (web_socket)
diff --git a/services/network/public/cpp/BUILD.gn b/services/network/public/cpp/BUILD.gn
index d3c2df9..0d29d51 100644
--- a/services/network/public/cpp/BUILD.gn
+++ b/services/network/public/cpp/BUILD.gn
@@ -584,6 +584,7 @@
     "proxy_config_mojom_traits_unittest.cc",
     "request_destination_unittest.cc",
     "schemeful_site_mojom_traits_unittest.cc",
+    "self_deleting_url_loader_factory_unittest.cc",
     "shared_dictionary_isolation_key_mojom_traits_unittest.cc",
     "shared_dictionary_usage_info_mojom_traits_unittest.cc",
     "simple_host_resolver_unittest.cc",
diff --git a/services/network/public/cpp/self_deleting_url_loader_factory_unittest.cc b/services/network/public/cpp/self_deleting_url_loader_factory_unittest.cc
new file mode 100644
index 0000000..97c210d
--- /dev/null
+++ b/services/network/public/cpp/self_deleting_url_loader_factory_unittest.cc
@@ -0,0 +1,64 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/network/public/cpp/self_deleting_url_loader_factory.h"
+
+#include <memory>
+
+#include "base/functional/bind.h"
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace network {
+namespace {
+
+class MockSelfDeletingURLLoaderFactory
+    : public network::SelfDeletingURLLoaderFactory {
+ public:
+  MockSelfDeletingURLLoaderFactory(
+      mojo::PendingReceiver<network::mojom::URLLoaderFactory> pending_receiver,
+      base::OnceClosure callback)
+      : network::SelfDeletingURLLoaderFactory(std::move(pending_receiver)),
+        callback_(std::move(callback)) {}
+
+  // network::mojom::URLLoaderFactory:
+  void CreateLoaderAndStart(
+      mojo::PendingReceiver<network::mojom::URLLoader> loader,
+      int32_t request_id,
+      uint32_t options,
+      const network::ResourceRequest& request,
+      mojo::PendingRemote<network::mojom::URLLoaderClient> client,
+      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
+      override {}
+
+  ~MockSelfDeletingURLLoaderFactory() override { std::move(callback_).Run(); }
+
+ private:
+  base::OnceClosure callback_;
+};
+
+class SelfDeletingURLLoaderFactoryTest : public testing::Test {
+ protected:
+  SelfDeletingURLLoaderFactoryTest() = default;
+  ~SelfDeletingURLLoaderFactoryTest() override = default;
+
+ private:
+  base::test::SingleThreadTaskEnvironment task_environment_{
+      base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
+};
+
+TEST_F(SelfDeletingURLLoaderFactoryTest, InitializeAndReset) {
+  mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote;
+  base::RunLoop run_loop;
+
+  new MockSelfDeletingURLLoaderFactory(
+      pending_remote.InitWithNewPipeAndPassReceiver(), run_loop.QuitClosure());
+
+  pending_remote.reset();
+  run_loop.Run();
+}
+
+}  // namespace
+}  // namespace network
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index 06ccd16c..89027ae 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -2136,12 +2136,25 @@
   return resource_type_;
 }
 
-bool URLLoader::AllowCookies(
+bool URLLoader::CookiesDisabled() const {
+  return options_ & mojom::kURLLoadOptionBlockAllCookies;
+}
+
+bool URLLoader::AllowCookie(const net::CanonicalCookie& cookie,
+                            const GURL& url,
+                            const net::SiteForCookies& site_for_cookies) const {
+  if (cookie.IsPartitioned() && !CookiesDisabled()) {
+    return true;
+  }
+  return AllowFullCookies(url, site_for_cookies);
+}
+
+bool URLLoader::AllowFullCookies(
     const GURL& url,
     const net::SiteForCookies& site_for_cookies) const {
   net::StaticCookiePolicy::Type policy =
       net::StaticCookiePolicy::ALLOW_ALL_COOKIES;
-  if (options_ & mojom::kURLLoadOptionBlockAllCookies) {
+  if (CookiesDisabled()) {
     policy = net::StaticCookiePolicy::BLOCK_ALL_COOKIES;
   } else if (options_ & mojom::kURLLoadOptionBlockThirdPartyCookies) {
     policy = net::StaticCookiePolicy::BLOCK_ALL_THIRD_PARTY_COOKIES;
diff --git a/services/network/url_loader.h b/services/network/url_loader.h
index 20ae7f6..97e1f35 100644
--- a/services/network/url_loader.h
+++ b/services/network/url_loader.h
@@ -253,11 +253,25 @@
   int32_t GetProcessId() const;
   uint32_t GetResourceType() const;
 
+  // Whether this URLLoader should allow sending/setting any cookies.
+  // This decision is based on the options passed to
+  // URLLoaderFactory::CreateLoaderAndStart().
+  bool CookiesDisabled() const;
+
   // Whether this URLLoader should allow sending/setting cookies for requests
   // with |url| and |site_for_cookies|. This decision is based on the options
   // passed to URLLoaderFactory::CreateLoaderAndStart().
-  bool AllowCookies(const GURL& url,
-                    const net::SiteForCookies& site_for_cookies) const;
+  // If this returns false, partitioned cookies could still be provided if
+  // CookiesDisabled returns false.
+  bool AllowFullCookies(const GURL& url,
+                        const net::SiteForCookies& site_for_cookies) const;
+
+  // Returns whether a particular cookie is allowed to be sent for requests
+  // with |url| and |site_for_cookies|. This decision is based on the options
+  // passed to URLLoaderFactory::CreateLoaderAndStart().
+  bool AllowCookie(const net::CanonicalCookie& cookie,
+                   const GURL& url,
+                   const net::SiteForCookies& site_for_cookies) const;
 
   const net::HttpRequestHeaders& custom_proxy_pre_cache_headers() const {
     return custom_proxy_pre_cache_headers_;
diff --git a/services/network/url_loader_unittest.cc b/services/network/url_loader_unittest.cc
index 6f7db1d8..75f2794e 100644
--- a/services/network/url_loader_unittest.cc
+++ b/services/network/url_loader_unittest.cc
@@ -59,6 +59,7 @@
 #include "net/cookies/cookie_access_result.h"
 #include "net/cookies/cookie_change_dispatcher.h"
 #include "net/cookies/cookie_inclusion_status.h"
+#include "net/cookies/cookie_partition_key.h"
 #include "net/cookies/cookie_setting_override.h"
 #include "net/cookies/cookie_util.h"
 #include "net/dns/mock_host_resolver.h"
@@ -4843,6 +4844,11 @@
       net::SiteForCookies::FromUrl(first_party_url);
   GURL third_party_url("http://www.some.other.origin.test/");
 
+  GURL cookie_url = test_server()->GetURL("/");
+  auto cc = net::CanonicalCookie::Create(
+      cookie_url, "a=b", base::Time::Now(), absl::nullopt /* server_time */,
+      net::CookiePartitionKey::FromURLForTesting(
+          GURL("https://toplevelsite.com")));
   ResourceRequest request = CreateResourceRequest("GET", first_party_url);
   base::RunLoop delete_run_loop;
   mojo::PendingRemote<mojom::URLLoader> loader;
@@ -4855,8 +4861,9 @@
       loader.InitWithNewPipeAndPassReceiver(), request,
       client()->CreateRemote());
 
-  EXPECT_FALSE(url_loader->AllowCookies(first_party_url, site_for_cookies));
-  EXPECT_FALSE(url_loader->AllowCookies(third_party_url, site_for_cookies));
+  EXPECT_FALSE(url_loader->AllowCookie(*cc, first_party_url, site_for_cookies));
+  EXPECT_FALSE(url_loader->AllowFullCookies(first_party_url, site_for_cookies));
+  EXPECT_FALSE(url_loader->AllowFullCookies(third_party_url, site_for_cookies));
 }
 
 TEST_F(URLLoaderTest, BlockOnlyThirdPartyCookies) {
@@ -4865,6 +4872,11 @@
       net::SiteForCookies::FromUrl(first_party_url);
   GURL third_party_url("http://www.some.other.origin.test/");
 
+  GURL cookie_url = test_server()->GetURL("/");
+  auto cc = net::CanonicalCookie::Create(
+      cookie_url, "a=b", base::Time::Now(), absl::nullopt /* server_time */,
+      net::CookiePartitionKey::FromURLForTesting(
+          GURL("https://toplevelsite.com")));
   ResourceRequest request = CreateResourceRequest("GET", first_party_url);
   base::RunLoop delete_run_loop;
   mojo::PendingRemote<mojom::URLLoader> loader;
@@ -4877,8 +4889,9 @@
       loader.InitWithNewPipeAndPassReceiver(), request,
       client()->CreateRemote());
 
-  EXPECT_TRUE(url_loader->AllowCookies(first_party_url, site_for_cookies));
-  EXPECT_FALSE(url_loader->AllowCookies(third_party_url, site_for_cookies));
+  EXPECT_TRUE(url_loader->AllowCookie(*cc, first_party_url, site_for_cookies));
+  EXPECT_TRUE(url_loader->AllowFullCookies(first_party_url, site_for_cookies));
+  EXPECT_FALSE(url_loader->AllowFullCookies(third_party_url, site_for_cookies));
 }
 
 TEST_F(URLLoaderTest, AllowAllCookies) {
@@ -4887,6 +4900,11 @@
       net::SiteForCookies::FromUrl(first_party_url);
   GURL third_party_url("http://www.some.other.origin.test/");
 
+  GURL cookie_url = test_server()->GetURL("/");
+  auto cc = net::CanonicalCookie::Create(
+      cookie_url, "a=b", base::Time::Now(), absl::nullopt /* server_time */,
+      net::CookiePartitionKey::FromURLForTesting(
+          GURL("https://toplevelsite.com")));
   ResourceRequest request = CreateResourceRequest("GET", first_party_url);
   base::RunLoop delete_run_loop;
   mojo::PendingRemote<mojom::URLLoader> loader;
@@ -4897,8 +4915,9 @@
       loader.InitWithNewPipeAndPassReceiver(), request,
       client()->CreateRemote());
 
-  EXPECT_TRUE(url_loader->AllowCookies(first_party_url, site_for_cookies));
-  EXPECT_TRUE(url_loader->AllowCookies(third_party_url, site_for_cookies));
+  EXPECT_TRUE(url_loader->AllowCookie(*cc, first_party_url, site_for_cookies));
+  EXPECT_TRUE(url_loader->AllowFullCookies(first_party_url, site_for_cookies));
+  EXPECT_TRUE(url_loader->AllowFullCookies(third_party_url, site_for_cookies));
 }
 
 class URLLoaderCookieSettingOverridesTest
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 2e6e1f8..72eefddf 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -3252,6 +3252,7 @@
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
         "name": "browser_tests_require_lacros",
+        "retry_only_failed_tests": true,
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
@@ -5310,9 +5311,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6286.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6287.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 123.0.6286.0",
+        "description": "Run with ash-chrome version 123.0.6287.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5322,8 +5323,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v123.0.6286.0",
-              "revision": "version:123.0.6286.0"
+              "location": "lacros_version_skew_tests_v123.0.6287.0",
+              "revision": "version:123.0.6287.0"
             }
           ],
           "dimensions": {
@@ -5466,9 +5467,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6286.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6287.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 123.0.6286.0",
+        "description": "Run with ash-chrome version 123.0.6287.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5478,8 +5479,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v123.0.6286.0",
-              "revision": "version:123.0.6286.0"
+              "location": "lacros_version_skew_tests_v123.0.6287.0",
+              "revision": "version:123.0.6287.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.coverage.json b/testing/buildbot/chromium.coverage.json
index e851b6e..e9b6218f 100644
--- a/testing/buildbot/chromium.coverage.json
+++ b/testing/buildbot/chromium.coverage.json
@@ -20464,9 +20464,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6286.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6287.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 123.0.6286.0",
+        "description": "Run with ash-chrome version 123.0.6287.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -20476,8 +20476,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v123.0.6286.0",
-              "revision": "version:123.0.6286.0"
+              "location": "lacros_version_skew_tests_v123.0.6287.0",
+              "revision": "version:123.0.6287.0"
             }
           ],
           "dimensions": {
@@ -20614,9 +20614,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6286.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6287.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 123.0.6286.0",
+        "description": "Run with ash-chrome version 123.0.6287.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -20626,8 +20626,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v123.0.6286.0",
-              "revision": "version:123.0.6286.0"
+              "location": "lacros_version_skew_tests_v123.0.6287.0",
+              "revision": "version:123.0.6287.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.dev.json b/testing/buildbot/chromium.dev.json
index 730e05f..62eae5d 100644
--- a/testing/buildbot/chromium.dev.json
+++ b/testing/buildbot/chromium.dev.json
@@ -195,22 +195,6 @@
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "content_browsertests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "arm64",
-            "os": "Mac-13"
-          },
-          "service_account": "chromium-tester-dev@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 4
-        },
-        "test": "content_browsertests",
-        "test_id_prefix": "ninja://content/test:content_browsertests/"
-      },
-      {
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
         "name": "content_unittests",
         "swarming": {
           "dimensions": {
@@ -226,21 +210,6 @@
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "arm64",
-            "os": "Mac-13"
-          },
-          "service_account": "chromium-tester-dev@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "interactive_ui_tests",
-        "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
-      },
-      {
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
         "name": "net_unittests",
         "swarming": {
           "dimensions": {
@@ -305,23 +274,6 @@
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "content_browsertests",
-        "swarming": {
-          "dimensions": {
-            "cores": "8|12",
-            "cpu": "x86-64",
-            "os": "Mac-13"
-          },
-          "service_account": "chromium-tester-dev@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 4
-        },
-        "test": "content_browsertests",
-        "test_id_prefix": "ninja://content/test:content_browsertests/"
-      },
-      {
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
         "name": "content_unittests",
         "swarming": {
           "dimensions": {
@@ -337,21 +289,6 @@
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Mac-13"
-          },
-          "service_account": "chromium-tester-dev@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "interactive_ui_tests",
-        "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
-      },
-      {
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
         "name": "net_unittests",
         "swarming": {
           "dimensions": {
@@ -449,7 +386,8 @@
           "dimensions": {
             "os": "Windows-10-19045"
           },
-          "service_account": "chromium-tester-dev@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester-dev@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 3
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
@@ -552,7 +490,8 @@
           "dimensions": {
             "os": "Windows-11-22000"
           },
-          "service_account": "chromium-tester-dev@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester-dev@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 3
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index f58371d..3e99092 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -3567,1082 +3567,6 @@
       }
     ]
   },
-  "chromeos-amd64-generic-rel (reclient compare)": {
-    "additional_compile_targets": [
-      "chromiumos_preflight"
-    ]
-  },
-  "chromeos-amd64-generic-rel (reclient)": {
-    "additional_compile_targets": [
-      "chromiumos_preflight"
-    ],
-    "gtest_tests": [
-      {
-        "args": [
-          "--ozone-platform=headless",
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "aura_unittests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "aura_unittests",
-        "test_id_prefix": "ninja://ui/aura:aura_unittests/"
-      },
-      {
-        "args": [
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "base_unittests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "base_unittests",
-        "test_id_prefix": "ninja://base:base_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-jobs=1",
-          "--gtest_filter=-*UsingRealWebcam_CaptureMjpeg*",
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "capture_unittests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "capture_unittests",
-        "test_id_prefix": "ninja://media/capture:capture_unittests/"
-      },
-      {
-        "args": [
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "cc_unittests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "cc_unittests",
-        "test_id_prefix": "ninja://cc:cc_unittests/"
-      },
-      {
-        "args": [
-          "--tast-retries=1",
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "chrome_all_tast_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "idempotent": false,
-          "io_timeout": 3600,
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 10
-        },
-        "test": "chrome_all_tast_tests",
-        "test_id_prefix": "ninja://chromeos:chrome_all_tast_tests/"
-      },
-      {
-        "args": [
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "chromeos_integration_tests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "chromeos_integration_tests",
-        "test_id_prefix": "ninja://chrome/test:chromeos_integration_tests/"
-      },
-      {
-        "args": [
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "crypto_unittests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "crypto_unittests",
-        "test_id_prefix": "ninja://crypto:crypto_unittests/"
-      },
-      {
-        "args": [
-          "--strip-chrome",
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//tools/perf/process_perf_results.py"
-        },
-        "name": "disk_usage_tast_test",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "idempotent": false,
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "disk_usage_tast_test",
-        "test_id_prefix": "ninja://chromeos:disk_usage_tast_test/"
-      },
-      {
-        "args": [
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "display_unittests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "display_unittests",
-        "test_id_prefix": "ninja://ui/display:display_unittests/"
-      },
-      {
-        "args": [
-          "--env-var",
-          "LIBVA_DRIVERS_PATH",
-          "./",
-          "--env-var",
-          "LIBVA_DRIVER_NAME",
-          "libfake",
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "experiment_percentage": 100,
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "fake_libva_driver_unittest",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "fake_libva_driver_unittest",
-        "test_id_prefix": "ninja://media/gpu/vaapi/test/fake_libva_driver:fake_libva_driver_unittest/"
-      },
-      {
-        "args": [
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "google_apis_unittests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "google_apis_unittests",
-        "test_id_prefix": "ninja://google_apis:google_apis_unittests/"
-      },
-      {
-        "args": [
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "ipc_tests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ipc_tests",
-        "test_id_prefix": "ninja://ipc:ipc_tests/"
-      },
-      {
-        "args": [
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "latency_unittests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "latency_unittests",
-        "test_id_prefix": "ninja://ui/latency:latency_unittests/"
-      },
-      {
-        "args": [
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "libcups_unittests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "libcups_unittests",
-        "test_id_prefix": "ninja://chrome/services/cups_proxy:libcups_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.media_unittests.filter",
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "media_unittests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "media_unittests",
-        "test_id_prefix": "ninja://media:media_unittests/"
-      },
-      {
-        "args": [
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "midi_unittests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "midi_unittests",
-        "test_id_prefix": "ninja://media/midi:midi_unittests/"
-      },
-      {
-        "args": [
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "mojo_unittests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "mojo_unittests",
-        "test_id_prefix": "ninja://mojo:mojo_unittests/"
-      },
-      {
-        "args": [
-          "--vpython-dir=../../vpython_dir_linux_amd64",
-          "--gtest_filter=-PythonUtils.PythonRunTime",
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "net_unittests",
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/3pp/tools/cpython3/linux-amd64",
-              "location": "vpython_dir_linux_amd64",
-              "revision": "version:2@3.8.10.chromium.21"
-            },
-            {
-              "cipd_package": "infra/tools/luci/vpython/linux-amd64",
-              "location": "vpython_dir_linux_amd64",
-              "revision": "git_revision:0f694cdc06ba054b9960aa1ae9766e45b53d02c1"
-            }
-          ],
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test": "net_unittests",
-        "test_id_prefix": "ninja://net:net_unittests/"
-      },
-      {
-        "args": [
-          "--stop-ui",
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "ozone_gl_unittests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ozone_gl_unittests",
-        "test_id_prefix": "ninja://ui/ozone/gl:ozone_gl_unittests/"
-      },
-      {
-        "args": [
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "ozone_unittests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ozone_unittests",
-        "test_id_prefix": "ninja://ui/ozone:ozone_unittests/"
-      },
-      {
-        "args": [
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "pdf_unittests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "pdf_unittests",
-        "test_id_prefix": "ninja://pdf:pdf_unittests/"
-      },
-      {
-        "args": [
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "printing_unittests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "printing_unittests",
-        "test_id_prefix": "ninja://printing:printing_unittests/"
-      },
-      {
-        "args": [
-          "--stop-ui",
-          "--test-launcher-jobs=1",
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "profile_provider_unittest",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "profile_provider_unittest",
-        "test_id_prefix": "ninja://chrome/browser/metrics/perf:profile_provider_unittest/"
-      },
-      {
-        "args": [
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "rust_gtest_interop_unittests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "rust_gtest_interop_unittests",
-        "test_id_prefix": "ninja://testing/rust_gtest_interop:rust_gtest_interop_unittests/"
-      },
-      {
-        "args": [
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "sql_unittests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "sql_unittests",
-        "test_id_prefix": "ninja://sql:sql_unittests/"
-      },
-      {
-        "args": [
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "url_unittests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "url_unittests",
-        "test_id_prefix": "ninja://url:url_unittests/"
-      },
-      {
-        "args": [
-          "--magic-vm-cache=magic_cros_vm_cache",
-          "--stop-ui",
-          "--gtest_filter=\"VaapiTest.*\"",
-          "--env-var",
-          "LIBVA_DRIVERS_PATH",
-          "./",
-          "--env-var",
-          "LIBVA_DRIVER_NAME",
-          "libfake"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "vaapi_unittest",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "vaapi_unittest",
-        "test_id_prefix": "ninja://media/gpu/vaapi:vaapi_unittest/"
-      },
-      {
-        "args": [
-          "--env-var",
-          "LIBVA_DRIVERS_PATH",
-          "./",
-          "--env-var",
-          "LIBVA_DRIVER_NAME",
-          "libfake",
-          "../../media/test/data/test-25fps.vp9",
-          "../../media/test/data/test-25fps.vp9.json",
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "ci_only": true,
-        "experiment_percentage": 100,
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "video_decode_accelerator_tests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "video_decode_accelerator_tests",
-        "test_id_prefix": "ninja://media/gpu/test:video_decode_accelerator_tests/"
-      }
-    ],
-    "isolated_scripts": [
-      {
-        "args": [
-          "--browser=cros-chrome",
-          "--remote=127.0.0.1",
-          "--remote-ssh-port=9222",
-          "--xvfb",
-          "--typ-max-failures=3",
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "telemetry_perf_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "idempotent": false,
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
-        },
-        "test": "telemetry_perf_unittests",
-        "test_id_prefix": "ninja://chrome/test:telemetry_perf_unittests/"
-      },
-      {
-        "args": [
-          "--jobs=1",
-          "--browser=cros-chrome",
-          "--remote=127.0.0.1",
-          "--remote-ssh-port=9222",
-          "--typ-max-failures=3",
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "telemetry_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "idempotent": false,
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 24
-        },
-        "test": "telemetry_unittests",
-        "test_id_prefix": "ninja://chrome/test:telemetry_unittests/"
-      },
-      {
-        "args": [
-          "webgl1_conformance",
-          "--show-stdout",
-          "--browser=cros-chrome",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--log-level=0 --js-flags=--expose-gc --force_high_performance_gpu",
-          "--enforce-browser-version",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_linux_runtimes.json",
-          "--jobs=1",
-          "--remote=127.0.0.1",
-          "--remote-ssh-port=9222",
-          "--magic-vm-cache=magic_cros_vm_cache"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "webgl_conformance_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "kvm": "1",
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests"
-          },
-          "idempotent": false,
-          "named_caches": [
-            {
-              "name": "cros_vm",
-              "path": "magic_cros_vm_cache"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "cros_vm"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 20
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      }
-    ]
-  },
   "chromeos-jacuzzi-rel-skylab-fyi": {
     "additional_compile_targets": [
       "chrome"
@@ -4791,7 +3715,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -4816,7 +3740,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -4839,7 +3763,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -4864,7 +3788,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -4890,7 +3814,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -4915,7 +3839,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -4938,7 +3862,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -4963,7 +3887,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -4986,7 +3910,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -5011,7 +3935,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -5034,7 +3958,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -5059,7 +3983,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -5084,7 +4008,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--git-revision=${got_revision}"
         ],
@@ -5115,7 +4039,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -5138,7 +4062,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -5163,7 +4087,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -5186,7 +4110,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -5211,7 +4135,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -5234,7 +4158,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -5259,7 +4183,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -5282,7 +4206,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -5307,7 +4231,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -5330,7 +4254,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -5355,7 +4279,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -5378,7 +4302,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -5403,7 +4327,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -5429,7 +4353,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -5454,7 +4378,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -5477,7 +4401,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -5502,7 +4426,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -5527,7 +4451,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -5552,7 +4476,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -5577,7 +4501,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -5602,7 +4526,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -5627,7 +4551,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -5654,7 +4578,7 @@
           "hard_timeout": 14400,
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -5681,7 +4605,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -5706,7 +4630,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -5729,7 +4653,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -5754,7 +4678,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -5777,7 +4701,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -5802,7 +4726,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -5825,7 +4749,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -5850,7 +4774,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -5873,7 +4797,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -5898,7 +4822,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -5921,7 +4845,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -5946,7 +4870,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -5969,7 +4893,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -5994,7 +4918,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -6017,7 +4941,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -6042,7 +4966,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -6065,7 +4989,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -6090,7 +5014,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -6113,7 +5037,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -6138,7 +5062,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -6161,7 +5085,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -6186,7 +5110,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -6209,7 +5133,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -6234,7 +5158,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -6250,6 +5174,8 @@
       },
       {
         "args": [
+          "--test-launcher-bot-mode",
+          "--test-launcher-filter-file=testing/buildbot/filters/ios.gpu_unittests.filter",
           "--platform",
           "iPhone 14",
           "--version",
@@ -6257,7 +5183,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -6282,7 +5208,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -6305,7 +5231,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -6330,7 +5256,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -6353,7 +5279,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -6378,7 +5304,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -6401,7 +5327,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -6426,7 +5352,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -6449,7 +5375,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -6474,7 +5400,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -6497,7 +5423,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -6522,7 +5448,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -6545,7 +5471,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -6570,7 +5496,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -6593,7 +5519,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -6618,7 +5544,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -6645,7 +5571,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -6670,7 +5596,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -6697,7 +5623,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -6722,7 +5648,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -6745,7 +5671,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -6770,7 +5696,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -6793,7 +5719,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -6818,7 +5744,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -6841,7 +5767,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -6866,7 +5792,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -6889,7 +5815,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -6914,7 +5840,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -6937,7 +5863,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -6962,7 +5888,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -6985,7 +5911,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -7010,7 +5936,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -7033,7 +5959,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -7058,7 +5984,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -7081,7 +6007,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -7106,7 +6032,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -7129,7 +6055,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -7154,7 +6080,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -7177,7 +6103,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -7202,7 +6128,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -7225,7 +6151,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -7250,7 +6176,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -7273,7 +6199,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -7298,7 +6224,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -7322,7 +6248,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -7347,7 +6273,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -7370,7 +6296,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -7395,7 +6321,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -7418,7 +6344,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -7443,7 +6369,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -7466,7 +6392,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -7491,7 +6417,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -7517,7 +6443,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -7542,7 +6468,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -7565,7 +6491,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -7590,7 +6516,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -7613,7 +6539,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -7638,7 +6564,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -24821,7 +23747,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -24846,7 +23772,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -24869,7 +23795,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -24894,7 +23820,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -24917,7 +23843,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -24942,7 +23868,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -24965,7 +23891,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -24990,7 +23916,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -25013,7 +23939,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -25038,7 +23964,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -25061,7 +23987,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -25086,7 +24012,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -25109,7 +24035,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -25134,7 +24060,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -25157,7 +24083,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -25182,7 +24108,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -25205,7 +24131,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -25230,7 +24156,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -25253,7 +24179,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -25278,7 +24204,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -25301,7 +24227,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -25326,7 +24252,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -25349,7 +24275,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -25374,7 +24300,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -25397,7 +24323,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -25422,7 +24348,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -25445,7 +24371,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -25470,7 +24396,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -25493,7 +24419,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -25518,7 +24444,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -25541,7 +24467,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -25566,7 +24492,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -25589,7 +24515,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -25614,7 +24540,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -25637,7 +24563,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -25662,7 +24588,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -25685,7 +24611,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -25713,7 +24639,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -25736,7 +24662,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -25764,7 +24690,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -25787,7 +24713,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -25815,7 +24741,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -25838,7 +24764,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -25866,7 +24792,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -25889,7 +24815,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -25919,7 +24845,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -25943,7 +24869,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -25973,7 +24899,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -25997,7 +24923,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -26027,7 +24953,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -26051,7 +24977,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -26081,7 +25007,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -26105,7 +25031,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -26135,7 +25061,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -26159,7 +25085,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -26189,7 +25115,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -26213,7 +25139,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -26243,7 +25169,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -26267,7 +25193,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -26295,7 +25221,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -26319,7 +25245,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -26347,7 +25273,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -26371,7 +25297,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -26399,7 +25325,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -26423,7 +25349,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -26451,7 +25377,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -26475,7 +25401,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -26503,7 +25429,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -26526,7 +25452,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -26554,7 +25480,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -26577,7 +25503,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -26605,7 +25531,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -26628,7 +25554,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -26656,7 +25582,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -26679,7 +25605,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -26709,7 +25635,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -26733,7 +25659,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -26763,7 +25689,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -26787,7 +25713,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -26817,7 +25743,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -26841,7 +25767,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -26871,7 +25797,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -26895,7 +25821,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -26920,7 +25846,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -26943,7 +25869,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -26968,7 +25894,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -26991,7 +25917,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -27016,7 +25942,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -27039,7 +25965,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -27064,7 +25990,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -27087,7 +26013,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -27115,7 +26041,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -27139,7 +26065,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -27167,7 +26093,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -27191,7 +26117,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -27219,7 +26145,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -27243,7 +26169,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -27271,7 +26197,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -27295,7 +26221,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -27320,7 +26246,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -27343,7 +26269,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner"
         ],
@@ -27369,7 +26295,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -27392,7 +26318,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -27417,7 +26343,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -27441,7 +26367,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -27466,7 +26392,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -27489,7 +26415,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -27514,7 +26440,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -27537,7 +26463,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -27562,7 +26488,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -27585,7 +26511,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -27610,7 +26536,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -27633,7 +26559,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -27658,7 +26584,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -27681,7 +26607,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -27706,7 +26632,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -27729,7 +26655,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -27757,7 +26683,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -27780,7 +26706,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -27808,7 +26734,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -27831,7 +26757,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -27859,7 +26785,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -27882,7 +26808,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -27907,7 +26833,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -27930,7 +26856,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -27955,7 +26881,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -27978,7 +26904,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -28003,7 +26929,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -28026,7 +26952,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -28051,7 +26977,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -28074,7 +27000,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -28099,7 +27025,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -28122,7 +27048,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -28147,7 +27073,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -28170,7 +27096,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -28195,7 +27121,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -28218,7 +27144,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -28243,7 +27169,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -28266,7 +27192,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -28291,7 +27217,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -28314,7 +27240,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -28339,7 +27265,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -28362,7 +27288,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -28387,7 +27313,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -28410,7 +27336,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -28435,7 +27361,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -28458,7 +27384,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -28483,7 +27409,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -28506,7 +27432,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -28531,7 +27457,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -28554,7 +27480,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -28579,7 +27505,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -28602,7 +27528,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -28627,7 +27553,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -28650,7 +27576,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -28675,7 +27601,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -28698,7 +27624,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -28723,7 +27649,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -28746,7 +27672,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -28771,7 +27697,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -28794,7 +27720,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -28819,7 +27745,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -28842,7 +27768,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -28867,7 +27793,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -28890,7 +27816,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -28915,7 +27841,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -28938,7 +27864,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -28963,7 +27889,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -28986,7 +27912,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -29011,7 +27937,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -29041,7 +27967,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -29066,7 +27992,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -29089,7 +28015,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -29114,7 +28040,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -29137,7 +28063,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -29162,7 +28088,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -29185,7 +28111,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -29210,7 +28136,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -29233,7 +28159,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -29258,7 +28184,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -29281,7 +28207,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -29306,7 +28232,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -29329,7 +28255,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -29354,7 +28280,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -29377,7 +28303,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -29402,7 +28328,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -29425,7 +28351,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -29450,7 +28376,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -29473,7 +28399,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -29498,7 +28424,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -29521,7 +28447,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -29546,7 +28472,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -29569,7 +28495,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -29594,7 +28520,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -29617,7 +28543,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -29642,7 +28568,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -29665,7 +28591,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -29690,7 +28616,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -29713,7 +28639,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -29738,7 +28664,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -29761,7 +28687,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -29786,7 +28712,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -29809,7 +28735,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -29834,7 +28760,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -29857,7 +28783,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -29882,7 +28808,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -29905,7 +28831,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -29933,7 +28859,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -29956,7 +28882,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -29984,7 +28910,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -30007,7 +28933,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -30035,7 +28961,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -30058,7 +28984,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -30088,7 +29014,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -30112,7 +29038,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -30142,7 +29068,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -30166,7 +29092,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -30196,7 +29122,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -30220,7 +29146,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -30250,7 +29176,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -30274,7 +29200,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -30304,7 +29230,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -30328,7 +29254,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -30358,7 +29284,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -30382,7 +29308,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -30410,7 +29336,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -30434,7 +29360,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -30462,7 +29388,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -30486,7 +29412,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -30514,7 +29440,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -30538,7 +29464,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -30566,7 +29492,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -30589,7 +29515,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -30617,7 +29543,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -30640,7 +29566,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -30668,7 +29594,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -30691,7 +29617,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -30721,7 +29647,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -30745,7 +29671,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -30775,7 +29701,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -30799,7 +29725,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -30829,7 +29755,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -30853,7 +29779,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -30878,7 +29804,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -30901,7 +29827,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -30926,7 +29852,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -30949,7 +29875,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -30974,7 +29900,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -30997,7 +29923,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -31022,7 +29948,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -31045,7 +29971,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -31073,7 +29999,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -31097,7 +30023,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -31125,7 +30051,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -31149,7 +30075,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -31177,7 +30103,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -31201,7 +30127,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -31226,7 +30152,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -31249,7 +30175,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner"
         ],
@@ -31275,7 +30201,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -31298,7 +30224,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -31323,7 +30249,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -31347,7 +30273,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -31372,7 +30298,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -31395,7 +30321,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -31420,7 +30346,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -31443,7 +30369,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -31468,7 +30394,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -31491,7 +30417,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -31516,7 +30442,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -31539,7 +30465,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -31564,7 +30490,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -31587,7 +30513,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -31612,7 +30538,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -31635,7 +30561,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -31663,7 +30589,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -31686,7 +30612,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -31714,7 +30640,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -31737,7 +30663,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -31765,7 +30691,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -31788,7 +30714,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -31813,7 +30739,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -31836,7 +30762,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -31861,7 +30787,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -31884,7 +30810,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -31909,7 +30835,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -31932,7 +30858,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -31957,7 +30883,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -31980,7 +30906,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -32005,7 +30931,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -32028,7 +30954,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -32053,7 +30979,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -32076,7 +31002,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -32101,7 +31027,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -32124,7 +31050,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -32149,7 +31075,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -32172,7 +31098,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -32197,7 +31123,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -32220,7 +31146,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -32245,7 +31171,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -32268,7 +31194,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -32293,7 +31219,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -32316,7 +31242,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -32341,7 +31267,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -32364,7 +31290,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -32389,7 +31315,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -32412,7 +31338,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -32437,7 +31363,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -32460,7 +31386,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -32485,7 +31411,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -32508,7 +31434,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -32533,7 +31459,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -32556,7 +31482,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -32581,7 +31507,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -32604,7 +31530,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -32629,7 +31555,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -32652,7 +31578,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -32677,7 +31603,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -32700,7 +31626,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -32725,7 +31651,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -32748,7 +31674,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -32773,7 +31699,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -32796,7 +31722,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -32821,7 +31747,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -32844,7 +31770,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -32869,7 +31795,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -32892,7 +31818,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -32917,7 +31843,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -32947,7 +31873,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -32972,7 +31898,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -32995,7 +31921,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -33020,7 +31946,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -33043,7 +31969,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -33068,7 +31994,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -33091,7 +32017,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -33116,7 +32042,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -33139,7 +32065,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -33164,7 +32090,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -33187,7 +32113,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -33212,7 +32138,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -33235,7 +32161,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -33260,7 +32186,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -33283,7 +32209,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -33308,7 +32234,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -33331,7 +32257,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -33356,7 +32282,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -33379,7 +32305,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -33404,7 +32330,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -33427,7 +32353,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -33452,7 +32378,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -33475,7 +32401,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -33500,7 +32426,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -33523,7 +32449,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -33548,7 +32474,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -33571,7 +32497,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -33596,7 +32522,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -33619,7 +32545,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -33644,7 +32570,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -33667,7 +32593,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -33692,7 +32618,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -33715,7 +32641,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -33740,7 +32666,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -33763,7 +32689,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -33788,7 +32714,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -33811,7 +32737,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -33836,7 +32762,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -33859,7 +32785,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -33884,7 +32810,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -33907,7 +32833,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -33932,7 +32858,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -33955,7 +32881,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -33983,7 +32909,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -34006,7 +32932,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -34034,7 +32960,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -34057,7 +32983,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -34087,7 +33013,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -34111,7 +33037,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -34141,7 +33067,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -34165,7 +33091,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -34195,7 +33121,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -34219,7 +33145,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -34249,7 +33175,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -34273,7 +33199,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -34301,7 +33227,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -34325,7 +33251,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -34353,7 +33279,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -34377,7 +33303,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -34405,7 +33331,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -34428,7 +33354,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -34456,7 +33382,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -34479,7 +33405,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -34509,7 +33435,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -34533,7 +33459,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -34563,7 +33489,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -34587,7 +33513,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -34612,7 +33538,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -34635,7 +33561,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -34660,7 +33586,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -34683,7 +33609,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -34708,7 +33634,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -34731,7 +33657,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -34759,7 +33685,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -34783,7 +33709,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -34811,7 +33737,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -34835,7 +33761,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -34860,7 +33786,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -34883,7 +33809,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -34908,7 +33834,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -34931,7 +33857,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner"
         ],
@@ -34957,7 +33883,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -34980,7 +33906,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner"
         ],
@@ -35006,7 +33932,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -35029,7 +33955,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -35054,7 +33980,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -35078,7 +34004,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -35103,7 +34029,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -35127,7 +34053,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -35152,7 +34078,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -35175,7 +34101,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -35200,7 +34126,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -35223,7 +34149,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -35248,7 +34174,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -35271,7 +34197,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -35296,7 +34222,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -35319,7 +34245,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -35344,7 +34270,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -35367,7 +34293,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -35392,7 +34318,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -35415,7 +34341,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -35440,7 +34366,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -35463,7 +34389,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -35491,7 +34417,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -35514,7 +34440,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner",
           "--record-video",
@@ -35542,7 +34468,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -35565,7 +34491,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -35590,7 +34516,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -35613,7 +34539,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -35638,7 +34564,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -35661,7 +34587,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -35686,7 +34612,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -35709,7 +34635,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -35734,7 +34660,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -35757,7 +34683,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -35782,7 +34708,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -35805,7 +34731,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -35830,7 +34756,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -35853,7 +34779,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -35878,7 +34804,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -35901,7 +34827,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -35926,7 +34852,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -35949,7 +34875,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -35974,7 +34900,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -35997,7 +34923,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -36022,7 +34948,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -36045,7 +34971,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -36070,7 +34996,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -36093,7 +35019,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -36118,7 +35044,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -36141,7 +35067,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -36166,7 +35092,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -36189,7 +35115,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -36214,7 +35140,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -36237,7 +35163,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -36262,7 +35188,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -36285,7 +35211,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -36310,7 +35236,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -36333,7 +35259,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -36358,7 +35284,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -36381,7 +35307,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -36406,7 +35332,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -36429,7 +35355,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -36454,7 +35380,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -36477,7 +35403,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -36502,7 +35428,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -36525,7 +35451,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -36550,7 +35476,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -36573,7 +35499,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -36598,7 +35524,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -36621,7 +35547,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -36646,7 +35572,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -36681,7 +35607,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -36706,7 +35632,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -36729,7 +35655,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -36754,7 +35680,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -36777,7 +35703,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -36802,7 +35728,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -36825,7 +35751,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -36850,7 +35776,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -36873,7 +35799,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -36898,7 +35824,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -36921,7 +35847,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -36946,7 +35872,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -36969,7 +35895,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -36994,7 +35920,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -37017,7 +35943,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -37042,7 +35968,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -37065,7 +35991,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -37090,7 +36016,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -37113,7 +36039,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -37138,7 +36064,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -37161,7 +36087,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -37186,7 +36112,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -37209,7 +36135,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -37234,7 +36160,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -37257,7 +36183,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -37282,7 +36208,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -37305,7 +36231,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -37330,7 +36256,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -37353,7 +36279,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -37378,7 +36304,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -37401,7 +36327,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -37426,7 +36352,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -37449,7 +36375,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -37474,7 +36400,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -37497,7 +36423,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -37522,7 +36448,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -37545,7 +36471,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -37570,7 +36496,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -37593,7 +36519,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -37618,7 +36544,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -37641,7 +36567,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -37666,7 +36592,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -37689,7 +36615,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner"
         ],
@@ -37715,7 +36641,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -37738,7 +36664,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner"
         ],
@@ -37764,7 +36690,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -37787,7 +36713,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -37815,7 +36741,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -37839,7 +36765,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -37867,7 +36793,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -37891,7 +36817,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -37919,7 +36845,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -37943,7 +36869,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -37971,7 +36897,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -37995,7 +36921,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner"
         ],
@@ -38021,7 +36947,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -38045,7 +36971,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner"
         ],
@@ -38071,7 +36997,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -38095,7 +37021,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner"
         ],
@@ -38121,7 +37047,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -38144,7 +37070,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner"
         ],
@@ -38170,7 +37096,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -38193,7 +37119,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -38221,7 +37147,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -38245,7 +37171,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--clones",
           "2",
@@ -38273,7 +37199,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -38297,7 +37223,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -38322,7 +37248,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -38345,7 +37271,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -38370,7 +37296,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -38393,7 +37319,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -38418,7 +37344,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -38441,7 +37367,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner"
         ],
@@ -38467,7 +37393,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -38491,7 +37417,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner"
         ],
@@ -38517,7 +37443,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -38541,7 +37467,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -38566,7 +37492,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -38589,7 +37515,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -38614,7 +37540,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -38637,7 +37563,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -38662,7 +37588,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -38686,7 +37612,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -38711,7 +37637,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -38735,7 +37661,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -38760,7 +37686,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -38783,7 +37709,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -38808,7 +37734,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -38831,7 +37757,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -38856,7 +37782,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -38879,7 +37805,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -38904,7 +37830,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -38927,7 +37853,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -38952,7 +37878,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -38975,7 +37901,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -39000,7 +37926,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -39023,7 +37949,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -39048,7 +37974,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -39071,7 +37997,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner"
         ],
@@ -39097,7 +38023,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -39120,7 +38046,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest",
           "--xcodebuild-sim-runner"
         ],
@@ -39146,7 +38072,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -39169,7 +38095,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -39194,7 +38120,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -39217,7 +38143,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -39242,7 +38168,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -39265,7 +38191,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -39290,7 +38216,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -39313,7 +38239,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -39338,7 +38264,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -39361,7 +38287,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -39386,7 +38312,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -39409,7 +38335,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -39434,7 +38360,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -39457,7 +38383,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -39482,7 +38408,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -39505,7 +38431,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -39530,7 +38456,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -39553,7 +38479,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -39578,7 +38504,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -39601,7 +38527,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -39626,7 +38552,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -39649,7 +38575,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -39674,7 +38600,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -39697,7 +38623,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -39722,7 +38648,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -39745,7 +38671,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -39770,7 +38696,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -39793,7 +38719,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -39818,7 +38744,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -39841,7 +38767,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -39866,7 +38792,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -39889,7 +38815,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -39914,7 +38840,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -39937,7 +38863,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -39962,7 +38888,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -39985,7 +38911,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -40010,7 +38936,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -40033,7 +38959,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -40058,7 +38984,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -40081,7 +39007,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -40106,7 +39032,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -40129,7 +39055,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -40154,7 +39080,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -40177,7 +39103,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -40202,7 +39128,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -40225,7 +39151,7 @@
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xcode-build-version",
-          "15e5178i",
+          "15e5188j",
           "--xctest"
         ],
         "merge": {
@@ -40250,7 +39176,7 @@
           },
           "named_caches": [
             {
-              "name": "xcode_ios_15e5178i",
+              "name": "xcode_ios_15e5188j",
               "path": "Xcode.app"
             },
             {
@@ -42532,9 +41458,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6286.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6287.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 123.0.6286.0",
+        "description": "Run with ash-chrome version 123.0.6287.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -42543,8 +41469,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v123.0.6286.0",
-              "revision": "version:123.0.6286.0"
+              "location": "lacros_version_skew_tests_v123.0.6287.0",
+              "revision": "version:123.0.6287.0"
             }
           ],
           "dimensions": {
@@ -42682,9 +41608,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6286.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6287.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 123.0.6286.0",
+        "description": "Run with ash-chrome version 123.0.6287.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -42693,8 +41619,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v123.0.6286.0",
-              "revision": "version:123.0.6286.0"
+              "location": "lacros_version_skew_tests_v123.0.6287.0",
+              "revision": "version:123.0.6287.0"
             }
           ],
           "dimensions": {
@@ -44014,9 +42940,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6286.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6287.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 123.0.6286.0",
+        "description": "Run with ash-chrome version 123.0.6287.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -44025,8 +42951,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v123.0.6286.0",
-              "revision": "version:123.0.6286.0"
+              "location": "lacros_version_skew_tests_v123.0.6287.0",
+              "revision": "version:123.0.6287.0"
             }
           ],
           "dimensions": {
@@ -44164,9 +43090,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6286.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6287.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 123.0.6286.0",
+        "description": "Run with ash-chrome version 123.0.6287.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -44175,8 +43101,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v123.0.6286.0",
-              "revision": "version:123.0.6286.0"
+              "location": "lacros_version_skew_tests_v123.0.6287.0",
+              "revision": "version:123.0.6287.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index f944faa..521bf51 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -16507,12 +16507,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6286.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6287.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 123.0.6286.0",
+        "description": "Run with ash-chrome version 123.0.6287.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -16522,8 +16522,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v123.0.6286.0",
-              "revision": "version:123.0.6286.0"
+              "location": "lacros_version_skew_tests_v123.0.6287.0",
+              "revision": "version:123.0.6287.0"
             }
           ],
           "dimensions": {
@@ -16683,12 +16683,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6286.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6287.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 123.0.6286.0",
+        "description": "Run with ash-chrome version 123.0.6287.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -16698,8 +16698,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v123.0.6286.0",
-              "revision": "version:123.0.6286.0"
+              "location": "lacros_version_skew_tests_v123.0.6287.0",
+              "revision": "version:123.0.6287.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn
index 4ebc3b9..1df98acb 100644
--- a/testing/buildbot/filters/BUILD.gn
+++ b/testing/buildbot/filters/BUILD.gn
@@ -165,6 +165,13 @@
               "{{source_root_relative_dir}}/{{source_file_part}}" ]
 }
 
+bundle_data("gpu_unittests_filters_bundle_data") {
+  testonly = true
+  sources = [ "//testing/buildbot/filters/ios.gpu_unittests.filter" ]
+  outputs = [ "{{bundle_resources_dir}}/" +
+              "{{source_root_relative_dir}}/{{source_file_part}}" ]
+}
+
 source_set("content_browsertests_filters") {
   testonly = true
 
diff --git a/testing/buildbot/filters/ios.gpu_unittests.filter b/testing/buildbot/filters/ios.gpu_unittests.filter
new file mode 100644
index 0000000..e68263f
--- /dev/null
+++ b/testing/buildbot/filters/ios.gpu_unittests.filter
@@ -0,0 +1,7 @@
+# TODO(crbug.com/324003984): These tests failed since the `UseGles2ForOopR`
+# feature was disabled.
+-GpuChannelExitForContextLostTest.CreateFailsDuringLostContextShutdown_1
+-GpuChannelExitForContextLostTest.CreateFailsDuringLostContextShutdown_2
+-GpuChannelTest.CreateFailsIfSharedContextIsLost
+-GpuChannelTest.CreateOffscreenCommandBuffer
+-GpuChannelTest.IncompatibleStreamIds
diff --git a/testing/buildbot/internal.optimization_guide.json b/testing/buildbot/internal.optimization_guide.json
index c4fddf1..47f72be4 100644
--- a/testing/buildbot/internal.optimization_guide.json
+++ b/testing/buildbot/internal.optimization_guide.json
@@ -218,6 +218,7 @@
         },
         "swarming": {
           "dimensions": {
+            "gce": "1",
             "os": "Ubuntu-22.04",
             "pool": "chrome.tests.intelligence"
           },
@@ -245,6 +246,7 @@
         },
         "swarming": {
           "dimensions": {
+            "gce": "1",
             "os": "Ubuntu-22.04",
             "pool": "chrome.tests.intelligence"
           },
@@ -754,6 +756,7 @@
         },
         "swarming": {
           "dimensions": {
+            "gce": "1",
             "os": "Windows-10-19045",
             "pool": "chrome.tests.intelligence"
           },
@@ -781,6 +784,7 @@
         },
         "swarming": {
           "dimensions": {
+            "gce": "1",
             "os": "Windows-10-19045",
             "pool": "chrome.tests.intelligence"
           },
@@ -789,6 +793,34 @@
         "test": "model_validation_tests",
         "test_id_prefix": "ninja://components/optimization_guide/internal/testing:model_validation_tests/",
         "variant_id": "MODEL_VALIDATION_TRUNK"
+      },
+      {
+        "args": [
+          "--chromedriver",
+          "chromedriver.exe",
+          "--binary",
+          "Chrome.exe",
+          "--target-platform=win32"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ondevice_stability_tests AMD Radeon RX 5500 XT",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "gpu": "1002:7340",
+            "os": "Windows-10-19045",
+            "pool": "chrome.tests.intelligence"
+          },
+          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "ondevice_stability_tests",
+        "test_id_prefix": "ninja://components/optimization_guide/internal/testing:ondevice_stability_tests/",
+        "variant_id": "AMD Radeon RX 5500 XT"
       }
     ]
   },
@@ -914,6 +946,7 @@
         },
         "swarming": {
           "dimensions": {
+            "gce": "1",
             "os": "Windows-10-19045",
             "pool": "chrome.tests.intelligence"
           },
@@ -941,6 +974,7 @@
         },
         "swarming": {
           "dimensions": {
+            "gce": "1",
             "os": "Windows-10-19045",
             "pool": "chrome.tests.intelligence"
           },
@@ -949,6 +983,60 @@
         "test": "model_validation_tests",
         "test_id_prefix": "ninja://components/optimization_guide/internal/testing:model_validation_tests/",
         "variant_id": "MODEL_VALIDATION_TRUNK"
+      },
+      {
+        "args": [
+          "--chromedriver",
+          "chromedriver.exe",
+          "--binary",
+          "Chrome.exe"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ondevice_stability_tests AMD Radeon RX 5500 XT",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "gpu": "1002:7340",
+            "os": "Windows-10-19045",
+            "pool": "chrome.tests.intelligence"
+          },
+          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "ondevice_stability_tests",
+        "test_id_prefix": "ninja://components/optimization_guide/internal/testing:ondevice_stability_tests/",
+        "variant_id": "AMD Radeon RX 5500 XT"
+      },
+      {
+        "args": [
+          "--chromedriver",
+          "chromedriver.exe",
+          "--binary",
+          "Chrome.exe"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ondevice_stability_tests Intel UHD 630",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "gpu": "8086:9bc5",
+            "os": "Windows-10-19045",
+            "pool": "chrome.tests.intelligence"
+          },
+          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "ondevice_stability_tests",
+        "test_id_prefix": "ninja://components/optimization_guide/internal/testing:ondevice_stability_tests/",
+        "variant_id": "Intel UHD 630"
       }
     ]
   }
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index efb1d65..b01791a9 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -490,6 +490,13 @@
       '--logs-dir=${ISOLATED_OUTDIR}/logs',
     ],
   },
+  'gce': {
+    'swarming': {
+      'dimensions': {
+        'gce': '1',
+      },
+    },
+  },
   'gpu-swarming-pool': {
     'swarming': {
       'dimensions': {
@@ -1323,12 +1330,12 @@
   'xcode_15_beta': {
     'args': [
       '--xcode-build-version',
-      '15e5178i',
+      '15e5188j',
     ],
     'swarming': {
       'named_caches': [
         {
-          'name': 'xcode_ios_15e5178i',
+          'name': 'xcode_ios_15e5188j',
           'path': 'Xcode.app',
         },
       ],
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 40624133..c4fdd272 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -1318,6 +1318,13 @@
       'Win11 Tests x64',
     ],
   },
+  'browser_tests_require_lacros': {
+    'modifications': {
+      'linux-chromeos-rel': {
+        'retry_only_failed_tests': True
+      }
+    }
+  },
   'capture_unittests': {
     'modifications': {
       'WebRTC Chromium Linux Tester': {
@@ -3698,6 +3705,22 @@
       'ToTWin64(dll)',
     ],
   },
+  'ondevice_stability_tests AMD Radeon RX 5500 XT': {
+    'modifications': {
+      'optimization_guide-win32': {
+        'args': [
+          '--target-platform=win32',
+        ],
+      },
+    },
+  },
+  'ondevice_stability_tests Intel UHD 630': {
+    'remove_from': [
+      # TODO(b:324122374): Enable this suite on the win32 builder
+      # once GPU issue is resolved.
+      'optimization_guide-win32',
+    ]
+  },
   'ozone_unittests': {
     'modifications': {
       'chromeos-betty-pi-arc-chrome': {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 57027dd..dba5d18 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -758,20 +758,6 @@
       'chrome_public_smoke_test': {},
     },
 
-    'chromium_dev_desktop_gtests': {
-      'base_unittests': {},
-      'content_browsertests': {
-        'swarming': {
-          'shards': 4,
-        },
-      },
-      'content_unittests': {},
-      'interactive_ui_tests': {},
-      'net_unittests': {},
-      'rust_gtest_interop_unittests': {},
-      'unit_tests': {},
-    },
-
     'chromium_dev_linux_gtests': {
       'base_unittests': {
         'swarming': {
@@ -828,6 +814,32 @@
       },
     },
 
+    'chromium_dev_mac_gtests': {
+      'base_unittests': {},
+      'content_unittests': {},
+      'net_unittests': {},
+      'rust_gtest_interop_unittests': {},
+      'unit_tests': {},
+    },
+
+    'chromium_dev_win_gtests': {
+      'base_unittests': {},
+      'content_browsertests': {
+        'swarming': {
+          'shards': 4,
+        },
+      },
+      'content_unittests': {},
+      'interactive_ui_tests': {
+        'swarming': {
+          'shards': 3,
+        },
+      },
+      'net_unittests': {},
+      'rust_gtest_interop_unittests': {},
+      'unit_tests': {},
+    },
+
     'chromium_gtests': {
       'absl_hardening_tests': {},
       'angle_unittests': {
@@ -4104,7 +4116,12 @@
       'gin_unittests': {},
       'gl_unittests': {},
       'google_apis_unittests': {},
-      'gpu_unittests': {},
+      'gpu_unittests': {
+        'args': [
+          '--test-launcher-bot-mode',
+          '--test-launcher-filter-file=testing/buildbot/filters/ios.gpu_unittests.filter',
+        ],
+      },
       'gwp_asan_unittests': {},
       'ipc_tests': {},
       'latency_unittests': {},
@@ -7974,6 +7991,9 @@
 
     'optimization_guide_linux_script_tests': {
       'model_validation_tests': {
+        'mixins': [
+          'gce',
+        ],
         'variants': [
           'MODEL_VALIDATION_BASE',
           'MODEL_VALIDATION_TRUNK',
@@ -8008,16 +8028,29 @@
           'INTEL_UHD_630',
         ],
       },
-      'optimization_guide_nogpu_gtests': {},
+      'optimization_guide_nogpu_gtests': {
+        'mixins': [
+          'gce',
+        ],
+      },
     },
 
     'optimization_guide_win_script_tests': {
       'model_validation_tests': {
+        'mixins': [
+          'gce',
+        ],
         'variants': [
           'MODEL_VALIDATION_BASE',
           'MODEL_VALIDATION_TRUNK',
         ],
       },
+      'ondevice_stability_tests': {
+        'variants': [
+          'AMD_RADEON_RX_5500_XT',
+          'INTEL_UHD_630',
+        ],
+      },
     },
 
     'webview_trichrome_10_cts_tests_gtest': {
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 90c72e8d..fab1872 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -307,16 +307,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'identifier': 'Lacros version skew testing ash canary',
-    'description': 'Run with ash-chrome version 123.0.6286.0',
+    'description': 'Run with ash-chrome version 123.0.6287.0',
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6286.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v123.0.6287.0/test_ash_chrome',
     ],
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v123.0.6286.0',
-          'revision': 'version:123.0.6286.0',
+          'location': 'lacros_version_skew_tests_v123.0.6287.0',
+          'revision': 'version:123.0.6287.0',
         },
       ],
     },
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 769a9572..254616d4 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -2802,7 +2802,7 @@
           'mac_default_arm64',
         ],
         'test_suites': {
-           'gtest_tests': 'chromium_dev_desktop_gtests',
+           'gtest_tests': 'chromium_dev_mac_gtests',
         },
       },
       'mac-rel-dev': {
@@ -2810,7 +2810,7 @@
           'mac_default_x64',
         ],
         'test_suites': {
-           'gtest_tests': 'chromium_dev_desktop_gtests',
+           'gtest_tests': 'chromium_dev_mac_gtests',
         },
       },
       'win-rel-dev': {
@@ -2818,7 +2818,7 @@
           'win10',
         ],
         'test_suites': {
-           'gtest_tests': 'chromium_dev_desktop_gtests',
+           'gtest_tests': 'chromium_dev_win_gtests',
         },
       },
       'win11-rel-dev': {
@@ -2826,7 +2826,7 @@
           'win11',
         ],
         'test_suites': {
-           'gtest_tests': 'chromium_dev_desktop_gtests',
+           'gtest_tests': 'chromium_dev_win_gtests',
         },
       },
     },
@@ -3266,32 +3266,6 @@
         },
       },
 
-      # TODO(crbug.com/1235218): remove after the migration.
-      'chromeos-amd64-generic-rel (reclient compare)': {
-        'additional_compile_targets': [
-          'chromiumos_preflight',
-        ],
-        'browser_config': 'cros-chrome',
-        'mixins': [
-          'chromeos-amd64-generic',
-        ],
-        'os_type': 'chromeos',
-      },
-      'chromeos-amd64-generic-rel (reclient)': {
-        'additional_compile_targets': [
-          'chromiumos_preflight',
-        ],
-        'browser_config': 'cros-chrome',
-        'mixins': [
-          'chromeos-amd64-generic',
-        ],
-        'os_type': 'chromeos',
-        'test_suites': {
-          'gpu_telemetry_tests': 'gpu_chromeos_telemetry_tests',
-          'gtest_tests': 'chromeos_vm_gtests_and_tast',
-          'isolated_scripts': 'chromeos_isolated_scripts',
-        },
-      },
       'chromeos-jacuzzi-rel-skylab-fyi': {
         'additional_compile_targets': [
           'chrome',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index dd0f8e5..1198221 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -3876,6 +3876,26 @@
             ]
         }
     ],
+    "ChromeWallpaperSearchLaunch": [
+        {
+            "platforms": [
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "CustomizeChromeWallpaperSearch",
+                        "CustomizeChromeWallpaperSearchInspirationCard"
+                    ]
+                }
+            ]
+        }
+    ],
     "ChromeWideEchoCancellation": [
         {
             "platforms": [
@@ -6527,6 +6547,33 @@
             ]
         }
     ],
+    "ESBIPHPromoOnDownloads": [
+        {
+            "platforms": [
+                "chromeos",
+                "chromeos_lacros",
+                "fuchsia",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "params": {
+                        "availability": "any",
+                        "event_1": "name:download_bubble_dangerous_download_detected;comparator:>=1;window:21;storage:360",
+                        "event_trigger": "name:download_bubble_esb_iph_trigger;comparator:==0;window:360;storage:360",
+                        "event_used": "name:enable_enhanced_protection;comparator:==0;window:21;storage:360",
+                        "session_rate": "any"
+                    },
+                    "enable_features": [
+                        "IPH_DownloadEsbPromo"
+                    ]
+                }
+            ]
+        }
+    ],
     "EarlyInitializeStartupMetrics": [
         {
             "platforms": [
@@ -6795,6 +6842,21 @@
             ]
         }
     ],
+    "EnableMojoJSProtectedMemory": [
+        {
+            "platforms": [
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "EnableMojoJSProtectedMemory"
+                    ]
+                }
+            ]
+        }
+    ],
     "EnableNtpHistoryClustersModuleDiscounts": [
         {
             "platforms": [
@@ -7775,6 +7837,28 @@
             ]
         }
     ],
+    "FrameRoutingCache": [
+        {
+            "platforms": [
+                "android",
+                "android_webview",
+                "chromeos",
+                "chromeos_lacros",
+                "fuchsia",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "FrameRoutingCache"
+                    ]
+                }
+            ]
+        }
+    ],
     "GCMAccountTokenReporting": [
         {
             "platforms": [
@@ -9515,21 +9599,6 @@
             ]
         }
     ],
-    "IOSSessionRestorationSessionIDCheck": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "SessionRestorationSessionIDCheck"
-                    ]
-                }
-            ]
-        }
-    ],
     "IOSSharedHighlightingColorChange": [
         {
             "platforms": [
@@ -17881,6 +17950,27 @@
             ]
         }
     ],
+    "SkipUnnecessaryThreadHopsForParseHeaders": [
+        {
+            "platforms": [
+                "android",
+                "android_webview",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "SkipUnnecessaryThreadHopsForParseHeaders"
+                    ]
+                }
+            ]
+        }
+    ],
     "SmallerInterestArea": [
         {
             "platforms": [
@@ -19095,22 +19185,6 @@
             ]
         }
     ],
-    "TranslateMessageUI": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "ContentLanguagesInLanguagePicker",
-                        "TranslateMessageUI"
-                    ]
-                }
-            ]
-        }
-    ],
     "TrustSafetySentimentSurvey": [
         {
             "platforms": [
diff --git a/third_party/abseil-cpp/symbols_arm64_rel.def b/third_party/abseil-cpp/symbols_arm64_rel.def
index 43f81b9..4020964 100644
--- a/third_party/abseil-cpp/symbols_arm64_rel.def
+++ b/third_party/abseil-cpp/symbols_arm64_rel.def
@@ -171,6 +171,7 @@
     ??$__for_each_segment@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEBU1234@AEBU1234@PEBQEBU1234@_J$0A@@__Cr@std@@U?$_CopySegment@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEBU1234@AEBU1234@PEBQEBU1234@_J$0A@@__Cr@std@@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEAU1234@AEAU1234@PEAPEAU1234@_J$0A@@23@@?$__copy_loop@U_ClassicAlgPolicy@__Cr@std@@@23@@__Cr@std@@YAXV?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEBU1234@AEBU1234@PEBQEBU1234@_J$0A@@01@0U?$_CopySegment@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEBU1234@AEBU1234@PEBQEBU1234@_J$0A@@__Cr@std@@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEAU1234@AEAU1234@PEAPEAU1234@_J$0A@@23@@?$__copy_loop@U_ClassicAlgPolicy@__Cr@std@@@01@@Z
     ??$__rehash@$00@?$__hash_table@PEBUCordRep@cord_internal@absl@@U?$hash@PEBUCordRep@cord_internal@absl@@@__Cr@std@@U?$equal_to@PEBUCordRep@cord_internal@absl@@@56@V?$allocator@PEBUCordRep@cord_internal@absl@@@56@@__Cr@std@@AEAAX_K@Z
     ??$__rehash@$00@?$__hash_table@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@V?$__unordered_map_hasher@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@$00@23@V?$__unordered_map_equal@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@$00@23@V?$allocator@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@@23@@__Cr@std@@AEAAX_K@Z
+    ??$__upper_bound@U_ClassicAlgPolicy@__Cr@std@@UByCivilTime@Transition@cctz@time_internal@absl@@PEBU5678@PEBU5678@U5678@U__identity@23@@__Cr@std@@YAPEBUTransition@cctz@time_internal@absl@@PEBU2345@0AEBU2345@$$QEAUByCivilTime@2345@$$QEAU__identity@01@@Z
     ??$assign@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEBU1234@AEBU1234@PEBQEBU1234@_J$0A@@__Cr@std@@$0A@@?$deque@UPrefixCrc@CrcCordState@crc_internal@absl@@V?$allocator@UPrefixCrc@CrcCordState@crc_internal@absl@@@__Cr@std@@@__Cr@std@@QEAAXV?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEBU1234@AEBU1234@PEBQEBU1234@_J$0A@@12@0@Z
     ??$combine@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@H@?$HashStateBase@VMixingHashState@hash_internal@absl@@@hash_internal@absl@@SA?AVMixingHashState@12@V312@AEBV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AEBH@Z
     ??$construct_at@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@AEBUpiecewise_construct_t@23@V?$tuple@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$tuple@$$V@23@PEAU123@@__Cr@std@@YAPEAU?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@01@PEAU201@AEBUpiecewise_construct_t@01@$$QEAV?$tuple@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@01@$$QEAV?$tuple@$$V@01@@Z
@@ -212,9 +213,8 @@
     ??0ByLength@absl@@QEAA@_J@Z
     ??0ByString@absl@@QEAA@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
     ??0CRC@crc_internal@absl@@IEAA@XZ
+    ??0CharIterator@Cord@absl@@AEAA@PEBV12@@Z
     ??0CheckOpMessageBuilder@log_internal@absl@@QEAA@PEBD@Z
-    ??0ChunkIterator@Cord@absl@@AEAA@PEAUCordRep@cord_internal@2@@Z
-    ??0ChunkIterator@Cord@absl@@AEAA@PEBV12@@Z
     ??0Condition@absl@@QEAA@P6A_NPEAX@Z0@Z
     ??0Condition@absl@@QEAA@PEB_N@Z
     ??0Cord@absl@@AEAA@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@W4MethodIdentifier@CordzUpdateTracker@cord_internal@1@@Z
@@ -743,6 +743,7 @@
     ?InitDiscreteDistribution@random_internal@absl@@YA?AV?$vector@U?$pair@N_K@__Cr@std@@V?$allocator@U?$pair@N_K@__Cr@std@@@23@@__Cr@std@@PEAV?$vector@NV?$allocator@N@__Cr@std@@@45@@Z
     ?InitFrom@?$Storage@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__Cr@std@@@inlined_vector_internal@absl@@QEAAXAEBV123@@Z
     ?InitTables@CRC32@crc_internal@absl@@UEAAXXZ
+    ?InitTree@ChunkIterator@Cord@absl@@AEAAXPEAUCordRep@cord_internal@3@@Z
     ?InitWhat@BadStatusOrAccess@absl@@AEBAXXZ
     ?Initialize@ExponentialBiased@profiling_internal@absl@@AEAAXXZ
     ?InitializeCordRepExternal@cord_internal@absl@@YAXV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PEAUCordRepExternal@12@@Z
diff --git a/third_party/abseil-cpp/symbols_x64_rel.def b/third_party/abseil-cpp/symbols_x64_rel.def
index b933a5d0..b1bda62 100644
--- a/third_party/abseil-cpp/symbols_x64_rel.def
+++ b/third_party/abseil-cpp/symbols_x64_rel.def
@@ -165,6 +165,7 @@
     ??$__push_back_slow_path@AEBQEBVCordzHandle@cord_internal@absl@@@?$vector@PEBVCordzHandle@cord_internal@absl@@V?$allocator@PEBVCordzHandle@cord_internal@absl@@@__Cr@std@@@__Cr@std@@AEAAPEAPEBVCordzHandle@cord_internal@absl@@AEBQEBV345@@Z
     ??$__rehash@$00@?$__hash_table@PEBUCordRep@cord_internal@absl@@U?$hash@PEBUCordRep@cord_internal@absl@@@__Cr@std@@U?$equal_to@PEBUCordRep@cord_internal@absl@@@56@V?$allocator@PEBUCordRep@cord_internal@absl@@@56@@__Cr@std@@AEAAX_K@Z
     ??$__rehash@$00@?$__hash_table@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@V?$__unordered_map_hasher@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@$00@23@V?$__unordered_map_equal@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@$00@23@V?$allocator@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@@23@@__Cr@std@@AEAAX_K@Z
+    ??$__upper_bound@U_ClassicAlgPolicy@__Cr@std@@UByCivilTime@Transition@cctz@time_internal@absl@@PEBU5678@PEBU5678@U5678@U__identity@23@@__Cr@std@@YAPEBUTransition@cctz@time_internal@absl@@PEBU2345@0AEBU2345@$$QEAUByCivilTime@2345@$$QEAU__identity@01@@Z
     ??$assign@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEBU1234@AEBU1234@PEBQEBU1234@_J$0A@@__Cr@std@@$0A@@?$deque@UPrefixCrc@CrcCordState@crc_internal@absl@@V?$allocator@UPrefixCrc@CrcCordState@crc_internal@absl@@@__Cr@std@@@__Cr@std@@QEAAXV?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEBU1234@AEBU1234@PEBQEBU1234@_J$0A@@12@0@Z
     ??$construct_at@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@AEBUpiecewise_construct_t@23@V?$tuple@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$tuple@$$V@23@PEAU123@@__Cr@std@@YAPEAU?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@01@PEAU201@AEBUpiecewise_construct_t@01@$$QEAV?$tuple@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@01@$$QEAV?$tuple@$$V@01@@Z
     ??$construct_at@UPayload@status_internal@absl@@AEBU123@PEAU123@@__Cr@std@@YAPEAUPayload@status_internal@absl@@PEAU234@AEBU234@@Z
diff --git a/third_party/abseil-cpp/symbols_x64_rel_asan.def b/third_party/abseil-cpp/symbols_x64_rel_asan.def
index 828df1e..33ff8ea 100644
--- a/third_party/abseil-cpp/symbols_x64_rel_asan.def
+++ b/third_party/abseil-cpp/symbols_x64_rel_asan.def
@@ -170,6 +170,7 @@
     ??$__push_back_slow_path@AEBQEBVCordzHandle@cord_internal@absl@@@?$vector@PEBVCordzHandle@cord_internal@absl@@V?$allocator@PEBVCordzHandle@cord_internal@absl@@@__Cr@std@@@__Cr@std@@AEAAPEAPEBVCordzHandle@cord_internal@absl@@AEBQEBV345@@Z
     ??$__rehash@$00@?$__hash_table@PEBUCordRep@cord_internal@absl@@U?$hash@PEBUCordRep@cord_internal@absl@@@__Cr@std@@U?$equal_to@PEBUCordRep@cord_internal@absl@@@56@V?$allocator@PEBUCordRep@cord_internal@absl@@@56@@__Cr@std@@AEAAX_K@Z
     ??$__rehash@$00@?$__hash_table@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@V?$__unordered_map_hasher@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@$00@23@V?$__unordered_map_equal@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@$00@23@V?$allocator@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@@23@@__Cr@std@@AEAAX_K@Z
+    ??$__upper_bound@U_ClassicAlgPolicy@__Cr@std@@UByCivilTime@Transition@cctz@time_internal@absl@@PEBU5678@PEBU5678@U5678@U__identity@23@@__Cr@std@@YAPEBUTransition@cctz@time_internal@absl@@PEBU2345@0AEBU2345@$$QEAUByCivilTime@2345@$$QEAU__identity@01@@Z
     ??$assign@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEBU1234@AEBU1234@PEBQEBU1234@_J$0A@@__Cr@std@@$0A@@?$deque@UPrefixCrc@CrcCordState@crc_internal@absl@@V?$allocator@UPrefixCrc@CrcCordState@crc_internal@absl@@@__Cr@std@@@__Cr@std@@QEAAXV?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEBU1234@AEBU1234@PEBQEBU1234@_J$0A@@12@0@Z
     ??$construct_at@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@AEBUpiecewise_construct_t@23@V?$tuple@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$tuple@$$V@23@PEAU123@@__Cr@std@@YAPEAU?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@01@PEAU201@AEBUpiecewise_construct_t@01@$$QEAV?$tuple@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@01@$$QEAV?$tuple@$$V@01@@Z
     ??$construct_at@UPayload@status_internal@absl@@AEBU123@PEAU123@@__Cr@std@@YAPEAUPayload@status_internal@absl@@PEAU234@AEBU234@@Z
diff --git a/third_party/abseil-cpp/symbols_x86_rel.def b/third_party/abseil-cpp/symbols_x86_rel.def
index 58d4ab4b..e0109b5 100644
--- a/third_party/abseil-cpp/symbols_x86_rel.def
+++ b/third_party/abseil-cpp/symbols_x86_rel.def
@@ -172,6 +172,7 @@
     ??$__rehash@$00@?$__hash_table@PBUCordRep@cord_internal@absl@@U?$hash@PBUCordRep@cord_internal@absl@@@__Cr@std@@U?$equal_to@PBUCordRep@cord_internal@absl@@@56@V?$allocator@PBUCordRep@cord_internal@absl@@@56@@__Cr@std@@AAEXI@Z
     ??$__rehash@$00@?$__hash_table@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@V?$__unordered_map_hasher@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@$00@23@V?$__unordered_map_equal@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@$00@23@V?$allocator@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@@23@@__Cr@std@@AAEXI@Z
     ??$__unwrap_and_dispatch@U?$__overload@U?$__copy_loop@U_ClassicAlgPolicy@__Cr@std@@@__Cr@std@@U__copy_trivial@23@@__Cr@std@@PBUPrefixCrc@CrcCordState@crc_internal@absl@@PBU4567@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PAU1234@AAU1234@PAPAU1234@H$0A@@23@$0A@@__Cr@std@@YA?AU?$pair@PBUPrefixCrc@CrcCordState@crc_internal@absl@@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PAU1234@AAU1234@PAPAU1234@H$0A@@__Cr@std@@@01@PBUPrefixCrc@CrcCordState@crc_internal@absl@@0V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PAU1234@AAU1234@PAPAU1234@H$0A@@01@@Z
+    ??$__upper_bound@U_ClassicAlgPolicy@__Cr@std@@UByCivilTime@Transition@cctz@time_internal@absl@@PBU5678@PBU5678@U5678@U__identity@23@@__Cr@std@@YAPBUTransition@cctz@time_internal@absl@@PBU2345@0ABU2345@$$QAUByCivilTime@2345@$$QAU__identity@01@@Z
     ??$assign@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PBU1234@ABU1234@PBQBU1234@H$0A@@__Cr@std@@$0A@@?$deque@UPrefixCrc@CrcCordState@crc_internal@absl@@V?$allocator@UPrefixCrc@CrcCordState@crc_internal@absl@@@__Cr@std@@@__Cr@std@@QAEXV?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PBU1234@ABU1234@PBQBU1234@H$0A@@12@0@Z
     ??$construct_at@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@ABUpiecewise_construct_t@23@V?$tuple@ABV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$tuple@$$V@23@PAU123@@__Cr@std@@YAPAU?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PBVImpl@time_zone@cctz@time_internal@absl@@@01@PAU201@ABUpiecewise_construct_t@01@$$QAV?$tuple@ABV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@01@$$QAV?$tuple@$$V@01@@Z
     ??$construct_at@UPayload@status_internal@absl@@ABU123@PAU123@@__Cr@std@@YAPAUPayload@status_internal@absl@@PAU234@ABU234@@Z
diff --git a/third_party/androidx/build.gradle.template b/third_party/androidx/build.gradle.template
index 80323fe56..4e7e545 100644
--- a/third_party/androidx/build.gradle.template
+++ b/third_party/androidx/build.gradle.template
@@ -12,29 +12,18 @@
     mavenCentral()
 }
 
-dependencies.constraints {
-    // This exists to resolve an ambiguous dependency onto 2 versions of this library. "androidJvm"
-    // seemed like the more correct one to use. See crbug.com/1511597.
-    compile("androidx.sqlite:sqlite") {
-        attributes {
-            attribute(Attribute.of("org.jetbrains.kotlin.platform.type", String), "androidJvm")
-        }
-    }
-    compile("androidx.room:room-runtime") {
-        attributes {
-            attribute(Attribute.of("org.jetbrains.kotlin.platform.type", String), "androidJvm")
-        }
-    }
-    compile("androidx.lifecycle:lifecycle-runtime") {
-        attributes {
-            attribute(Attribute.of("org.jetbrains.kotlin.platform.type", String), "androidJvm")
-        }
-    }
-    androidTestCompile("androidx.lifecycle:lifecycle-runtime") {
-        attributes {
-            attribute(Attribute.of("org.jetbrains.kotlin.platform.type", String), "androidJvm")
-        }
-    }
+// We aren't entirely sure how this fixed our problems, but we were seeing
+// gradle errors where it couldn't determine which variants we wanted. The
+// seemingly obvious choice, setting "org.jetbrains.kotlin.platform.type" to
+// "androidJvm" globally did not work (although it did on a per-library basis).
+// See crbug.com/41497313 and crbug.com/1511597 for other prior issues.
+configurations.androidTestCompile {
+    attributes.attribute(Attribute.of("org.gradle.category", String), "library")
+    attributes.attribute(Attribute.of("org.gradle.usage", String), "java-runtime")
+}
+configurations.compile {
+    attributes.attribute(Attribute.of("org.gradle.category", String), "library")
+    attributes.attribute(Attribute.of("org.gradle.usage", String), "java-runtime")
 }
 
 dependencies {
diff --git a/third_party/angle b/third_party/angle
index cf77126..f431641 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit cf77126a7f89f0195d973484b8f66af65882eb4e
+Subproject commit f431641a948660f5e1709aa7cd89b16ccf93f1de
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 91eb443..965c78b 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -929,6 +929,8 @@
              base::FEATURE_DISABLED_BY_DEFAULT
 #endif
 );
+const base::FeatureParam<std::string> kFilteringScrollPredictionFilterParam{
+    &kFilteringScrollPrediction, "filter", "one_euro_filter"};
 
 BASE_FEATURE(kFixGestureScrollQueuingBug,
              "FixGestureScrollQueuingBug",
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 875119a..2918f24 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -470,6 +470,8 @@
 // Uses the kFilterName* values in ui_base_features.h as the 'filter' feature
 // param.
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kFilteringScrollPrediction);
+BLINK_COMMON_EXPORT extern const base::FeatureParam<std::string>
+    kFilteringScrollPredictionFilterParam;
 
 // (b/283408783): When enabled, first gesture scrolls on web pages that have
 // touch handlers registered will go through the normal queueing process if
diff --git a/third_party/blink/public/common/interest_group/interest_group.h b/third_party/blink/public/common/interest_group/interest_group.h
index f4e5ec6..a82be7a 100644
--- a/third_party/blink/public/common/interest_group/interest_group.h
+++ b/third_party/blink/public/common/interest_group/interest_group.h
@@ -193,6 +193,9 @@
 * ParseUpdateJson in interest_group_update_manager.cc
 * Update AdAuctionServiceImplTest.UpdateAllUpdatableFields
 
+If the new field is a required Mojo field, set a value for it in all the
+texprotos in the ad_auction_service_mojolpm_fuzzer/ directory.
+
 See crrev.com/c/3517534 for an example (adding the priority field), and also
 remember to update bidder_worklet.cc too.
 
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 3f162d9..0f82a48 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -234,6 +234,7 @@
     "web_launch/web_launch.mojom",
     "webaudio/audio_context_manager.mojom",
     "webdatabase/web_database.mojom",
+    "webid/digital_identity_request.mojom",
     "webid/federated_auth_request.mojom",
     "webid/federated_auth_request_automation.mojom",
     "webpreferences/web_preferences.mojom",
diff --git a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
index 43f218c..19a46144 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
@@ -4198,6 +4198,7 @@
   kCSSCustomStateDeprecatedSyntax = 4834,
   kFullscreenAllowedByContentSetting = 4835,
   kSharedStorageAPI_CreateWorklet_Method = 4836,
+  kCanvas2DLayers = 4837,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/mojom/webid/digital_identity_request.mojom b/third_party/blink/public/mojom/webid/digital_identity_request.mojom
new file mode 100644
index 0000000..7ea3092
--- /dev/null
+++ b/third_party/blink/public/mojom/webid/digital_identity_request.mojom
@@ -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.
+
+module blink.mojom;
+
+import "third_party/blink/public/mojom/webid/federated_auth_request.mojom";
+
+// Implementation of the proposed Digital Identity Credential API.
+//
+// Proposal: https://wicg.github.io/digital-identities
+
+// TODO(crbug.com/324115589) Move DigitalCredentialProvider from
+// federated_auth_request.mojom here.
+
+// Represents the fetch result from a digital identity request. It is
+// used to determine whether a JavaScript exception should be thrown, and what
+// the error message of such exception should say.
+enum RequestDigitalIdentityStatus {
+  kSuccess,
+  kErrorTooManyRequests,
+  kErrorCanceled,
+  kError,
+};
+
+// Create a digital identity request using the specified provider.
+// This interface is called from a renderer process and is implemented in the
+// browser process.
+interface DigitalIdentityRequest {
+  // Requests a token to be generated, given a DigitalCredentialProvider.
+  // Returns:
+  // - Status of the request.
+  // - Raw content of the token.
+  //
+  // Does not support concurrent requests. Throws an error on a new request if
+  // there is a pending request.
+  Request(DigitalCredentialProvider digital_credential_provider) =>
+      (RequestDigitalIdentityStatus status,
+      string? token);
+
+  // Aborts the pending request, if any.
+  Abort();
+};
diff --git a/third_party/blink/public/web/web_ax_object.h b/third_party/blink/public/web/web_ax_object.h
index bc495c9..ba0ee4f 100644
--- a/third_party/blink/public/web/web_ax_object.h
+++ b/third_party/blink/public/web/web_ax_object.h
@@ -104,7 +104,6 @@
   void Serialize(ui::AXNodeData* node_data,
                  ui::AXMode accessibility_mode) const;
 
-  void MarkSerializerSubtreeDirty() const;
   void OnLoadInlineTextBoxes() const;
   void SetImageAsDataNodeId(const gfx::Size& max_size) const;
   int ImageDataNodeId() const;
@@ -270,10 +269,8 @@
                          gfx::Transform& container_transform,
                          bool* clips_children = nullptr) const;
 
-  // Marks ths object as dirty (needing serialization). If subtree is true,
-  // the entire AX subtree should be invalidated as well.
+  // Marks this object as dirty (needing serialization).
   void AddDirtyObjectToSerializationQueue(
-      bool subtree,
       ax::mojom::EventFrom event_from,
       ax::mojom::Action event_from_action,
       std::vector<ui::AXEventIntent> event_intents) const;
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index bfa8b54..421b11f 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1512,12 +1512,14 @@
   sources += rebase_path(blink_core_tests_lcp_critical_path_predictor,
                          "",
                          "lcp_critical_path_predictor")
+  sources += rebase_path(blink_core_tests_loader, "", "loader")
   sources += rebase_path(blink_core_tests_mathml, "", "mathml")
   sources += rebase_path(blink_core_tests_messaging, "", "messaging")
   sources += rebase_path(blink_core_tests_mobile_metrics, "", "mobile_metrics")
   sources += rebase_path(blink_core_tests_navigation_api, "", "navigation_api")
   sources += rebase_path(blink_core_tests_origin_trials, "", "origin_trials")
   sources += rebase_path(blink_core_tests_paint, "", "paint")
+  sources += rebase_path(blink_core_tests_page, "", "page")
   sources +=
       rebase_path(blink_core_tests_permissions_policy, "", "permissions_policy")
   sources +=
diff --git a/third_party/blink/renderer/core/accessibility/ax_object_cache.h b/third_party/blink/renderer/core/accessibility/ax_object_cache.h
index a7a55df..d67706a 100644
--- a/third_party/blink/renderer/core/accessibility/ax_object_cache.h
+++ b/third_party/blink/renderer/core/accessibility/ax_object_cache.h
@@ -257,7 +257,6 @@
   // or an event from a focus action.
   virtual void AddDirtyObjectToSerializationQueue(
       AXObject* obj,
-      bool subtree,
       ax::mojom::blink::EventFrom event_from,
       ax::mojom::blink::Action event_from_action,
       const std::vector<ui::AXEventIntent>& event_intents) = 0;
diff --git a/third_party/blink/renderer/core/animation/interpolable_color.cc b/third_party/blink/renderer/core/animation/interpolable_color.cc
index 634385f..ded12fac 100644
--- a/third_party/blink/renderer/core/animation/interpolable_color.cc
+++ b/third_party/blink/renderer/core/animation/interpolable_color.cc
@@ -79,16 +79,16 @@
   // the proper fraction of the keyword color is added in.
   switch (color_keyword) {
     case ColorKeyword::kCurrentcolor:
-      result->current_color_ = InlinedInterpolableNumber(1);
+      result->current_color_ = InlinedInterpolableDouble(1);
       break;
     case ColorKeyword::kWebkitActivelink:
-      result->webkit_active_link_ = InlinedInterpolableNumber(1);
+      result->webkit_active_link_ = InlinedInterpolableDouble(1);
       break;
     case ColorKeyword::kWebkitLink:
-      result->webkit_link_ = InlinedInterpolableNumber(1);
+      result->webkit_link_ = InlinedInterpolableDouble(1);
       break;
     case ColorKeyword::kQuirkInherit:
-      result->quirk_inherit_ = InlinedInterpolableNumber(1);
+      result->quirk_inherit_ = InlinedInterpolableDouble(1);
       break;
   }
   // Keyword colors are functionally legacy colors for interpolation.
@@ -120,14 +120,14 @@
 }
 
 InterpolableColor::InterpolableColor(
-    InlinedInterpolableNumber param0,
-    InlinedInterpolableNumber param1,
-    InlinedInterpolableNumber param2,
-    InlinedInterpolableNumber alpha,
-    InlinedInterpolableNumber current_color,
-    InlinedInterpolableNumber webkit_active_link,
-    InlinedInterpolableNumber webkit_link,
-    InlinedInterpolableNumber quirk_inherit,
+    InlinedInterpolableDouble param0,
+    InlinedInterpolableDouble param1,
+    InlinedInterpolableDouble param2,
+    InlinedInterpolableDouble alpha,
+    InlinedInterpolableDouble current_color,
+    InlinedInterpolableDouble webkit_active_link,
+    InlinedInterpolableDouble webkit_link,
+    InlinedInterpolableDouble quirk_inherit,
     Color::ColorSpace color_space)
     : param0_(std::move(param0)),
       param1_(std::move(param1)),
@@ -147,10 +147,10 @@
 
 InterpolableColor* InterpolableColor::RawCloneAndZero() const {
   return MakeGarbageCollected<InterpolableColor>(
-      InlinedInterpolableNumber(0), InlinedInterpolableNumber(0),
-      InlinedInterpolableNumber(0), InlinedInterpolableNumber(0),
-      InlinedInterpolableNumber(0), InlinedInterpolableNumber(0),
-      InlinedInterpolableNumber(0), InlinedInterpolableNumber(0), color_space_);
+      InlinedInterpolableDouble(0), InlinedInterpolableDouble(0),
+      InlinedInterpolableDouble(0), InlinedInterpolableDouble(0),
+      InlinedInterpolableDouble(0), InlinedInterpolableDouble(0),
+      InlinedInterpolableDouble(0), InlinedInterpolableDouble(0), color_space_);
 }
 
 Color InterpolableColor::GetColor() const {
diff --git a/third_party/blink/renderer/core/animation/interpolable_color.h b/third_party/blink/renderer/core/animation/interpolable_color.h
index 9e7dccc1..2b41a1f 100644
--- a/third_party/blink/renderer/core/animation/interpolable_color.h
+++ b/third_party/blink/renderer/core/animation/interpolable_color.h
@@ -109,14 +109,14 @@
     v->Trace(quirk_inherit_);
   }
 
-  InterpolableColor(InlinedInterpolableNumber param0,
-                    InlinedInterpolableNumber param1,
-                    InlinedInterpolableNumber param2,
-                    InlinedInterpolableNumber alpha,
-                    InlinedInterpolableNumber current_color,
-                    InlinedInterpolableNumber webkit_active_link,
-                    InlinedInterpolableNumber webkit_link,
-                    InlinedInterpolableNumber quirk_inherit,
+  InterpolableColor(InlinedInterpolableDouble param0,
+                    InlinedInterpolableDouble param1,
+                    InlinedInterpolableDouble param2,
+                    InlinedInterpolableDouble alpha,
+                    InlinedInterpolableDouble current_color,
+                    InlinedInterpolableDouble webkit_active_link,
+                    InlinedInterpolableDouble webkit_link,
+                    InlinedInterpolableDouble quirk_inherit,
                     Color::ColorSpace color_space);
 
  private:
@@ -126,15 +126,15 @@
 
   // All color params are stored premultiplied by alpha.
   // https://csswg.sesse.net/css-color-4/#interpolation-space
-  InlinedInterpolableNumber param0_;
-  InlinedInterpolableNumber param1_;
-  InlinedInterpolableNumber param2_;
-  InlinedInterpolableNumber alpha_;
+  InlinedInterpolableDouble param0_;
+  InlinedInterpolableDouble param1_;
+  InlinedInterpolableDouble param2_;
+  InlinedInterpolableDouble alpha_;
 
-  InlinedInterpolableNumber current_color_;
-  InlinedInterpolableNumber webkit_active_link_;
-  InlinedInterpolableNumber webkit_link_;
-  InlinedInterpolableNumber quirk_inherit_;
+  InlinedInterpolableDouble current_color_;
+  InlinedInterpolableDouble webkit_active_link_;
+  InlinedInterpolableDouble webkit_link_;
+  InlinedInterpolableDouble quirk_inherit_;
 
   Color::ColorSpace color_space_ = Color::ColorSpace::kNone;
 };
diff --git a/third_party/blink/renderer/core/animation/interpolable_value.cc b/third_party/blink/renderer/core/animation/interpolable_value.cc
index 89d627e..3be7021 100644
--- a/third_party/blink/renderer/core/animation/interpolable_value.cc
+++ b/third_party/blink/renderer/core/animation/interpolable_value.cc
@@ -24,6 +24,31 @@
 
 }  // namespace
 
+bool InterpolableDouble::Equals(const InterpolableValue& other) const {
+  return value_.Value() == To<InterpolableDouble>(other).value_.Value();
+}
+
+void InterpolableDouble::Interpolate(const InterpolableValue& to,
+                                     const double progress,
+                                     InterpolableValue& result) const {
+  const auto& to_number = To<InterpolableDouble>(to);
+  auto& result_number = To<InterpolableDouble>(result);
+  result_number.Set(value_.Interpolate(to_number.Value(), progress));
+}
+
+void InterpolableDouble::Scale(double scale) {
+  value_.Scale(scale);
+}
+
+void InterpolableDouble::Add(const InterpolableValue& other) {
+  value_.Add(To<InterpolableDouble>(other).value_.Value());
+}
+
+void InterpolableDouble::AssertCanInterpolateWith(
+    const InterpolableValue& other) const {
+  DCHECK(other.IsDouble());
+}
+
 InterpolableNumber::InterpolableNumber(double value) {
   SetDouble(value);
 }
@@ -35,7 +60,7 @@
 
 double InterpolableNumber::Value(
     const CSSLengthResolver& length_resolver) const {
-  if (IsDouble()) {
+  if (IsDoubleValue()) {
     return value_.Value();
   }
   return expression_->ComputeNumber(length_resolver);
@@ -60,7 +85,7 @@
 }
 
 bool InterpolableNumber::Equals(const InterpolableValue& other) const {
-  if (IsDouble()) {
+  if (IsDoubleValue()) {
     return value_.Value() == To<InterpolableNumber>(other).value_.Value();
   }
   return expression_->CustomCSSText() ==
@@ -78,7 +103,7 @@
   return true;
 }
 
-double InlinedInterpolableNumber::Interpolate(double to,
+double InlinedInterpolableDouble::Interpolate(double to,
                                               const double progress) const {
   if (progress == 0 || value_ == to) {
     return value_;
@@ -99,7 +124,7 @@
                                      InterpolableValue& result) const {
   const auto& to_number = To<InterpolableNumber>(to);
   auto& result_number = To<InterpolableNumber>(result);
-  if (IsDouble()) {
+  if (IsDoubleValue()) {
     result_number.SetDouble(value_.Interpolate(to_number.Value(), progress));
     return;
   }
@@ -153,7 +178,7 @@
 }
 
 void InterpolableNumber::Scale(double scale) {
-  if (IsDouble()) {
+  if (IsDoubleValue()) {
     value_.Scale(scale);
     return;
   }
@@ -168,7 +193,7 @@
 }
 
 void InterpolableNumber::Add(const InterpolableValue& other) {
-  if (IsDouble()) {
+  if (IsDoubleValue()) {
     value_.Add(To<InterpolableNumber>(other).value_.Value());
     return;
   }
diff --git a/third_party/blink/renderer/core/animation/interpolable_value.h b/third_party/blink/renderer/core/animation/interpolable_value.h
index bda7fe6..bf1f2a2 100644
--- a/third_party/blink/renderer/core/animation/interpolable_value.h
+++ b/third_party/blink/renderer/core/animation/interpolable_value.h
@@ -35,6 +35,7 @@
                            const double progress,
                            InterpolableValue& result) const = 0;
 
+  virtual bool IsDouble() const { return false; }
   virtual bool IsNumber() const { return false; }
   virtual bool IsBool() const { return false; }
   virtual bool IsColor() const { return false; }
@@ -83,12 +84,12 @@
   virtual InterpolableValue* RawCloneAndZero() const = 0;
 };
 
-class CORE_EXPORT InlinedInterpolableNumber final {
+class CORE_EXPORT InlinedInterpolableDouble final {
   DISALLOW_NEW();
 
  public:
-  InlinedInterpolableNumber() = default;
-  explicit InlinedInterpolableNumber(double d) : value_(d) {}
+  InlinedInterpolableDouble() = default;
+  explicit InlinedInterpolableDouble(double d) : value_(d) {}
 
   double Value() const { return value_; }
   void Set(double value) { value_ = value; }
@@ -107,6 +108,46 @@
   double value_ = 0.;
 };
 
+class CORE_EXPORT InterpolableDouble final : public InterpolableValue {
+ public:
+  InterpolableDouble() = default;
+  explicit InterpolableDouble(double value) : value_(value) {
+    static_assert(std::is_trivially_destructible_v<InterpolableDouble>,
+                  "Require trivial destruction for faster sweeping");
+  }
+
+  double Value() const { return value_.Value(); }
+  void Set(double value) { value_.Set(value); }
+
+  // InterpolableValue
+  void Interpolate(const InterpolableValue& to,
+                   const double progress,
+                   InterpolableValue& result) const final;
+  bool IsDouble() const final { return true; }
+  bool Equals(const InterpolableValue& other) const final;
+  void Scale(double scale) final;
+  void Add(const InterpolableValue& other) final;
+  void AssertCanInterpolateWith(const InterpolableValue& other) const final;
+
+  InterpolableDouble* Clone() const { return RawClone(); }
+  InterpolableDouble* CloneAndZero() const { return RawCloneAndZero(); }
+
+  void Trace(Visitor* v) const override {
+    InterpolableValue::Trace(v);
+    v->Trace(value_);
+  }
+
+ private:
+  InterpolableDouble* RawClone() const final {
+    return MakeGarbageCollected<InterpolableDouble>(value_.Value());
+  }
+  InterpolableDouble* RawCloneAndZero() const final {
+    return MakeGarbageCollected<InterpolableDouble>(0);
+  }
+
+  InlinedInterpolableDouble value_;
+};
+
 class CORE_EXPORT InterpolableNumber final : public InterpolableValue {
  public:
   InterpolableNumber() = default;
@@ -138,7 +179,7 @@
 
  private:
   InterpolableNumber* RawClone() const final {
-    if (IsDouble()) {
+    if (IsDoubleValue()) {
       return MakeGarbageCollected<InterpolableNumber>(value_.Value());
     }
     return MakeGarbageCollected<InterpolableNumber>(*expression_);
@@ -147,7 +188,7 @@
     return MakeGarbageCollected<InterpolableNumber>(0);
   }
 
-  bool IsDouble() const { return type_ == Type::kDouble; }
+  bool IsDoubleValue() const { return type_ == Type::kDouble; }
   bool IsExpression() const { return type_ == Type::kExpression; }
 
   void SetDouble(double value);
@@ -156,7 +197,7 @@
 
   enum class Type { kDouble, kExpression };
   Type type_;
-  InlinedInterpolableNumber value_;
+  InlinedInterpolableDouble value_;
   Member<const CSSMathExpressionNode> expression_;
 };
 
@@ -223,6 +264,13 @@
 };
 
 template <>
+struct DowncastTraits<InterpolableDouble> {
+  static bool AllowFrom(const InterpolableValue& value) {
+    return value.IsDouble();
+  }
+};
+
+template <>
 struct DowncastTraits<InterpolableNumber> {
   static bool AllowFrom(const InterpolableValue& value) {
     return value.IsNumber();
diff --git a/third_party/blink/renderer/core/animation/interpolable_value_test.cc b/third_party/blink/renderer/core/animation/interpolable_value_test.cc
index f6226bf..6e0e498 100644
--- a/third_party/blink/renderer/core/animation/interpolable_value_test.cc
+++ b/third_party/blink/renderer/core/animation/interpolable_value_test.cc
@@ -43,6 +43,14 @@
         .Value();
   }
 
+  double InterpolateDoubles(int a, int b, double progress) {
+    auto* start = MakeGarbageCollected<InterpolableDouble>(a);
+    auto* end = MakeGarbageCollected<InterpolableDouble>(b);
+    auto* result = MakeGarbageCollected<InterpolableDouble>(0);
+    start->Interpolate(*end, progress, *result);
+    return result->Value();
+  }
+
   void ScaleAndAdd(InterpolableValue& base,
                    double scale,
                    const InterpolableValue& add) {
@@ -68,6 +76,15 @@
   EXPECT_FLOAT_EQ(-21, InterpolateNumbers(42, 0, 1.5));
 }
 
+TEST_F(AnimationInterpolableValueTest, InterpolateDoubles) {
+  EXPECT_FLOAT_EQ(126, InterpolateDoubles(42, 0, -2));
+  EXPECT_FLOAT_EQ(42, InterpolateDoubles(42, 0, 0));
+  EXPECT_FLOAT_EQ(29.4f, InterpolateDoubles(42, 0, 0.3));
+  EXPECT_FLOAT_EQ(21, InterpolateDoubles(42, 0, 0.5));
+  EXPECT_FLOAT_EQ(0, InterpolateDoubles(42, 0, 1));
+  EXPECT_FLOAT_EQ(-21, InterpolateDoubles(42, 0, 1.5));
+}
+
 TEST_F(AnimationInterpolableValueTest, SimpleList) {
   auto* list_a = MakeGarbageCollected<InterpolableList>(3);
   list_a->Set(0, MakeGarbageCollected<InterpolableNumber>(0));
@@ -127,6 +144,20 @@
   EXPECT_FLOAT_EQ(-2, base->Value());
 }
 
+TEST_F(AnimationInterpolableValueTest, ScaleAndAddDoubles) {
+  InterpolableDouble* base = MakeGarbageCollected<InterpolableDouble>(10);
+  ScaleAndAdd(*base, 2, *MakeGarbageCollected<InterpolableDouble>(1));
+  EXPECT_FLOAT_EQ(21, base->Value());
+
+  base = MakeGarbageCollected<InterpolableDouble>(10);
+  ScaleAndAdd(*base, 0, *MakeGarbageCollected<InterpolableDouble>(5));
+  EXPECT_FLOAT_EQ(5, base->Value());
+
+  base = MakeGarbageCollected<InterpolableDouble>(10);
+  ScaleAndAdd(*base, -1, *MakeGarbageCollected<InterpolableDouble>(8));
+  EXPECT_FLOAT_EQ(-2, base->Value());
+}
+
 TEST_F(AnimationInterpolableValueTest, ScaleAndAddLists) {
   auto* base_list = MakeGarbageCollected<InterpolableList>(3);
   base_list->Set(0, MakeGarbageCollected<InterpolableNumber>(5));
diff --git a/third_party/blink/renderer/core/clipboard/clipboard_utilities.cc b/third_party/blink/renderer/core/clipboard/clipboard_utilities.cc
index 0c1964a0..2ae53b97 100644
--- a/third_party/blink/renderer/core/clipboard/clipboard_utilities.cc
+++ b/third_party/blink/renderer/core/clipboard/clipboard_utilities.cc
@@ -104,4 +104,50 @@
   return markup.ToString();
 }
 
+// NSPasteboardTypeHTML does not define what encoding should be used, and if
+// no character encoding is specified, it is likely that the data will be
+// interpreted as ISO-8859-1, even with modern releases like macOS 14.2.
+// (https://crbug.com/11957)
+//
+// (Note that Safari does not encounter this issue because in addition to
+// adding the data to the pasteboard as a NSPasteboardTypeHTML flavor, it also
+// adds it as a UTTypeWebArchive type which includes the encoding.)
+//
+// This issue has been filed as FB13522476. When this feedback is addressed
+// and NSPasteboardTypeHTML is interpreted as UTF-8, remove the code that adds
+// a charset declaration.
+#if BUILDFLAG(IS_MAC)
+static constexpr char kMetaTag[] = "<meta charset=\"utf-8\">";
+static constexpr unsigned kMetaTagLength = sizeof(kMetaTag) - 1;
+#endif
+
+String AddMetaCharsetTagToHtmlOnMac(const String& html) {
+#if BUILDFLAG(IS_MAC)
+  if (html.Find(kMetaTag) == kNotFound) {
+    StringBuilder html_result;
+    html_result.Append(kMetaTag);
+    html_result.Append(html);
+    return html_result.ToString();
+  }
+#endif
+  return html;
+}
+
+String RemoveMetaTagAndCalcFragmentOffsetsFromHtmlOnMac(
+    const String& html,
+    unsigned& fragment_start,
+    unsigned& fragment_end) {
+#if BUILDFLAG(IS_MAC)
+  DCHECK_EQ(fragment_start, 0u);
+  DCHECK_EQ(fragment_end, html.length());
+  if (html.StartsWith(kMetaTag)) {
+    const String html_fragment = html.Substring(kMetaTagLength);
+    fragment_start = 0;
+    fragment_end = html_fragment.length();
+    return html_fragment;
+  }
+#endif
+  return html;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/clipboard/clipboard_utilities.h b/third_party/blink/renderer/core/clipboard/clipboard_utilities.h
index c4adbe0f..dcddb35 100644
--- a/third_party/blink/renderer/core/clipboard/clipboard_utilities.h
+++ b/third_party/blink/renderer/core/clipboard/clipboard_utilities.h
@@ -47,6 +47,11 @@
 CORE_EXPORT String ConvertURIListToURL(const String& uri_list);
 CORE_EXPORT String URLToImageMarkup(const KURL&, const String& title);
 CORE_EXPORT String PNGToImageMarkup(const mojo_base::BigBuffer& png_data);
+CORE_EXPORT String AddMetaCharsetTagToHtmlOnMac(const String& html);
+CORE_EXPORT String
+RemoveMetaTagAndCalcFragmentOffsetsFromHtmlOnMac(const String& html,
+                                                 unsigned& fragment_start,
+                                                 unsigned& fragment_end);
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/core/clipboard/clipboard_utilities_test.cc b/third_party/blink/renderer/core/clipboard/clipboard_utilities_test.cc
index dc65917..b5d82189 100644
--- a/third_party/blink/renderer/core/clipboard/clipboard_utilities_test.cc
+++ b/third_party/blink/renderer/core/clipboard/clipboard_utilities_test.cc
@@ -72,4 +72,39 @@
       PNGToImageMarkup(png));
 }
 
+TEST(ClipboardUtilitiesTest, AddMetaCharsetTagToHtmlOnMac) {
+  const String html_markup = "<p>Test</p>";
+  const String expected_html_markup = "<meta charset=\"utf-8\"><p>Test</p>";
+#if BUILDFLAG(IS_MAC)
+  EXPECT_EQ(AddMetaCharsetTagToHtmlOnMac(html_markup), expected_html_markup);
+#else
+  EXPECT_EQ(AddMetaCharsetTagToHtmlOnMac(html_markup), html_markup);
+#endif
+}
+
+TEST(ClipboardUtilitiesTest, RemoveMetaTagAndCalcFragmentOffsetsFromHtmlOnMac) {
+#if BUILDFLAG(IS_MAC)
+  const String expected_html_markup = "<p>Test</p>";
+  const String html_markup = "<meta charset=\"utf-8\"><p>Test</p>";
+  unsigned fragment_start = 0;
+  unsigned fragment_end = html_markup.length();
+  const String actual_value = RemoveMetaTagAndCalcFragmentOffsetsFromHtmlOnMac(
+      html_markup, fragment_start, fragment_end);
+  EXPECT_EQ(actual_value, expected_html_markup);
+  EXPECT_EQ(fragment_start, 0u);
+  // Meta tag is not part of the copied fragment.
+  EXPECT_EQ(fragment_end, expected_html_markup.length());
+#else
+  const String html_markup = "<p>Test</p>";
+  unsigned fragment_start = 0;
+  unsigned fragment_end = html_markup.length();
+  const String actual_value = RemoveMetaTagAndCalcFragmentOffsetsFromHtmlOnMac(
+      html_markup, fragment_start, fragment_end);
+  // On non-Mac platforms, the HTML markup is unchanged.
+  EXPECT_EQ(actual_value, html_markup);
+  EXPECT_EQ(fragment_start, 0u);
+  EXPECT_EQ(fragment_end, html_markup.length());
+#endif
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/clipboard/data_object_item.cc b/third_party/blink/renderer/core/clipboard/data_object_item.cc
index f91e3e6b..73c24ca 100644
--- a/third_party/blink/renderer/core/clipboard/data_object_item.cc
+++ b/third_party/blink/renderer/core/clipboard/data_object_item.cc
@@ -36,6 +36,7 @@
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_data_transfer_token.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/core/clipboard/clipboard_mime_types.h"
+#include "third_party/blink/renderer/core/clipboard/clipboard_utilities.h"
 #include "third_party/blink/renderer/core/clipboard/system_clipboard.h"
 #include "third_party/blink/renderer/core/fileapi/blob.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
@@ -206,8 +207,15 @@
     data = system_clipboard_->ReadRTF();
   } else if (type_ == kMimeTypeTextHTML) {
     KURL ignored_source_url;
-    unsigned ignored;
-    data = system_clipboard_->ReadHTML(ignored_source_url, ignored, ignored);
+    unsigned ignored_start = 0;
+    unsigned ignored_end = 0;
+    data = system_clipboard_->ReadHTML(ignored_source_url, ignored_start,
+                                       ignored_end);
+    // On Mac, remove meta charset tag that was added for compatibility with
+    // native apps. See comments in AddMetaCharsetTagToHtmlOnMac for more
+    // details.
+    data = RemoveMetaTagAndCalcFragmentOffsetsFromHtmlOnMac(data, ignored_start,
+                                                            ignored_end);
   } else {
     data = system_clipboard_->ReadCustomData(type_);
   }
diff --git a/third_party/blink/renderer/core/editing/commands/clipboard_commands.cc b/third_party/blink/renderer/core/editing/commands/clipboard_commands.cc
index 20ebd3f2f..77b70c9 100644
--- a/third_party/blink/renderer/core/editing/commands/clipboard_commands.cc
+++ b/third_party/blink/renderer/core/editing/commands/clipboard_commands.cc
@@ -285,7 +285,9 @@
   const KURL& url = frame.GetDocument()->Url();
   const String html = frame.Selection().SelectedHTMLForClipboard();
   String plain_text = frame.SelectedTextForClipboard();
-  frame.GetSystemClipboard()->WriteHTML(html, url,
+  // On Mac, add a meta charset tag for compatibility with native apps.
+  // See comments in AddMetaCharsetTagToHtmlOnMac for more details.
+  frame.GetSystemClipboard()->WriteHTML(AddMetaCharsetTagToHtmlOnMac(html), url,
                                         GetSmartReplaceOption(frame));
   ReplaceNBSPWithSpace(plain_text);
   frame.GetSystemClipboard()->WritePlainText(plain_text,
@@ -460,10 +462,15 @@
     unsigned fragment_start = 0;
     unsigned fragment_end = 0;
     KURL url;
+    // On Mac, remove meta charset tag that was added for compatibility with
+    // native apps. See comments in AddMetaCharsetTagToHtmlOnMac for more
+    // details.
     const String markup =
         frame.GetSystemClipboard()->ReadHTML(url, fragment_start, fragment_end);
+    const String html_markup = RemoveMetaTagAndCalcFragmentOffsetsFromHtmlOnMac(
+        markup, fragment_start, fragment_end);
     fragment = CreateStrictlyProcessedFragmentFromMarkupWithContext(
-        *frame.GetDocument(), markup, fragment_start, fragment_end, url);
+        *frame.GetDocument(), html_markup, fragment_start, fragment_end, url);
   }
   if (fragment)
     return std::make_pair(fragment, false);
diff --git a/third_party/blink/renderer/core/editing/commands/editor_command.cc b/third_party/blink/renderer/core/editing/commands/editor_command.cc
index 7b574b6..f2077b4 100644
--- a/third_party/blink/renderer/core/editing/commands/editor_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/editor_command.cc
@@ -1130,9 +1130,17 @@
 static bool EnabledInEditableText(LocalFrame& frame,
                                   Event* event,
                                   EditorCommandSource source) {
-  if (source == EditorCommandSource::kDOM &&
-      frame.GetInputMethodController().GetActiveEditContext()) {
-    return false;
+  if (frame.GetInputMethodController().GetActiveEditContext()) {
+    if (source == EditorCommandSource::kDOM) {
+      return false;
+    } else if (source == EditorCommandSource::kMenuOrKeyBinding) {
+      // If there's an active EditContext, always give the EditContext
+      // a chance to handle menu or key binding commands regardless
+      // of the selection position. This is important for the case
+      // where the EditContext's associated element is a <canvas>,
+      // which cannot contain selection; only focus.
+      return true;
+    }
   }
 
   frame.GetDocument()->UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
index 87f05aa..93e859f 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
@@ -459,7 +459,10 @@
     return;
 
   LocalFrame* frame = element_->GetDocument().GetFrame();
-  frame->GetSystemClipboard()->WriteHTML(web_plugin_->SelectionAsMarkup(),
+  String html = web_plugin_->SelectionAsMarkup();
+  // On Mac, add a meta charset tag for compatibility with native apps.
+  // See comments in AddMetaCharsetTagToHtmlOnMac for more details.
+  frame->GetSystemClipboard()->WriteHTML(AddMetaCharsetTagToHtmlOnMac(html),
                                          KURL());
   String text = web_plugin_->SelectionAsText();
   ReplaceNBSPWithSpace(text);
diff --git a/third_party/blink/renderer/core/fetch/fetch_manager.cc b/third_party/blink/renderer/core/fetch/fetch_manager.cc
index 89ce85470..d6652e9 100644
--- a/third_party/blink/renderer/core/fetch/fetch_manager.cc
+++ b/third_party/blink/renderer/core/fetch/fetch_manager.cc
@@ -716,8 +716,7 @@
     // https://fetch.spec.whatwg.org/#fetching
     // The user agent should ignore the suspension request if the ongoing
     // fetch is updating the response in the HTTP cache for the request.
-    place_holder_body_->Update(BufferingBytesConsumer::CreateWithDelay(
-        &body, GetExecutionContext()->GetTaskRunner(TaskType::kNetworking)));
+    place_holder_body_->Update(BufferingBytesConsumer::CreateWithDelay(&body));
   } else {
     place_holder_body_->Update(&body);
   }
diff --git a/third_party/blink/renderer/core/html/forms/html_label_element.cc b/third_party/blink/renderer/core/html/forms/html_label_element.cc
index 7d3cb23..ae12a209 100644
--- a/third_party/blink/renderer/core/html/forms/html_label_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_label_element.cc
@@ -139,20 +139,31 @@
 }
 
 void HTMLLabelElement::DefaultEventHandler(Event& evt) {
+  if (DefaultEventHandlerInternal(evt) ||
+      RuntimeEnabledFeatures::LabelEventHandlerCallSuperEnabled()) {
+    HTMLElement::DefaultEventHandler(evt);
+  }
+}
+
+// If this returns false, then it means that we should not run
+// HTMLElement::DefaultEventHandler when LabelEventHandlerCallSuper is disabled
+// to emulate old behavior.
+// TODO(crbug.com/1523168): Remove this method when the flag is removed.
+bool HTMLLabelElement::DefaultEventHandlerInternal(Event& evt) {
   if (evt.type() == event_type_names::kClick && !processing_click_) {
     HTMLElement* element = control();
 
     // If we can't find a control or if the control received the click
     // event, then there's no need for us to do anything.
     if (!element)
-      return;
+      return false;
     Node* target_node = evt.target() ? evt.target()->ToNode() : nullptr;
     if (target_node) {
       if (element->IsShadowIncludingInclusiveAncestorOf(*target_node))
-        return;
+        return false;
 
       if (IsInInteractiveContent(target_node))
-        return;
+        return false;
     }
 
     //   Behaviour of label element is as follows:
@@ -193,7 +204,7 @@
           // Only in case of drag, *neither* we pass the click event,
           // *nor* we focus the control element.
           if (mouse_event->ClickCount() == 1)
-            return;
+            return false;
         }
       }
     }
@@ -221,7 +232,7 @@
     evt.SetDefaultHandled();
   }
 
-  HTMLElement::DefaultEventHandler(evt);
+  return true;
 }
 
 bool HTMLLabelElement::HasActivationBehavior() const {
diff --git a/third_party/blink/renderer/core/html/forms/html_label_element.h b/third_party/blink/renderer/core/html/forms/html_label_element.h
index 1f46adf..6759d88 100644
--- a/third_party/blink/renderer/core/html/forms/html_label_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_label_element.h
@@ -53,6 +53,7 @@
 
   // Overridden to either click() or focus() the corresponding control.
   void DefaultEventHandler(Event&) override;
+  bool DefaultEventHandlerInternal(Event&);
   bool HasActivationBehavior() const override;
 
   void Focus(const FocusParams&) override;
diff --git a/third_party/blink/renderer/core/layout/box_fragment_builder.cc b/third_party/blink/renderer/core/layout/box_fragment_builder.cc
index f09bc5fe..69c8a80cb 100644
--- a/third_party/blink/renderer/core/layout/box_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/box_fragment_builder.cc
@@ -381,14 +381,37 @@
        !child_fragment.IsFloatingOrOutOfFlowPositioned()) ||
       child_layout_result.ShouldForceSameFragmentationFlow();
 
+  // If we're paginated, monolithic overflow will be placed on subsequent pages,
+  // even though there are no fragments for the content there. We need to be
+  // aware of such overflow when laying out subsequent pages, so that we can
+  // move past it, rather than overlapping with it. This approach works (kind
+  // of) because in our implementation, pages are stacked in the block
+  // direction, so that the block-start offset of the next page is the same as
+  // the block-end offset of the preceding page.
+  //
+  // We need to reserve space for monolithic overflow caused by any child that
+  // is in the same flow as its parent, so that subsequent content in this flow
+  // gets pushed below the monolithic overflow. If we're at the root, even
+  // include content from parallel flows, since we want to encompass everything
+  // in that case, in order to create enough pages for it.
+  //
+  // Some children disable this monolithic overflow propagation, if they are
+  // out-of-flow and inside another out-of-flow (so that the containing block
+  // chain is broken), and the outer out-of-flow has clipped overflow.
+  //
+  // TODO(mstensho): Figure out if the !IsFragmentainerBoxType() condition below
+  // makes any sense.
   if (GetConstraintSpace().IsPaginated() &&
       ((child_is_in_same_flow && !IsFragmentainerBoxType()) ||
-       Node().IsPaginatedRoot())) {
+       Node().IsPaginatedRoot()) &&
+      (!child_box_fragment ||
+       !child_box_fragment->IsMonolithicOverflowPropagationDisabled())) {
     DCHECK(GetConstraintSpace().HasKnownFragmentainerBlockSize());
     // Include overflow inside monolithic content if this is for a page
     // fragment. Otherwise just use the fragment size.
     LayoutUnit block_size;
-    if (Node().IsPaginatedRoot()) {
+    if (Node().IsPaginatedRoot() &&
+        !child_fragment.HasNonVisibleBlockOverflow()) {
       // The root node is guaranteed to be block-level, so there should be a
       // child box fragment here.
       DCHECK(child_box_fragment);
@@ -407,11 +430,7 @@
         fragment_block_end - FragmentainerSpaceLeft(GetConstraintSpace());
     if (fragmentainer_overflow > LayoutUnit()) {
       // This child overflows the page, because there's something monolithic
-      // inside. We need to be aware of this when laying out subsequent pages,
-      // so that we can move past it, rather than overlapping with it. This
-      // approach works (kind of) because in our implementation, pages are
-      // stacked in the block direction, so that the block-start offset of the
-      // next page is the same as the block-end offset of the preceding page.
+      // inside.
       ReserveSpaceForMonolithicOverflow(fragmentainer_overflow);
     }
   }
diff --git a/third_party/blink/renderer/core/layout/constraint_space.h b/third_party/blink/renderer/core/layout/constraint_space.h
index aa9d17c1..e6d0bbd 100644
--- a/third_party/blink/renderer/core/layout/constraint_space.h
+++ b/third_party/blink/renderer/core/layout/constraint_space.h
@@ -542,6 +542,17 @@
     return HasRareData() && rare_data_->is_block_fragmentation_forced_off;
   }
 
+  // Return true if monolithic overflow isn't to be propagated when printing.
+  // This is required when there's a tall monolithic abspos inside another
+  // abspos (or relpos) that has clipped overflow. Normally (non-OOF) it's not
+  // necessary to set such a flag, since we check for clipping when propagating
+  // up the tree, but OOF fragmentation breaks the containing block chain, so
+  // that any clipping ancestor won't be seen.
+  bool IsMonolithicOverflowPropagationDisabled() const {
+    return HasRareData() &&
+           rare_data_->is_monolithic_overflow_propagation_disabled;
+  }
+
   // Return true if the document is paginated (for printing).
   bool IsPaginated() const {
     // TODO(layout-dev): This will not work correctly if establishing a nested
@@ -882,6 +893,7 @@
           block_direction_fragmentation_type(
               static_cast<unsigned>(kFragmentNone)),
           is_block_fragmentation_forced_off(false),
+          is_monolithic_overflow_propagation_disabled(false),
           requires_content_before_breaking(false),
           is_inside_balanced_columns(false),
           should_ignore_forced_breaks(false),
@@ -915,6 +927,8 @@
               other.block_direction_fragmentation_type),
           is_block_fragmentation_forced_off(
               other.is_block_fragmentation_forced_off),
+          is_monolithic_overflow_propagation_disabled(
+              other.is_monolithic_overflow_propagation_disabled),
           requires_content_before_breaking(
               other.requires_content_before_breaking),
           is_inside_balanced_columns(other.is_inside_balanced_columns),
@@ -1002,6 +1016,8 @@
               other.block_direction_fragmentation_type ||
           is_block_fragmentation_forced_off !=
               other.is_block_fragmentation_forced_off ||
+          is_monolithic_overflow_propagation_disabled !=
+              other.is_monolithic_overflow_propagation_disabled ||
           requires_content_before_breaking !=
               other.requires_content_before_breaking ||
           is_inside_balanced_columns != other.is_inside_balanced_columns ||
@@ -1011,8 +1027,9 @@
           min_break_appeal != other.min_break_appeal ||
           propagate_child_break_values != other.propagate_child_break_values ||
           should_repeat != other.should_repeat ||
-          is_inside_repeatable_content != other.is_inside_repeatable_content)
+          is_inside_repeatable_content != other.is_inside_repeatable_content) {
         return false;
+      }
 
       switch (GetDataUnionType()) {
         case DataUnionType::kNone:
@@ -1044,12 +1061,14 @@
           hide_table_cell_if_empty ||
           block_direction_fragmentation_type != kFragmentNone ||
           is_block_fragmentation_forced_off ||
+          is_monolithic_overflow_propagation_disabled ||
           requires_content_before_breaking || is_inside_balanced_columns ||
           should_ignore_forced_breaks || is_in_column_bfc || is_past_break ||
           min_break_appeal != kBreakAppealLastResort ||
           propagate_child_break_values || is_at_fragmentainer_start ||
-          should_repeat || is_inside_repeatable_content)
+          should_repeat || is_inside_repeatable_content) {
         return false;
+      }
 
       switch (GetDataUnionType()) {
         case DataUnionType::kNone:
@@ -1313,6 +1332,7 @@
 
     unsigned block_direction_fragmentation_type : 2;
     unsigned is_block_fragmentation_forced_off : 1;
+    unsigned is_monolithic_overflow_propagation_disabled : 1;
     unsigned requires_content_before_breaking : 1;
     unsigned is_inside_balanced_columns : 1;
     unsigned should_ignore_forced_breaks : 1;
@@ -1643,6 +1663,10 @@
     rare_data_->is_block_fragmentation_forced_off = true;
   }
 
+  void DisableMonolithicOverflowPropagation() {
+    EnsureRareData()->is_monolithic_overflow_propagation_disabled = true;
+  }
+
   LogicalSize available_size_;
 
   // To save a little space, we union these two fields. rare_data_ is valid if
diff --git a/third_party/blink/renderer/core/layout/constraint_space_builder.h b/third_party/blink/renderer/core/layout/constraint_space_builder.h
index 1a4bf84..0619431 100644
--- a/third_party/blink/renderer/core/layout/constraint_space_builder.h
+++ b/third_party/blink/renderer/core/layout/constraint_space_builder.h
@@ -178,6 +178,9 @@
   }
 
   void DisableFurtherFragmentation() { space_.DisableFurtherFragmentation(); }
+  void DisableMonolithicOverflowPropagation() {
+    space_.DisableMonolithicOverflowPropagation();
+  }
 
   void SetIsFixedInlineSize(bool b) {
     if (LIKELY(is_in_parallel_flow_))
diff --git a/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc
index 5e17d34..844accd4 100644
--- a/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc
+++ b/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc
@@ -1587,6 +1587,10 @@
       auto& children = FragmentationContextChildren();
       wtf_size_t num_children = children.size();
 
+      // Even if all OOFs are done creating fragments, we need to create enough
+      // fragmentainers to encompass all monolithic overflow when printing.
+      LayoutUnit monolithic_overflow;
+
       // Set to true if an OOF inside a fragmentainer breaks. This does not
       // include repeated fixed-positioned elements.
       bool last_fragmentainer_has_break_inside = false;
@@ -1622,7 +1626,8 @@
           last_fragmentainer_has_break_inside = false;
           LayoutOOFsInFragmentainer(
               pending_descendants, index, fragmentainer_progression,
-              &last_fragmentainer_has_break_inside, &fragmented_descendants);
+              &monolithic_overflow, &last_fragmentainer_has_break_inside,
+              &fragmented_descendants);
 
           // Retrieve the updated or newly added fragmentainer, and add its
           // block contribution to the consumed block size. Skip this if we are
@@ -1640,11 +1645,15 @@
 
         // Extend |descendants_to_layout| if an OOF element fragments into a
         // fragmentainer at an index that does not yet exist in
-        // |descendants_to_layout|. At the same time we need to make sure that
-        // repeated fixed-positioned elements don't trigger creation of
-        // additional fragmentainers (since they'd just repeat forever).
+        // |descendants_to_layout|. We also need to do this if there's
+        // monolithic overflow (when printing), so that there are enough
+        // fragmentainers to paint the overflow. At the same time we need to
+        // make sure that repeated fixed-positioned elements don't trigger
+        // creation of additional fragmentainers on their own (since they'd just
+        // repeat forever).
         if (index == descendants_to_layout.size() - 1 &&
             (last_fragmentainer_has_break_inside ||
+             monolithic_overflow > LayoutUnit() ||
              (!fragmented_descendants.empty() &&
               index + 1 < FragmentationContextChildren().size()))) {
           descendants_to_layout.resize(index + 2);
@@ -2424,6 +2433,7 @@
       DCHECK_EQ(node.Style().GetPosition(), EPosition::kFixed);
       builder.SetShouldRepeat(true);
       builder.SetIsInsideRepeatableContent(true);
+      builder.DisableMonolithicOverflowPropagation();
       is_repeatable = true;
     } else {
       SetupSpaceBuilderForFragmentation(
@@ -2443,9 +2453,11 @@
       // finished this OOF there. But we have no (easy) way of telling where
       // that might be. But as long as the OOF doesn't contribute to any
       // additional fragmentainers, we should be (pretty) good.
-      if (is_last_fragmentainer_so_far &&
-          node_info.containing_block.IsFragmentedInsideClippedContainer()) {
-        builder.DisableFurtherFragmentation();
+      if (node_info.containing_block.IsFragmentedInsideClippedContainer()) {
+        if (is_last_fragmentainer_so_far) {
+          builder.DisableFurtherFragmentation();
+        }
+        builder.DisableMonolithicOverflowPropagation();
       }
     }
   } else if (container_builder_->IsInitialColumnBalancingPass()) {
@@ -2465,12 +2477,13 @@
     HeapVector<NodeToLayout>& pending_descendants,
     wtf_size_t index,
     LogicalOffset fragmentainer_progression,
+    LayoutUnit* monolithic_overflow,
     bool* has_actual_break_inside,
     HeapVector<NodeToLayout>* fragmented_descendants) {
   auto& children = FragmentationContextChildren();
   wtf_size_t num_children = children.size();
   bool is_new_fragment = index >= num_children;
-  bool is_last_fragmentainer_so_far = index + 1 == num_children;
+  bool is_last_fragmentainer_so_far = index + 1 >= num_children;
 
   DCHECK(fragmented_descendants);
   HeapVector<NodeToLayout> descendants_continued;
@@ -2478,13 +2491,15 @@
       &descendants_continued);
   std::swap(*fragmented_descendants, descendants_continued);
 
-  // If |index| is greater than the number of current children, and there are
-  // no OOF children to be added, we will still need to add an empty
-  // fragmentainer in its place. Otherwise, return early since there is no work
-  // to do.
+  // If |index| is greater than the number of current children, and there are no
+  // OOF children to be added, we will still need to add an empty fragmentainer
+  // in its place. Otherwise, return early since there is no work to do, unless
+  // there is overflowed monolithic content to take into account (in the case of
+  // pagination).
   if (pending_descendants.empty() && descendants_continued.empty() &&
-      !is_new_fragment)
+      *monolithic_overflow <= LayoutUnit() && !is_new_fragment) {
     return;
+  }
 
   const ConstraintSpace& space = GetFragmentainerConstraintSpace(index);
 
@@ -2537,10 +2552,25 @@
                           &algorithm, fragmented_descendants);
   }
 
+  // Don't update the builder when performing column balancing.
+  if (column_balancing_info_) {
+    return;
+  }
+
+  const LayoutResult* fragmentainer_result = algorithm.Layout();
+  const auto& new_fragmentainer =
+      To<PhysicalBoxFragment>(fragmentainer_result->GetPhysicalFragment());
+
   // Finalize layout on the cloned fragmentainer and replace all existing
   // references to the old result.
   ReplaceFragmentainer(index, fragmentainer_offset, is_new_fragment,
-                       &algorithm);
+                       new_fragmentainer);
+
+  if (const BlockBreakToken* break_token = new_fragmentainer.GetBreakToken()) {
+    *monolithic_overflow = break_token->MonolithicOverflow();
+  } else {
+    *monolithic_overflow = LayoutUnit();
+  }
 }
 
 void OutOfFlowLayoutPart::AddOOFToFragmentainer(
@@ -2703,18 +2733,11 @@
     wtf_size_t index,
     LogicalOffset offset,
     bool create_new_fragment,
-    SimplifiedOofLayoutAlgorithm* algorithm) {
-  // Don't update the builder when performing column balancing.
-  if (column_balancing_info_)
-    return;
-
+    const PhysicalBoxFragment& new_fragmentainer) {
   if (create_new_fragment) {
-    const LayoutResult* new_result = algorithm->Layout();
-    container_builder_->AddChild(new_result->GetPhysicalFragment(), offset);
+    container_builder_->AddChild(new_fragmentainer, offset);
   } else {
-    const LayoutResult* new_result = algorithm->Layout();
-    const PhysicalFragment* new_fragment = &new_result->GetPhysicalFragment();
-    container_builder_->ReplaceChild(index, *new_fragment, offset);
+    container_builder_->ReplaceChild(index, new_fragmentainer, offset);
 
     if (multicol_children_ && index < multicol_children_->size()) {
       // We are in a nested fragmentation context. Replace the column entry
@@ -2722,7 +2745,7 @@
       // there any new columns, they will be appended as part of regenerating
       // the multicol fragment.
       MulticolChildInfo& column_info = (*multicol_children_)[index];
-      column_info.mutable_link->fragment = new_fragment;
+      column_info.mutable_link->fragment = &new_fragmentainer;
     }
   }
 }
diff --git a/third_party/blink/renderer/core/layout/out_of_flow_layout_part.h b/third_party/blink/renderer/core/layout/out_of_flow_layout_part.h
index 9d6faba..beff9fc 100644
--- a/third_party/blink/renderer/core/layout/out_of_flow_layout_part.h
+++ b/third_party/blink/renderer/core/layout/out_of_flow_layout_part.h
@@ -374,6 +374,7 @@
       HeapVector<NodeToLayout>& pending_descendants,
       wtf_size_t index,
       LogicalOffset fragmentainer_progression,
+      LayoutUnit* monolithic_overflow,
       bool* has_actual_break_inside,
       HeapVector<NodeToLayout>* fragmented_descendants);
   void AddOOFToFragmentainer(NodeToLayout& descendant,
@@ -387,7 +388,7 @@
   void ReplaceFragmentainer(wtf_size_t index,
                             LogicalOffset offset,
                             bool create_new_fragment,
-                            SimplifiedOofLayoutAlgorithm* algorithm);
+                            const PhysicalBoxFragment& new_fragmentainer);
   LogicalOffset UpdatedFragmentainerOffset(
       LogicalOffset offset,
       wtf_size_t index,
diff --git a/third_party/blink/renderer/core/layout/physical_box_fragment.cc b/third_party/blink/renderer/core/layout/physical_box_fragment.cc
index 5f03b7e..b13ceb4 100644
--- a/third_party/blink/renderer/core/layout/physical_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/physical_box_fragment.cc
@@ -286,7 +286,10 @@
                  HasDescendantsForTablePartFlag::encode(false) |
                  IsFragmentationContextRootFlag::encode(
                      builder->is_fragmentation_context_root_) |
-                 IsMonolithicFlag::encode(builder->is_monolithic_)) {
+                 IsMonolithicFlag::encode(builder->is_monolithic_) |
+                 IsMonolithicOverflowPropagationDisabledFlag::encode(
+                     builder->GetConstraintSpace()
+                         .IsMonolithicOverflowPropagationDisabled())) {
   DCHECK(layout_object_);
   DCHECK(layout_object_->IsBoxModelObject());
   DCHECK(!builder->break_token_ || builder->break_token_->IsBlockType());
diff --git a/third_party/blink/renderer/core/layout/physical_box_fragment.h b/third_party/blink/renderer/core/layout/physical_box_fragment.h
index 26a9cba..9d77df63 100644
--- a/third_party/blink/renderer/core/layout/physical_box_fragment.h
+++ b/third_party/blink/renderer/core/layout/physical_box_fragment.h
@@ -411,6 +411,10 @@
 
   bool IsMonolithic() const { return bit_field_.get<IsMonolithicFlag>(); }
 
+  bool IsMonolithicOverflowPropagationDisabled() const {
+    return bit_field_.get<IsMonolithicOverflowPropagationDisabledFlag>();
+  }
+
 #if DCHECK_IS_ON()
   void CheckSameForSimplifiedLayout(const PhysicalBoxFragment&,
                                     bool check_same_block_size,
@@ -553,6 +557,8 @@
       HasDescendantsForTablePartFlag::DefineNextValue<bool, 1>;
   using IsMonolithicFlag =
       IsFragmentationContextRootFlag::DefineNextValue<bool, 1>;
+  using IsMonolithicOverflowPropagationDisabledFlag =
+      IsMonolithicFlag::DefineNextValue<bool, 1>;
 
   bool IncludeBorderTop() const {
     return bit_field_.get<IncludeBorderTopFlag>();
diff --git a/third_party/blink/renderer/core/loader/base_fetch_context_test.cc b/third_party/blink/renderer/core/loader/base_fetch_context_test.cc
index 6d94a60..cb731e7 100644
--- a/third_party/blink/renderer/core/loader/base_fetch_context_test.cc
+++ b/third_party/blink/renderer/core/loader/base_fetch_context_test.cc
@@ -46,6 +46,7 @@
 #include "third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h"
 #include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
@@ -157,6 +158,7 @@
     return GetFetchClientSettingsObject().GetSecurityOrigin();
   }
 
+  test::TaskEnvironment task_environment_;
   Persistent<ExecutionContext> execution_context_;
   Persistent<MockBaseFetchContext> fetch_context_;
   Persistent<ResourceFetcher> resource_fetcher_;
diff --git a/third_party/blink/renderer/core/loader/document_load_timing_test.cc b/third_party/blink/renderer/core/loader/document_load_timing_test.cc
index c8950be0..cedb752 100644
--- a/third_party/blink/renderer/core/loader/document_load_timing_test.cc
+++ b/third_party/blink/renderer/core/loader/document_load_timing_test.cc
@@ -5,15 +5,20 @@
 #include "third_party/blink/renderer/core/loader/document_load_timing.h"
 
 #include <memory>
+
 #include "base/time/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
-class DocumentLoadTimingTest : public testing::Test {};
+class DocumentLoadTimingTest : public testing::Test {
+ private:
+  test::TaskEnvironment task_environment_;
+};
 
 TEST_F(DocumentLoadTimingTest, ensureValidNavigationStartAfterEmbedder) {
   auto dummy_page = std::make_unique<DummyPageHolder>();
diff --git a/third_party/blink/renderer/core/loader/document_loader_auto_speculation_rules_test.cc b/third_party/blink/renderer/core/loader/document_loader_auto_speculation_rules_test.cc
index 4591f18..cbe2f973 100644
--- a/third_party/blink/renderer/core/loader/document_loader_auto_speculation_rules_test.cc
+++ b/third_party/blink/renderer/core/loader/document_loader_auto_speculation_rules_test.cc
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/core/speculation_rules/auto_speculation_rules_test_helper.h"
 #include "third_party/blink/renderer/core/speculation_rules/document_speculation_rules.h"
 #include "third_party/blink/renderer/core/speculation_rules/speculation_rules_metrics.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 namespace {
@@ -41,6 +42,7 @@
   }
 
  private:
+  test::TaskEnvironment task_environment_;
   base::test::ScopedFeatureList scoped_feature_list_;
   frame_test_helpers::WebViewHelper web_view_helper_;
   WebViewImpl* web_view_impl_;
diff --git a/third_party/blink/renderer/core/loader/document_loader_test.cc b/third_party/blink/renderer/core/loader/document_loader_test.cc
index 4529059..3750906 100644
--- a/third_party/blink/renderer/core/loader/document_loader_test.cc
+++ b/third_party/blink/renderer/core/loader/document_loader_test.cc
@@ -164,6 +164,7 @@
 
   WebLocalFrameImpl* MainFrame() { return web_view_helper_.LocalMainFrame(); }
 
+  test::TaskEnvironment task_environment_;
   frame_test_helpers::WebViewHelper web_view_helper_;
   base::test::ScopedFeatureList scoped_feature_list_;
 };
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc b/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc
index aa6258ef..b6919d8 100644
--- a/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc
+++ b/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc
@@ -74,6 +74,7 @@
 #include "third_party/blink/renderer/platform/loader/testing/mock_resource.h"
 #include "third_party/blink/renderer/platform/network/network_state_notifier.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/weborigin/reporting_disposition.h"
@@ -204,6 +205,7 @@
     return GetFetchContext()->GetTopFrameOrigin();
   }
 
+  test::TaskEnvironment task_environment_;
   std::unique_ptr<DummyPageHolder> dummy_page_holder;
   // We don't use the DocumentLoader directly in any tests, but need to keep it
   // around as long as the ResourceFetcher and Document live due to indirect
diff --git a/third_party/blink/renderer/core/loader/frame_loader_test.cc b/third_party/blink/renderer/core/loader/frame_loader_test.cc
index 7f9d521..d41f57f 100644
--- a/third_party/blink/renderer/core/loader/frame_loader_test.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader_test.cc
@@ -17,6 +17,7 @@
 #include "third_party/blink/renderer/core/testing/mock_policy_container_host.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
 
@@ -129,6 +130,7 @@
     url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
   }
 
+  test::TaskEnvironment task_environment_;
   frame_test_helpers::WebViewHelper web_view_helper_;
 };
 
diff --git a/third_party/blink/renderer/core/loader/frame_resource_fetcher_properties_test.cc b/third_party/blink/renderer/core/loader/frame_resource_fetcher_properties_test.cc
index 2cb05b4..bba9b981 100644
--- a/third_party/blink/renderer/core/loader/frame_resource_fetcher_properties_test.cc
+++ b/third_party/blink/renderer/core/loader/frame_resource_fetcher_properties_test.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/platform/network/network_state_notifier.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
@@ -21,6 +22,7 @@
             dummy_page_holder_->GetDocument())) {}
 
  protected:
+  test::TaskEnvironment task_environment_;
   const std::unique_ptr<DummyPageHolder> dummy_page_holder_;
   const Persistent<FrameResourceFetcherProperties> properties_;
 };
diff --git a/third_party/blink/renderer/core/loader/idleness_detector_test.cc b/third_party/blink/renderer/core/loader/idleness_detector_test.cc
index b3d355e..b87b90b 100644
--- a/third_party/blink/renderer/core/loader/idleness_detector_test.cc
+++ b/third_party/blink/renderer/core/loader/idleness_detector_test.cc
@@ -13,9 +13,13 @@
 
 class IdlenessDetectorTest : public PageTestBase {
  protected:
+  IdlenessDetectorTest()
+      : PageTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
+
   void SetUp() override {
     EnablePlatform();
     platform_time_ = platform()->NowTicks();
+    initial_time_ = platform_time_;
     DCHECK(!platform_time_.is_null());
     PageTestBase::SetUp();
   }
@@ -45,47 +49,48 @@
     Detector()->DidProcessTask(start_time, end_time);
   }
 
-  static base::TimeTicks SecondsToTimeTicks(double seconds) {
-    return base::TimeTicks() + base::Seconds(seconds);
+  base::TimeTicks SecondsToTimeTicks(double seconds) {
+    return initial_time_ + base::Seconds(seconds);
   }
 
  private:
+  base::TimeTicks initial_time_;
   base::TimeTicks platform_time_;
 };
 
 TEST_F(IdlenessDetectorTest, NetworkQuietBasic) {
   EXPECT_TRUE(IsNetworkQuietTimerActive());
 
-  WillProcessTask(SecondsToTimeTicks(1));
-  DidProcessTask(SecondsToTimeTicks(1), SecondsToTimeTicks(1.01));
+  WillProcessTask(SecondsToTimeTicks(0));
+  DidProcessTask(SecondsToTimeTicks(0), SecondsToTimeTicks(0.01));
 
-  WillProcessTask(SecondsToTimeTicks(1.52));
+  WillProcessTask(SecondsToTimeTicks(0.52));
   EXPECT_TRUE(HadNetworkQuiet());
-  DidProcessTask(SecondsToTimeTicks(1.52), SecondsToTimeTicks(1.53));
+  DidProcessTask(SecondsToTimeTicks(0.52), SecondsToTimeTicks(0.53));
 }
 
 TEST_F(IdlenessDetectorTest, NetworkQuietWithLongTask) {
   EXPECT_TRUE(IsNetworkQuietTimerActive());
 
-  WillProcessTask(SecondsToTimeTicks(1));
-  DidProcessTask(SecondsToTimeTicks(1), SecondsToTimeTicks(1.01));
+  WillProcessTask(SecondsToTimeTicks(0));
+  DidProcessTask(SecondsToTimeTicks(0), SecondsToTimeTicks(0.01));
 
-  WillProcessTask(SecondsToTimeTicks(1.02));
-  DidProcessTask(SecondsToTimeTicks(1.02), SecondsToTimeTicks(1.6));
+  WillProcessTask(SecondsToTimeTicks(0.02));
+  DidProcessTask(SecondsToTimeTicks(0.02), SecondsToTimeTicks(0.6));
   EXPECT_FALSE(HadNetworkQuiet());
 
-  WillProcessTask(SecondsToTimeTicks(2.11));
+  WillProcessTask(SecondsToTimeTicks(1.11));
   EXPECT_TRUE(HadNetworkQuiet());
-  DidProcessTask(SecondsToTimeTicks(2.11), SecondsToTimeTicks(2.12));
+  DidProcessTask(SecondsToTimeTicks(1.11), SecondsToTimeTicks(1.12));
 }
 
 TEST_F(IdlenessDetectorTest, NetworkQuietWatchdogTimerFired) {
   EXPECT_TRUE(IsNetworkQuietTimerActive());
 
-  WillProcessTask(SecondsToTimeTicks(1));
-  DidProcessTask(SecondsToTimeTicks(1), SecondsToTimeTicks(1.01));
+  WillProcessTask(SecondsToTimeTicks(0));
+  DidProcessTask(SecondsToTimeTicks(0), SecondsToTimeTicks(0.01));
 
-  FastForwardBy(base::Seconds(3));
+  FastForwardBy(base::Seconds(2));
   EXPECT_FALSE(IsNetworkQuietTimerActive());
   EXPECT_TRUE(HadNetworkQuiet());
 }
diff --git a/third_party/blink/renderer/core/loader/interactive_detector_test.cc b/third_party/blink/renderer/core/loader/interactive_detector_test.cc
index 24dd3a09..9e278ce4 100644
--- a/third_party/blink/renderer/core/loader/interactive_detector_test.cc
+++ b/third_party/blink/renderer/core/loader/interactive_detector_test.cc
@@ -19,6 +19,7 @@
 #include "third_party/blink/renderer/core/testing/scoped_mock_overlay_scrollbars.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
 
@@ -161,6 +162,7 @@
         TaskType::kUserInteraction);
   }
 
+  test::TaskEnvironment task_environment_;
   ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
       platform_;
 
diff --git a/third_party/blink/renderer/core/loader/link_loader_test.cc b/third_party/blink/renderer/core/loader/link_loader_test.cc
index 8941e9a..6203c77 100644
--- a/third_party/blink/renderer/core/loader/link_loader_test.cc
+++ b/third_party/blink/renderer/core/loader/link_loader_test.cc
@@ -39,6 +39,7 @@
 #include "third_party/blink/renderer/platform/scheduler/public/main_thread_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "third_party/blink/renderer/platform/testing/url_loader_mock_factory.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
@@ -142,6 +143,8 @@
       ASSERT_EQ(0, fetcher->CountPreloads());
     }
   }
+
+  test::TaskEnvironment task_environment_;
   std::unique_ptr<DummyPageHolder> dummy_page_holder_;
 };
 
@@ -486,7 +489,10 @@
 
 class LinkLoaderModulePreloadTest
     : public testing::TestWithParam<ModulePreloadTestParams>,
-      private ScopedMockOverlayScrollbars {};
+      private ScopedMockOverlayScrollbars {
+ private:
+  test::TaskEnvironment task_environment_;
+};
 
 class ModulePreloadTestModulator final : public DummyModulator {
  public:
@@ -565,6 +571,7 @@
 
  protected:
   const bool privacy_changes_enabled_;
+  test::TaskEnvironment task_environment_;
   ScopedTestingPlatformSupport<TestingPlatformSupport> platform_;
 
  private:
@@ -616,6 +623,7 @@
 class LinkLoaderTest : public testing::Test,
                        private ScopedMockOverlayScrollbars {
  protected:
+  test::TaskEnvironment task_environment_;
   ScopedTestingPlatformSupport<TestingPlatformSupport> platform_;
 };
 
@@ -809,6 +817,7 @@
   }
 
  protected:
+  test::TaskEnvironment task_environment_;
   ScopedTestingPlatformSupport<TestingPlatformSupport> platform_;
   scoped_refptr<base::TestMockTimeTaskRunner> test_task_runner_;
 
diff --git a/third_party/blink/renderer/core/loader/mixed_content_checker_test.cc b/third_party/blink/renderer/core/loader/mixed_content_checker_test.cc
index 71d051ec..e4cfe46 100644
--- a/third_party/blink/renderer/core/loader/mixed_content_checker_test.cc
+++ b/third_party/blink/renderer/core/loader/mixed_content_checker_test.cc
@@ -22,6 +22,7 @@
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
 #include "third_party/blink/renderer/platform/loader/mixed_content.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
@@ -35,6 +36,7 @@
 // Must be kept in sync manually!
 // LINT.IfChange
 TEST(MixedContentCheckerTest, IsMixedContent) {
+  test::TaskEnvironment task_environment;
   struct TestCase {
     const char* origin;
     const char* target;
@@ -81,6 +83,7 @@
 // LINT.ThenChange(content/browser/renderer_host/mixed_content_checker_unittest.cc)
 
 TEST(MixedContentCheckerTest, ContextTypeForInspector) {
+  test::TaskEnvironment task_environment;
   auto dummy_page_holder = std::make_unique<DummyPageHolder>(gfx::Size(1, 1));
   dummy_page_holder->GetFrame().Loader().CommitNavigation(
       WebNavigationParams::CreateWithHTMLBufferForTesting(
@@ -121,6 +124,7 @@
 }
 
 TEST(MixedContentCheckerTest, HandleCertificateError) {
+  test::TaskEnvironment task_environment;
   auto dummy_page_holder = std::make_unique<DummyPageHolder>(
       gfx::Size(1, 1), nullptr, MakeGarbageCollected<EmptyLocalFrameClient>());
 
@@ -158,6 +162,7 @@
 }
 
 TEST(MixedContentCheckerTest, DetectMixedForm) {
+  test::TaskEnvironment task_environment;
   KURL main_resource_url(NullURL(), "https://example.test/");
   auto dummy_page_holder = std::make_unique<DummyPageHolder>(
       gfx::Size(1, 1), nullptr, MakeGarbageCollected<EmptyLocalFrameClient>());
@@ -188,6 +193,7 @@
 }
 
 TEST(MixedContentCheckerTest, DetectMixedFavicon) {
+  test::TaskEnvironment task_environment;
   KURL main_resource_url("https://example.test/");
   auto dummy_page_holder = std::make_unique<DummyPageHolder>(
       gfx::Size(1, 1), nullptr, MakeGarbageCollected<EmptyLocalFrameClient>());
@@ -241,6 +247,7 @@
 }
 
 TEST(MixedContentCheckerTest, DetectUpgradeableMixedContent) {
+  test::TaskEnvironment task_environment;
   KURL main_resource_url("https://example.test/");
   auto dummy_page_holder = std::make_unique<DummyPageHolder>(
       gfx::Size(1, 1), nullptr, MakeGarbageCollected<EmptyLocalFrameClient>());
@@ -307,6 +314,7 @@
 
 TEST(MixedContentCheckerTest,
      NotAutoupgradedMixedContentHasUpgradeIfInsecureSet) {
+  test::TaskEnvironment task_environment;
   ResourceRequest request;
   request.SetUrl(KURL("https://example.test"));
   request.SetRequestContext(mojom::blink::RequestContextType::AUDIO);
@@ -323,6 +331,7 @@
 }
 
 TEST(MixedContentCheckerTest, AutoupgradedMixedContentHasUpgradeIfInsecureSet) {
+  test::TaskEnvironment task_environment;
   ResourceRequest request;
   request.SetUrl(KURL("http://example.test"));
   request.SetRequestContext(mojom::blink::RequestContextType::AUDIO);
@@ -340,6 +349,7 @@
 
 TEST(MixedContentCheckerTest,
      AutoupgradeMixedContentWithLiteralLocalIpAddress) {
+  test::TaskEnvironment task_environment;
   ResourceRequest request;
   request.SetUrl(KURL("http://127.0.0.1/"));
   request.SetRequestContext(mojom::blink::RequestContextType::AUDIO);
@@ -357,6 +367,7 @@
 
 TEST(MixedContentCheckerTest,
      NotAutoupgradeMixedContentWithLiteralNonLocalIpAddress) {
+  test::TaskEnvironment task_environment;
   ResourceRequest request;
   request.SetUrl(KURL("http://8.8.8.8/"));
   request.SetRequestContext(mojom::blink::RequestContextType::AUDIO);
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc b/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc
index 147eaed..36ef63b 100644
--- a/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc
@@ -40,6 +40,7 @@
 #include "third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h"
 #include "third_party/blink/renderer/platform/testing/mock_context_lifecycle_notifier.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_loader_mock_factory.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
@@ -120,6 +121,7 @@
   ModuleScriptLoaderTest(const ModuleScriptLoaderTest&) = delete;
   ModuleScriptLoaderTest& operator=(const ModuleScriptLoaderTest&) = delete;
   void SetUp() override;
+  void TearDown() override;
 
   void InitializeForDocument();
   void InitializeForWorklet();
@@ -144,9 +146,11 @@
         ->RunUntilIdle();
   }
 
- private:
   const base::TickClock* GetTickClock() override {
-    return platform_->test_task_runner()->GetMockTickClock();
+    if (test_task_runner_) {
+      return test_task_runner_->GetMockTickClock();
+    }
+    return PageTestBase::GetTickClock();
   }
 
  protected:
@@ -155,6 +159,7 @@
 
   Persistent<ResourceFetcher> fetcher_;
 
+  scoped_refptr<base::TestMockTimeTaskRunner> test_task_runner_;
   ScopedTestingPlatformSupport<FetchTestingPlatformSupport> platform_;
   std::unique_ptr<MockWorkerReportingProxy> reporting_proxy_;
   Persistent<ModuleScriptLoaderTestModulator> modulator_;
@@ -165,10 +170,25 @@
   PageTestBase::SetUp(gfx::Size(500, 500));
 }
 
+void ModuleScriptLoaderTest::TearDown() {
+  if (global_scope_) {
+    global_scope_->Dispose();
+    global_scope_->NotifyContextDestroyed();
+  }
+}
+
 ModuleScriptLoaderTest::ModuleScriptLoaderTest()
-    : url_("https://example.test"),
+    : PageTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
+      url_("https://example.test"),
       security_origin_(SecurityOrigin::Create(url_)) {
-  platform_->AdvanceClockSeconds(1.);  // For non-zero DocumentParserTimings
+  if (!task_environment()) {
+    // TODO(crbug.com/1315595): Remove once TaskEnvironment becomes the default
+    // in blink_unittests_v2
+    test_task_runner_ = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
+        base::TestMockTimeTaskRunner::Type::kStandalone);
+    test_task_runner_->AdvanceMockTickClock(
+        base::Seconds(1));  // For non-zero DocumentParserTimings
+  }
 }
 
 void ModuleScriptLoaderTest::InitializeForDocument() {
diff --git a/third_party/blink/renderer/core/loader/prerender_test.cc b/third_party/blink/renderer/core/loader/prerender_test.cc
index 11889a0..71fcefc 100644
--- a/third_party/blink/renderer/core/loader/prerender_test.cc
+++ b/third_party/blink/renderer/core/loader/prerender_test.cc
@@ -49,6 +49,7 @@
 #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/core/html_element_type_helpers.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_loader_mock_factory.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
@@ -176,6 +177,7 @@
         ->GetFrame()
         ->GetBrowserInterfaceBroker();
   }
+  test::TaskEnvironment task_environment_;
 
   std::vector<std::unique_ptr<MockNoStatePrefetchProcessor>> processors_;
 
diff --git a/third_party/blink/renderer/core/loader/programmatic_scroll_test.cc b/third_party/blink/renderer/core/loader/programmatic_scroll_test.cc
index 19ab6b9..7d33a797 100644
--- a/third_party/blink/renderer/core/loader/programmatic_scroll_test.cc
+++ b/third_party/blink/renderer/core/loader/programmatic_scroll_test.cc
@@ -19,6 +19,7 @@
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_loader_mock_factory.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
@@ -41,6 +42,7 @@
         WebString(base_url_), test::CoreTestDataPath(), WebString(file_name));
   }
 
+  test::TaskEnvironment task_environment_;
   String base_url_;
 };
 
diff --git a/third_party/blink/renderer/core/loader/progress_tracker_test.cc b/third_party/blink/renderer/core/loader/progress_tracker_test.cc
index a8236fd..c9f62e1 100644
--- a/third_party/blink/renderer/core/loader/progress_tracker_test.cc
+++ b/third_party/blink/renderer/core/loader/progress_tracker_test.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/core/testing/fake_local_frame_host.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 
 namespace blink {
@@ -74,6 +75,7 @@
   }
 
  private:
+  test::TaskEnvironment task_environment_;
   mutable base::RunLoop* current_run_loop_ = nullptr;
   frame_test_helpers::TestWebFrameClient web_frame_client_;
   frame_test_helpers::WebViewHelper web_view_helper_;
diff --git a/third_party/blink/renderer/core/loader/resource/font_resource_test.cc b/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
index 9a3e57cb1..32343747 100644
--- a/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
@@ -27,6 +27,7 @@
 #include "third_party/blink/renderer/platform/loader/testing/test_loader_factory.h"
 #include "third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h"
 #include "third_party/blink/renderer/platform/testing/mock_context_lifecycle_notifier.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
 #include "third_party/blink/renderer/platform/testing/url_loader_mock_factory.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
@@ -39,6 +40,9 @@
   void TearDown() override {
     url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
   }
+
+ private:
+  test::TaskEnvironment task_environment_;
 };
 
 class CacheAwareFontResourceTest : public FontResourceTest {
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 fcc2493..be4eba4 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
@@ -72,6 +72,7 @@
 #include "third_party/blink/renderer/platform/testing/mock_context_lifecycle_notifier.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/scoped_mocked_url.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_loader_mock_factory.h"
@@ -128,6 +129,9 @@
     ThreadState::Current()->CollectAllGarbageForTesting(
         ThreadState::StackState::kNoHeapPointers);
   }
+
+ private:
+  test::TaskEnvironment task_environment_;
 };
 
 // Ensure that the image decoder can determine the dimensions of kJpegImage from
@@ -1149,6 +1153,8 @@
     return InstanceCounters::CounterValue(
         InstanceCounters::kUACSSResourceCounter);
   }
+
+  test::TaskEnvironment task_environment_;
 };
 
 TEST_F(ImageResourceCounterTest, InstanceCounters) {
diff --git a/third_party/blink/renderer/core/loader/resource/resource_loader_code_cache_test.cc b/third_party/blink/renderer/core/loader/resource/resource_loader_code_cache_test.cc
index 98fbe11..65753edf 100644
--- a/third_party/blink/renderer/core/loader/resource/resource_loader_code_cache_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/resource_loader_code_cache_test.cc
@@ -19,6 +19,7 @@
 #include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h"
 #include "third_party/blink/renderer/platform/testing/mock_context_lifecycle_notifier.h"
 #include "third_party/blink/renderer/platform/testing/noop_url_loader.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
@@ -137,6 +138,7 @@
     return serialized_data;
   }
 
+  test::TaskEnvironment task_environment_;
   ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
       platform_;
 
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 edb024e..3c1a0dc 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
@@ -7,6 +7,7 @@
 #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"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
 #include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
@@ -15,6 +16,7 @@
 namespace {
 
 TEST(ScriptResourceTest, SuccessfulRevalidation) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   const KURL url("https://www.example.com/script.js");
   ScriptResource* resource =
@@ -40,6 +42,7 @@
 }
 
 TEST(ScriptResourceTest, FailedRevalidation) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   const KURL url("https://www.example.com/script.js");
   ScriptResource* resource =
@@ -67,6 +70,7 @@
 }
 
 TEST(ScriptResourceTest, RedirectDuringRevalidation) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   const KURL url("https://www.example.com/script.js");
   ScriptResource* resource =
@@ -96,6 +100,7 @@
 }
 
 TEST(ScriptResourceTest, WebUICodeCacheEnabled) {
+  test::TaskEnvironment task_environment;
 #if DCHECK_IS_ON()
   WTF::SetIsBeforeThreadCreatedForTest();  // Required for next operation:
 #endif
@@ -126,6 +131,7 @@
 }
 
 TEST(ScriptResourceTest, WebUICodeCacheDisabled) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   const KURL url("nocodecachewithhashing://www.example.com/script.js");
   ScriptResource* resource =
@@ -143,6 +149,7 @@
 }
 
 TEST(ScriptResourceTest, CodeCacheEnabledByResponseFlag) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   const KURL url("https://www.example.com/script.js");
   ScriptResource* resource =
diff --git a/third_party/blink/renderer/core/loader/resource_load_observer_for_frame_test.cc b/third_party/blink/renderer/core/loader/resource_load_observer_for_frame_test.cc
index 767e950..e3f495d 100644
--- a/third_party/blink/renderer/core/loader/resource_load_observer_for_frame_test.cc
+++ b/third_party/blink/renderer/core/loader/resource_load_observer_for_frame_test.cc
@@ -18,6 +18,7 @@
 #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
 #include "third_party/blink/renderer/platform/loader/testing/mock_resource.h"
 #include "third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 
@@ -26,6 +27,7 @@
 // Tests that when a resource with certificate errors is loaded from the memory
 // cache, the embedder is notified.
 TEST(ResourceLoadObserverForFrameTest, MemoryCacheCertificateError) {
+  test::TaskEnvironment task_environment;
   auto dummy_page_holder = std::make_unique<DummyPageHolder>(
       gfx::Size(), nullptr, MakeGarbageCollected<EmptyLocalFrameClient>());
   LocalFrame& frame = dummy_page_holder->GetFrame();
diff --git a/third_party/blink/renderer/core/loader/threadable_loader_test.cc b/third_party/blink/renderer/core/loader/threadable_loader_test.cc
index 1c64a7f..472b6b0d 100644
--- a/third_party/blink/renderer/core/loader/threadable_loader_test.cc
+++ b/third_party/blink/renderer/core/loader/threadable_loader_test.cc
@@ -31,6 +31,7 @@
 #include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_loader_mock_factory.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
@@ -175,6 +176,7 @@
   }
 
  private:
+  test::TaskEnvironment task_environment_;
   std::unique_ptr<DummyPageHolder> dummy_page_holder_;
   Checkpoint checkpoint_;
   Persistent<ThreadableLoader> loader_;
diff --git a/third_party/blink/renderer/core/loader/web_associated_url_loader_impl_test.cc b/third_party/blink/renderer/core/loader/web_associated_url_loader_impl_test.cc
index 684a078..cbd18ad 100644
--- a/third_party/blink/renderer/core/loader/web_associated_url_loader_impl_test.cc
+++ b/third_party/blink/renderer/core/loader/web_associated_url_loader_impl_test.cc
@@ -46,6 +46,7 @@
 #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_loader_mock_factory.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
@@ -247,6 +248,7 @@
   }
 
  protected:
+  test::TaskEnvironment task_environment_;
   String frame_file_path_;
   frame_test_helpers::WebViewHelper helper_;
 
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl_test.cc b/third_party/blink/renderer/core/page/chrome_client_impl_test.cc
index b2bafbe..d6d0e5a 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl_test.cc
+++ b/third_party/blink/renderer/core/page/chrome_client_impl_test.cc
@@ -71,6 +71,7 @@
 #include "third_party/blink/renderer/core/script/classic_script.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 #include "third_party/blink/renderer/platform/language.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 // To avoid conflicts with the CreateWindow macro from the Windows SDK...
 #undef CreateWindow
@@ -125,6 +126,7 @@
         To<ChromeClientImpl>(&web_view_->GetPage()->GetChromeClient());
   }
 
+  test::TaskEnvironment task_environment_;
   ViewCreatingClient web_frame_client_;
   frame_test_helpers::WebViewHelper helper_;
   WebViewImpl* web_view_;
@@ -300,6 +302,7 @@
   void TearDown() override {}
 
  protected:
+  test::TaskEnvironment task_environment_;
   frame_test_helpers::WebViewHelper helper_;
   WebViewImpl* web_view_;
   Persistent<WebLocalFrameImpl> main_frame_;
@@ -337,6 +340,7 @@
         To<ChromeClientImpl>(&web_view_->GetPage()->GetChromeClient());
   }
 
+  test::TaskEnvironment task_environment_;
   frame_test_helpers::WebViewHelper helper_;
   WebViewImpl* web_view_;
   Persistent<ChromeClientImpl> chrome_client_impl_;
diff --git a/third_party/blink/renderer/core/page/chrome_client_test.cc b/third_party/blink/renderer/core/page/chrome_client_test.cc
index 471de76..85e66bc5 100644
--- a/third_party/blink/renderer/core/page/chrome_client_test.cc
+++ b/third_party/blink/renderer/core/page/chrome_client_test.cc
@@ -15,6 +15,7 @@
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/core/testing/null_execution_context.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
@@ -40,7 +41,9 @@
 };
 }  // anonymous namespace
 
-class ChromeClientTest : public testing::Test {};
+class ChromeClientTest : public testing::Test {
+  test::TaskEnvironment task_environment_;
+};
 
 TEST_F(ChromeClientTest, UpdateTooltipUnderCursorFlood) {
   ChromeClientToolTipLogger logger;
diff --git a/third_party/blink/renderer/core/page/context_menu_controller_test.cc b/third_party/blink/renderer/core/page/context_menu_controller_test.cc
index 867df3ed..5889b95 100644
--- a/third_party/blink/renderer/core/page/context_menu_controller_test.cc
+++ b/third_party/blink/renderer/core/page/context_menu_controller_test.cc
@@ -47,6 +47,7 @@
 #include "third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h"
 #include "third_party/blink/renderer/platform/testing/empty_web_media_player.h"
 #include "third_party/blink/renderer/platform/testing/scoped_mocked_url.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_loader_mock.h"
 #include "third_party/blink/renderer/platform/testing/url_loader_mock_factory_impl.h"
@@ -209,6 +210,7 @@
   }
 
  protected:
+  test::TaskEnvironment task_environment_;
   base::test::ScopedFeatureList feature_list_;
   TestWebFrameClientImpl web_frame_client_;
   frame_test_helpers::WebViewHelper web_view_helper_;
@@ -2043,6 +2045,7 @@
   }
 
  protected:
+  test::TaskEnvironment task_environment_;
   base::test::ScopedFeatureList feature_list_;
   TestWebFrameClientImpl child_web_frame_client_;
   frame_test_helpers::WebViewHelper web_view_helper_;
diff --git a/third_party/blink/renderer/core/page/drag_image_test.cc b/third_party/blink/renderer/core/page/drag_image_test.cc
index 9fd30e30..80f40f0 100644
--- a/third_party/blink/renderer/core/page/drag_image_test.cc
+++ b/third_party/blink/renderer/core/page/drag_image_test.cc
@@ -31,6 +31,7 @@
 #include "third_party/blink/renderer/core/page/drag_image.h"
 
 #include <memory>
+
 #include "base/memory/scoped_refptr.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/platform/font_family_names.h"
@@ -38,6 +39,7 @@
 #include "third_party/blink/renderer/platform/graphics/bitmap_image.h"
 #include "third_party/blink/renderer/platform/graphics/image.h"
 #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkSurface.h"
@@ -103,6 +105,7 @@
 };
 
 TEST(DragImageTest, NullHandling) {
+  test::TaskEnvironment task_environment;
   EXPECT_FALSE(DragImage::Create(nullptr));
 
   scoped_refptr<TestImage> null_test_image(TestImage::Create(gfx::Size()));
@@ -110,6 +113,7 @@
 }
 
 TEST(DragImageTest, NonNullHandling) {
+  test::TaskEnvironment task_environment;
   scoped_refptr<TestImage> test_image(TestImage::Create(gfx::Size(2, 2)));
   std::unique_ptr<DragImage> drag_image = DragImage::Create(test_image.get());
   ASSERT_TRUE(drag_image);
@@ -121,6 +125,7 @@
 }
 
 TEST(DragImageTest, CreateDragImage) {
+  test::TaskEnvironment task_environment;
   // Tests that the DrageImage implementation doesn't choke on null values
   // of imageForCurrentFrame().
   // FIXME: how is this test any different from test NullHandling?
@@ -129,6 +134,7 @@
 }
 
 TEST(DragImageTest, TrimWhitespace) {
+  test::TaskEnvironment task_environment;
   KURL url("http://www.example.com/");
   String test_label = "          Example Example Example      \n    ";
   String expected_label = "Example Example Example";
@@ -152,6 +158,7 @@
 }
 
 TEST(DragImageTest, InterpolationNone) {
+  test::TaskEnvironment task_environment;
   SkBitmap expected_bitmap;
   expected_bitmap.allocN32Pixels(4, 4);
   expected_bitmap.eraseArea(SkIRect::MakeXYWH(0, 0, 2, 2), 0xFFFFFFFF);
diff --git a/third_party/blink/renderer/core/page/page_popup_client_test.cc b/third_party/blink/renderer/core/page/page_popup_client_test.cc
index 9701633..0cd6e7a1 100644
--- a/third_party/blink/renderer/core/page/page_popup_client_test.cc
+++ b/third_party/blink/renderer/core/page/page_popup_client_test.cc
@@ -4,12 +4,15 @@
 
 #include "third_party/blink/renderer/core/page/page_popup_client.h"
 
-#include "testing/gtest/include/gtest/gtest.h"
 #include <string>
 
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
+
 namespace blink {
 
 TEST(PagePopupClientTest, AddJavaScriptString) {
+  test::TaskEnvironment task_environment;
   scoped_refptr<SharedBuffer> buffer = SharedBuffer::Create();
   PagePopupClient::AddJavaScriptString(
       String::FromUTF8("abc\r\n'\"</script>\t\f\v\xE2\x80\xA8\xE2\x80\xA9"),
diff --git a/third_party/blink/renderer/core/page/page_test.cc b/third_party/blink/renderer/core/page/page_test.cc
index c2721d5..2cde771 100644
--- a/third_party/blink/renderer/core/page/page_test.cc
+++ b/third_party/blink/renderer/core/page/page_test.cc
@@ -12,10 +12,12 @@
 #include "third_party/blink/renderer/core/loader/empty_clients.h"
 #include "third_party/blink/renderer/core/page/scoped_browsing_context_group_pauser.h"
 #include "third_party/blink/renderer/platform/scheduler/public/dummy_schedulers.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
 TEST(PageTest, CreateOrdinaryBrowsingContextGroup) {
+  test::TaskEnvironment task_environment;
   EmptyChromeClient client;
   auto* scheduler = scheduler::CreateDummyAgentGroupScheduler();
   auto bcg_info = BrowsingContextGroupInfo::CreateUnique();
@@ -29,6 +31,7 @@
 }
 
 TEST(PageTest, CreateNonOrdinaryBrowsingContextGroup) {
+  test::TaskEnvironment task_environment;
   EmptyChromeClient client;
   auto* scheduler = scheduler::CreateDummyAgentGroupScheduler();
 
@@ -41,6 +44,7 @@
 }
 
 TEST(PageTest, BrowsingContextGroupUpdate) {
+  test::TaskEnvironment task_environment;
   EmptyChromeClient client;
   auto* scheduler = scheduler::CreateDummyAgentGroupScheduler();
   auto initial_bcg_info = BrowsingContextGroupInfo::CreateUnique();
@@ -63,6 +67,7 @@
 }
 
 TEST(PageTest, BrowsingContextGroupUpdateWithPauser) {
+  test::TaskEnvironment task_environment;
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(
       features::kPausePagesPerBrowsingContextGroup);
diff --git a/third_party/blink/renderer/core/page/plugin_data_test.cc b/third_party/blink/renderer/core/page/plugin_data_test.cc
index bbaaadc..e55e723 100644
--- a/third_party/blink/renderer/core/page/plugin_data_test.cc
+++ b/third_party/blink/renderer/core/page/plugin_data_test.cc
@@ -9,6 +9,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/plugins/plugin_registry.mojom-blink.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 
@@ -26,6 +27,7 @@
 };
 
 TEST(PluginDataTest, UpdatePluginList) {
+  test::TaskEnvironment task_environment;
   ScopedTestingPlatformSupport<TestingPlatformSupport> support;
 
   MockPluginRegistry mock_plugin_registry;
diff --git a/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor_test.cc b/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor_test.cc
index e40a0fcc..f5c0407 100644
--- a/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor_test.cc
@@ -269,6 +269,10 @@
   // which should clear LocalFrameView::fragment_anchor_ ...
   EXPECT_FALSE(GetDocument().View()->GetFragmentAnchor());
 
+  // Allow any enqueued animation frame tasks to run
+  // so their resources can be cleaned up.
+  Compositor().BeginFrame();
+
   // Non-crash is considered a pass.
 }
 
diff --git a/third_party/blink/renderer/core/page/scrolling/main_thread_scrolling_reasons_test.cc b/third_party/blink/renderer/core/page/scrolling/main_thread_scrolling_reasons_test.cc
index 0a043d3..c171c5b9 100644
--- a/third_party/blink/renderer/core/page/scrolling/main_thread_scrolling_reasons_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/main_thread_scrolling_reasons_test.cc
@@ -22,6 +22,7 @@
 #include "third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h"
 #include "third_party/blink/renderer/platform/testing/find_cc_layer.h"
 #include "third_party/blink/renderer/platform/testing/paint_test_configurations.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_loader_mock_factory.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
@@ -127,6 +128,7 @@
   }
 
  protected:
+  test::TaskEnvironment task_environment_;
   String base_url_;
   frame_test_helpers::WebViewHelper helper_;
 };
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc b/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
index 0b0d4c7..f432a709 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/public/web/web_script_source.h"
 #include "third_party/blink/public/web/web_settings.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_union_node_string_trustedscript.h"
+#include "third_party/blink/renderer/core/css/css_default_style_sheets.h"
 #include "third_party/blink/renderer/core/css/css_style_declaration.h"
 #include "third_party/blink/renderer/core/frame/browser_controls.h"
 #include "third_party/blink/renderer/core/frame/dom_visual_viewport.h"
@@ -34,7 +35,9 @@
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
+#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_loader_mock_factory.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
@@ -173,6 +176,7 @@
     view->UpdateAllLifecyclePhasesForTest();
   }
 
+  test::TaskEnvironment task_environment_;
   String base_url_;
   frame_test_helpers::CreateTestWebFrameWidgetCallback create_widget_callback_;
   std::unique_ptr<frame_test_helpers::WebViewHelper> helper_;
@@ -858,7 +862,12 @@
 class ImplicitRootScrollerSimTest : public SimTest {
  public:
   ImplicitRootScrollerSimTest() : implicit_root_scroller_for_test_(true) {}
-
+  ~ImplicitRootScrollerSimTest() override {
+    // TODO(crbug.com/1315595): Consider moving this to MainThreadIsolate.
+    MemoryCache::Get()->EvictResources();
+    // Clear lazily loaded style sheets.
+    CSSDefaultStyleSheets::Instance().PrepareForLeakDetection();
+  }
   void SetUp() override {
     SimTest::SetUp();
     WebView().GetPage()->GetSettings().SetViewportEnabled(true);
diff --git a/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc b/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc
index 3792cd2..7a00436 100644
--- a/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc
@@ -70,6 +70,7 @@
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/testing/find_cc_layer.h"
 #include "third_party/blink/renderer/platform/testing/paint_test_configurations.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_loader_mock_factory.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
@@ -235,6 +236,7 @@
     frame_test_helpers::LoadFrame(GetWebView()->MainFrameImpl(), url);
   }
 
+  test::TaskEnvironment task_environment_;
   frame_test_helpers::WebViewHelper helper_;
 };
 
diff --git a/third_party/blink/renderer/core/page/scrolling/snap_coordinator_test.cc b/third_party/blink/renderer/core/page/scrolling/snap_coordinator_test.cc
index 6f467d53..9b94d8e 100644
--- a/third_party/blink/renderer/core/page/scrolling/snap_coordinator_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/snap_coordinator_test.cc
@@ -25,6 +25,7 @@
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/core/testing/scoped_mock_overlay_scrollbars.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 
 namespace blink {
@@ -148,6 +149,7 @@
     return nullptr;
   }
 
+  test::TaskEnvironment task_environment_;
   std::unique_ptr<DummyPageHolder> page_holder_;
 };
 
diff --git a/third_party/blink/renderer/core/page/viewport_test.cc b/third_party/blink/renderer/core/page/viewport_test.cc
index 803d420b..0e604ced 100644
--- a/third_party/blink/renderer/core/page/viewport_test.cc
+++ b/third_party/blink/renderer/core/page/viewport_test.cc
@@ -50,6 +50,7 @@
 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
 #include "third_party/blink/renderer/platform/geometry/length.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_loader_mock_factory.h"
@@ -91,6 +92,7 @@
     blink::test::RunPendingTasks();
   }
 
+  test::TaskEnvironment task_environment_;
   std::string base_url_;
   std::string chrome_url_;
 
diff --git a/third_party/blink/renderer/core/view_transition/view_transition.cc b/third_party/blink/renderer/core/view_transition/view_transition.cc
index 99d40cc5..5df8fe37 100644
--- a/third_party/blink/renderer/core/view_transition/view_transition.cc
+++ b/third_party/blink/renderer/core/view_transition/view_transition.cc
@@ -136,6 +136,12 @@
                                               types, delegate);
 }
 
+ViewTransition* ViewTransition::CreateSkipped(
+    Document* document,
+    V8ViewTransitionCallback* callback) {
+  return MakeGarbageCollected<ViewTransition>(PassKey(), document, callback);
+}
+
 ViewTransition::ViewTransition(PassKey,
                                Document* document,
                                V8ViewTransitionCallback* update_dom_callback,
@@ -160,6 +166,20 @@
   ProcessCurrentState();
 }
 
+ViewTransition::ViewTransition(PassKey,
+                               Document* document,
+                               V8ViewTransitionCallback* update_dom_callback)
+    : ExecutionContextLifecycleObserver(document->GetExecutionContext()),
+      creation_type_(CreationType::kScript),
+      document_(document),
+      document_tag_(NextDocumentTag()),
+      script_delegate_(MakeGarbageCollected<DOMViewTransition>(
+          *document->GetExecutionContext(),
+          *this,
+          update_dom_callback)) {
+  SkipTransition();
+}
+
 // static
 ViewTransition* ViewTransition::CreateForSnapshotForNavigation(
     Document* document,
@@ -234,9 +254,12 @@
   }
 
   // If we already started processing the transition (i.e. we're beyond capture
-  // tag discovery), then send a release directive.
+  // tag discovery), then send a release directive. We don't do this, if we're
+  // capturing this for a snapshot. The only way that transition is skipped is
+  // if we finished capturing.
   if (static_cast<int>(state_) >
-      static_cast<int>(State::kCaptureTagDiscovery)) {
+          static_cast<int>(State::kCaptureTagDiscovery) &&
+      creation_type_ != CreationType::kForSnapshot) {
     delegate_->AddPendingRequest(
         ViewTransitionRequest::CreateRelease(document_tag_, navigation_id_));
   }
@@ -252,9 +275,13 @@
 
   // Resume rendering, and finalize the rest of the state.
   ResumeRendering();
-  style_tracker_->Abort();
+  if (style_tracker_) {
+    style_tracker_->Abort();
+  }
 
-  delegate_->OnTransitionFinished(this);
+  if (delegate_) {
+    delegate_->OnTransitionFinished(this);
+  }
 
   // This should be the last call in this function to avoid erroneously checking
   // the `state_` against the wrong state.
@@ -264,8 +291,9 @@
 bool ViewTransition::AdvanceTo(State state) {
   DCHECK(CanAdvanceTo(state)) << "Current state " << static_cast<int>(state_)
                               << " new state " << static_cast<int>(state);
+  bool was_initial = state_ == State::kInitial;
   state_ = state;
-  if (IsTerminalState(state_)) {
+  if (!was_initial && IsTerminalState(state_)) {
     if (auto* originating_element = document_->documentElement()) {
       originating_element->ActiveViewTransitionStateChanged();
     }
@@ -298,7 +326,7 @@
   switch (state_) {
     case State::kInitial:
       return state == State::kCaptureTagDiscovery ||
-             state == State::kWaitForRenderBlock;
+             state == State::kWaitForRenderBlock || state == State::kAborted;
     case State::kCaptureTagDiscovery:
       return state == State::kCaptureRequestPending || state == State::kAborted;
     case State::kCaptureRequestPending:
@@ -460,6 +488,7 @@
 
           std::move(transition_state_callback_)
               .Run(std::move(view_transition_state));
+          SkipTransition(PromiseResponse::kRejectAbort);
           break;
         }
 
diff --git a/third_party/blink/renderer/core/view_transition/view_transition.h b/third_party/blink/renderer/core/view_transition/view_transition.h
index fcb5eb8..b9f7169 100644
--- a/third_party/blink/renderer/core/view_transition/view_transition.h
+++ b/third_party/blink/renderer/core/view_transition/view_transition.h
@@ -62,6 +62,9 @@
       const std::optional<Vector<String>>& types,
       Delegate*);
 
+  // Creates a skipped transition that still runs the specified callbacks.
+  static ViewTransition* CreateSkipped(Document*, V8ViewTransitionCallback*);
+
   // Creates a ViewTransition to cache the state of a Document before a
   // navigation. The cached state is provided to the caller using the
   // |ViewTransitionStateCallback|.
@@ -85,6 +88,8 @@
                  V8ViewTransitionCallback*,
                  const std::optional<Vector<String>>& types,
                  Delegate*);
+  // Skipped transition constructor.
+  ViewTransition(PassKey, Document*, V8ViewTransitionCallback*);
   // Navigation-initiated for-snapshot constructor.
   ViewTransition(PassKey, Document*, ViewTransitionStateCallback, Delegate*);
   // Navigation-initiated from-snapshot constructor.
@@ -322,14 +327,14 @@
   const CreationType creation_type_;
 
   Member<Document> document_;
-  Delegate* const delegate_;
+  Delegate* const delegate_ = nullptr;
   const viz::NavigationID navigation_id_;
 
   // The document tag identifies the document to which this transition
   // belongs. It's unique among other local documents.
   uint32_t document_tag_ = 0u;
 
-  Member<ViewTransitionStyleTracker> style_tracker_;
+  Member<ViewTransitionStyleTracker> style_tracker_ = nullptr;
 
   // Manages pausing rendering of the Document between capture and updateDOM
   // callback finishing.
diff --git a/third_party/blink/renderer/core/view_transition/view_transition_supplement.cc b/third_party/blink/renderer/core/view_transition/view_transition_supplement.cc
index 545a0b6..0ba6847 100644
--- a/third_party/blink/renderer/core/view_transition/view_transition_supplement.cc
+++ b/third_party/blink/renderer/core/view_transition/view_transition_supplement.cc
@@ -142,11 +142,14 @@
     ExceptionState& exception_state) {
   // Disallow script initiated transitions during a navigation initiated
   // transition.
-  if (transition_ && !transition_->IsCreatedViaScriptAPI())
-    return nullptr;
+  if (transition_ && !transition_->IsCreatedViaScriptAPI()) {
+    return ViewTransition::CreateSkipped(&document, callback)
+        ->GetScriptDelegate();
+  }
 
-  if (transition_)
+  if (transition_) {
     transition_->SkipTransition();
+  }
 
   DCHECK(!transition_)
       << "SkipTransition() should finish existing |transition_|";
diff --git a/third_party/blink/renderer/core/view_transition/view_transition_test.cc b/third_party/blink/renderer/core/view_transition/view_transition_test.cc
index 913da0d..58b8558 100644
--- a/third_party/blink/renderer/core/view_transition/view_transition_test.cc
+++ b/third_party/blink/renderer/core/view_transition/view_transition_test.cc
@@ -54,9 +54,10 @@
 namespace blink {
 
 class ViewTransitionTest : public testing::Test,
-                           public PaintTestConfigurations {
+                           public PaintTestConfigurations,
+                           private ScopedViewTransitionOnNavigationForTest {
  public:
-  ViewTransitionTest() {}
+  ViewTransitionTest() : ScopedViewTransitionOnNavigationForTest(true) {}
 
   void SetUp() override {
     web_view_helper_ = std::make_unique<frame_test_helpers::WebViewHelper>();
@@ -1291,4 +1292,43 @@
   ASSERT_GT(GetDocument().getAnimations().size(), 0ul);
 }
 
+TEST_P(ViewTransitionTest, ScriptCallAfterNavigationTransition) {
+  V8TestingScope v8_scope;
+  ScriptState* script_state = v8_scope.GetScriptState();
+  ExceptionState& exception_state = v8_scope.GetExceptionState();
+
+  ViewTransitionSupplement::SnapshotDocumentForNavigation(
+      GetDocument(), base::BindOnce([](const ViewTransitionState&) {}));
+
+  ASSERT_TRUE(ViewTransitionSupplement::From(GetDocument())->GetTransition());
+
+  bool callback_issued = false;
+
+  // This callback sets the elements for the start phase of the transition.
+  auto start_setup_lambda =
+      [](const v8::FunctionCallbackInfo<v8::Value>& info) {
+        auto* callback_issued =
+            static_cast<bool*>(info.Data().As<v8::External>()->Value());
+        *callback_issued = true;
+      };
+  auto start_setup_callback =
+      v8::Function::New(
+          v8_scope.GetContext(), start_setup_lambda,
+          v8::External::New(v8_scope.GetIsolate(), &callback_issued))
+          .ToLocalChecked();
+  DOMViewTransition* script_transition =
+      ViewTransitionSupplement::startViewTransition(
+          script_state, GetDocument(),
+          V8ViewTransitionCallback::Create(start_setup_callback),
+          exception_state);
+
+  EXPECT_TRUE(script_transition);
+
+  UpdateAllLifecyclePhasesAndFinishDirectives();
+  test::RunPendingTasks();
+  UpdateAllLifecyclePhasesAndFinishDirectives();
+
+  EXPECT_TRUE(callback_issued);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/workers/worklet_module_responses_map_test.cc b/third_party/blink/renderer/core/workers/worklet_module_responses_map_test.cc
index c12d7dd..d91af443 100644
--- a/third_party/blink/renderer/core/workers/worklet_module_responses_map_test.cc
+++ b/third_party/blink/renderer/core/workers/worklet_module_responses_map_test.cc
@@ -34,12 +34,21 @@
 class WorkletModuleResponsesMapTest : public PageTestBase {
  public:
   WorkletModuleResponsesMapTest()
-      : url_("https://example.test"),
-        security_origin_(SecurityOrigin::Create(url_)) {}
+      : PageTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
+        url_("https://example.test"),
+        security_origin_(SecurityOrigin::Create(url_)) {
+    if (!task_environment()) {
+      // TODO(crbug.com/1315595): Remove once TaskEnvironment becomes the
+      // default in blink_unittests_v2
+      test_task_runner_ = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
+          base::TestMockTimeTaskRunner::Type::kStandalone);
+      test_task_runner_->AdvanceMockTickClock(
+          base::Seconds(1));  // For non-zero DocumentParserTimings
+    }
+  }
 
   void SetUp() override {
     PageTestBase::SetUp();
-    platform_->AdvanceClockSeconds(1.);  // For non-zero DocumentParserTimings
     auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
     auto* context = MakeGarbageCollected<MockFetchContext>();
     fetcher_ = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
@@ -79,10 +88,6 @@
     PageTestBase::TearDown();
   }
 
-  const base::TickClock* GetTickClock() override {
-    return platform_->test_task_runner()->GetMockTickClock();
-  }
-
   class ClientImpl final : public GarbageCollected<ClientImpl>,
                            public ModuleScriptFetcher::Client {
    public:
@@ -130,7 +135,15 @@
         ->RunUntilIdle();
   }
 
+  const base::TickClock* GetTickClock() override {
+    if (test_task_runner_) {
+      return test_task_runner_->GetMockTickClock();
+    }
+    return PageTestBase::GetTickClock();
+  }
+
  protected:
+  scoped_refptr<base::TestMockTimeTaskRunner> test_task_runner_;
   ScopedTestingPlatformSupport<FetchTestingPlatformSupport> platform_;
 
   const KURL url_;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 92dd0dba..031b51f 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -4444,7 +4444,7 @@
     // results are serialized.
     if (!CachedChildrenIncludingIgnored().empty()) {
       AXObjectCache().AddDirtyObjectToSerializationQueue(
-          this, /*subtree*/ false, ax::mojom::blink::EventFrom::kNone,
+          this, ax::mojom::blink::EventFrom::kNone,
           ax::mojom::blink::Action::kNone, {});
     }
   } else {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index 9d2fee0..4190ff3 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -1850,10 +1850,10 @@
   // If a DOM node is present, it will have been used to back the AXObject, in
   // which case we need to call Remove(node) instead.
   if (Node* node = layout_object->GetNode()) {
-    // Pseudo elements are a special case. They need to be marked dirty so that
-    // their entire subtree is recomputed (it is disappearing or changing).
+    // Pseudo elements are a special case. The entire subtree needs to be marked
+    // dirty so that it is recomputed (it is disappearing or changing).
     if (node->IsPseudoElement()) {
-      DeferTreeUpdate(TreeUpdateReason::kMarkDirtyFromRemove, node);
+      MarkSubtreeDirty(node);
     }
 
     if (IsA<HTMLImageElement>(node)) {
@@ -2598,7 +2598,7 @@
                layout_parent->Parent()->IsLayoutInline()) {
           layout_parent = layout_parent->Parent();
         }
-        MarkAXSubtreeDirty(Get(layout_parent));
+        MarkSubtreeDirty(layout_parent->GetNode());
       }
     }
     return;
@@ -3297,10 +3297,10 @@
       }
     }
 
-    CHECK(!IsDirty());
+    DUMP_WILL_BE_CHECK(!IsDirty());
     // TODO(accessibility): in the future, we may break up serialization into
     // pieces to reduce jank, in which case this assertion will not hold.
-    CHECK(!HasDirtyObjects() || !did_serialize)
+    DUMP_WILL_BE_CHECK(!HasDirtyObjects() || !did_serialize)
         << "A serialization occurred but dirty objects remained.";
   }
 }
@@ -3673,7 +3673,6 @@
       break;
     case TreeUpdateReason::kMarkDirtyFromHandleLayout:
     case TreeUpdateReason::kMarkDirtyFromHandleScroll:
-    case TreeUpdateReason::kMarkDirtyFromRemove:
       MarkAXObjectDirtyWithCleanLayout(Get(node));
       break;
     case TreeUpdateReason::kNodeGainedFocus:
@@ -4649,9 +4648,8 @@
 
   pending_events_.push_back(event);
 
-  AddDirtyObjectToSerializationQueue(obj, false, event.event_from,
-                                     event.event_from_action,
-                                     event.event_intents);
+  AddDirtyObjectToSerializationQueue(
+      obj, event.event_from, event.event_from_action, event.event_intents);
 
   if (immediate_serialization) {
     ScheduleImmediateSerialization();
@@ -4736,7 +4734,6 @@
 
 void AXObjectCacheImpl::MarkAXObjectDirtyWithCleanLayoutHelper(
     AXObject* obj,
-    bool subtree,
     ax::mojom::blink::EventFrom event_from,
     ax::mojom::blink::Action event_from_action) {
   CHECK(!IsFrozen());
@@ -4774,22 +4771,18 @@
   }
 
   std::vector<ui::AXEventIntent> event_intents;
-  AddDirtyObjectToSerializationQueue(obj, subtree, event_from,
-                                     event_from_action, event_intents);
+  AddDirtyObjectToSerializationQueue(obj, event_from, event_from_action,
+                                     event_intents);
 
   obj->UpdateCachedAttributeValuesIfNeeded(true);
-  for (auto agent : agents_)
-    agent->AXObjectModified(obj, subtree);
 }
 
 void AXObjectCacheImpl::MarkAXObjectDirtyWithCleanLayout(AXObject* obj) {
-  MarkAXObjectDirtyWithCleanLayoutHelper(obj, false, active_event_from_,
+  MarkAXObjectDirtyWithCleanLayoutHelper(obj, active_event_from_,
                                          active_event_from_action_);
-}
-
-void AXObjectCacheImpl::MarkAXSubtreeDirtyWithCleanLayout(AXObject* obj) {
-  MarkAXObjectDirtyWithCleanLayoutHelper(obj, true, active_event_from_,
-                                         active_event_from_action_);
+  for (auto agent : agents_) {
+    agent->AXObjectModified(obj, /*subtree*/ false);
+  }
 }
 
 void AXObjectCacheImpl::MarkAXObjectDirty(AXObject* obj) {
@@ -4802,6 +4795,24 @@
   DeferTreeUpdate(TreeUpdateReason::kMarkAXObjectDirty, obj);
 }
 
+void AXObjectCacheImpl::NotifySubtreeDirty(AXObject* obj) {
+  // Note: if there is no serializer yet, then there is nothing to mark dirty
+  // for serialization purposes yet -- effectively everything starts out dirty
+  // in a new serializer.
+  if (ax_tree_serializer_) {
+    ax_tree_serializer_->MarkSubtreeDirty(obj->AXObjectID());
+  }
+  for (auto agent : agents_) {
+    agent->AXObjectModified(obj, /*subtree*/ true);
+  }
+}
+
+void AXObjectCacheImpl::MarkAXSubtreeDirtyWithCleanLayout(AXObject* obj) {
+  MarkAXObjectDirtyWithCleanLayoutHelper(obj, active_event_from_,
+                                         active_event_from_action_);
+  NotifySubtreeDirty(obj);
+}
+
 void AXObjectCacheImpl::MarkAXSubtreeDirty(AXObject* obj) {
   if (!obj)
     return;
@@ -4809,6 +4820,15 @@
   DeferTreeUpdate(TreeUpdateReason::kMarkAXSubtreeDirty, obj);
 }
 
+void AXObjectCacheImpl::MarkSubtreeDirty(Node* node) {
+  if (AXObject* obj = Get(node)) {
+    MarkAXSubtreeDirty(obj);
+  } else {
+    // There is no AXObject, so there is no subtree to mark dirty.
+    MarkElementDirty(node);
+  }
+}
+
 void AXObjectCacheImpl::MarkDocumentDirty() {
   CHECK(!IsFrozen());
   mark_all_dirty_ = true;
@@ -5097,21 +5117,14 @@
 
 void AXObjectCacheImpl::AddDirtyObjectToSerializationQueue(
     AXObject* obj,
-    bool subtree,
     ax::mojom::blink::EventFrom event_from,
     ax::mojom::blink::Action event_from_action,
     const std::vector<ui::AXEventIntent>& event_intents) {
   CHECK(!IsFrozen());
+  // Add to object to a queue that will be sent to the serializer in
+  // SerializeDirtyObjectsAndEvents().
   dirty_objects_.push_back(
       AXDirtyObject::Create(obj, event_from, event_from_action, event_intents));
-
-  // Note: if there is no serializer yet, then there is no need to create one
-  // just to mark a subtree dirty -- descendant nodes will automatically be
-  // serialized in a new serializer anyway, because those nodes hadn't
-  // previously been serialized.
-  if (ax_tree_serializer_ && subtree) {
-    MarkSerializerSubtreeDirty(*obj);
-  }
 }
 
 void AXObjectCacheImpl::SerializeDirtyObjectsAndEvents(
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
index 99ecf74e..48adcfe 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
@@ -301,8 +301,6 @@
   void ProcessDeferredAccessibilityEvents(Document&, bool force) override;
   // Remove AXObject subtrees (once flat tree traversal is safe).
   void ProcessSubtreeRemovals() override;
-  // Is there work to be done when layout becomes clean?
-  bool IsDirty() override;
 
   // Called when a HTMLFrameOwnerElement (such as an iframe element) changes the
   // embedding token of its child frame.
@@ -388,6 +386,8 @@
   void MarkAXObjectDirtyWithCleanLayout(AXObject*);
 
   void MarkAXSubtreeDirtyWithCleanLayout(AXObject*);
+  void MarkSubtreeDirty(Node*);
+  void NotifySubtreeDirty(AXObject* obj);
 
   // Set the parent of |child|. If no parent is possible, this means the child
   // can no longer be in the AXTree, so remove the child.
@@ -519,7 +519,6 @@
   // Marks an object as dirty to be serialized in the next serialization.
   void AddDirtyObjectToSerializationQueue(
       AXObject* obj,
-      bool subtree = false,
       ax::mojom::blink::EventFrom event_from =
           ax::mojom::blink::EventFrom::kNone,
       ax::mojom::blink::Action event_from_action =
@@ -538,21 +537,18 @@
                            std::vector<ui::AXNodeData*>& nodes) override;
 
   // The difference between this and IsDirty():
-  // - IsDirty() means there are updates to be processed to have a complete
-  // representation in the tree structure.
+  // - IsDirty() means there are updates to be processed when layout becomes
+  // clean, in order to have a complete representation in the tree structure.
   // - HasDirtyOirtyObjects() means there are updates ready to be sent
   // to the serializer.
   // TODO(accessibility) Differentiate naming -- there are too many kinds of
   // "dirty", which leads to confusion.
   bool HasDirtyObjects() const override { return !dirty_objects_.empty(); }
-  bool IsDirty(AXObject& obj) { return ax_tree_serializer_->IsDirty(&obj); }
+  bool IsDirty() override;
 
   bool AddPendingEvent(const ui::AXEvent& event,
                        bool insert_at_beginning) override;
 
-  void MarkSerializerSubtreeDirty(AXObject& obj) {
-    ax_tree_serializer_->MarkSubtreeDirty(&obj);
-  }
   void SetImageAsDataNodeId(int id, const gfx::Size& max_size) {
     ax_tree_source_->set_image_data_node_id(id, max_size);
   }
@@ -770,31 +766,30 @@
     kIdChanged = 10,
     kMarkDirtyFromHandleLayout = 11,
     kMarkDirtyFromHandleScroll = 12,
-    kMarkDirtyFromRemove = 13,
-    kNodeGainedFocus = 14,
-    kNodeLostFocus = 15,
-    kPostNotificationFromHandleLoadComplete = 16,
-    kPostNotificationFromHandleLoadStart = 17,
-    kPostNotificationFromHandleScrolledToAnchor = 18,
-    kRemoveValidationMessageObjectFromFocusedUIElement = 19,
-    kRemoveValidationMessageObjectFromValidationMessageObject = 20,
-    kRoleChangeFromAriaHasPopup = 21,
-    kRoleChangeFromImageMapName = 22,
-    kRoleChangeFromRoleOrType = 23,
-    kRoleMaybeChangedFromEventListener = 24,
-    kRoleMaybeChangedFromHref = 25,
-    kSectionOrRegionRoleMaybeChangedFromLabel = 26,
-    kSectionOrRegionRoleMaybeChangedFromLabelledBy = 27,
-    kSectionOrRegionRoleMaybeChangedFromTitle = 28,
-    kTextChangedOnNode = 29,
-    kTextChangedOnClosestNodeForLayoutObject = 30,
-    kTextMarkerDataAdded = 31,
-    kUpdateActiveMenuOption = 32,
-    kNodeIsAttached = 33,
-    kUpdateAriaOwns = 34,
-    kUpdateTableRole = 35,
-    kUseMapAttributeChanged = 36,
-    kValidationMessageVisibilityChanged = 37,
+    kNodeGainedFocus = 13,
+    kNodeLostFocus = 14,
+    kPostNotificationFromHandleLoadComplete = 15,
+    kPostNotificationFromHandleLoadStart = 16,
+    kPostNotificationFromHandleScrolledToAnchor = 17,
+    kRemoveValidationMessageObjectFromFocusedUIElement = 18,
+    kRemoveValidationMessageObjectFromValidationMessageObject = 19,
+    kRoleChangeFromAriaHasPopup = 20,
+    kRoleChangeFromImageMapName = 21,
+    kRoleChangeFromRoleOrType = 22,
+    kRoleMaybeChangedFromEventListener = 23,
+    kRoleMaybeChangedFromHref = 24,
+    kSectionOrRegionRoleMaybeChangedFromLabel = 25,
+    kSectionOrRegionRoleMaybeChangedFromLabelledBy = 26,
+    kSectionOrRegionRoleMaybeChangedFromTitle = 27,
+    kTextChangedOnNode = 28,
+    kTextChangedOnClosestNodeForLayoutObject = 29,
+    kTextMarkerDataAdded = 30,
+    kUpdateActiveMenuOption = 31,
+    kNodeIsAttached = 32,
+    kUpdateAriaOwns = 33,
+    kUpdateTableRole = 34,
+    kUseMapAttributeChanged = 35,
+    kValidationMessageVisibilityChanged = 36,
 
     // These updates are associated with an AXID:
     kChildrenChanged = 100,
@@ -846,7 +841,6 @@
 
   void MarkAXObjectDirtyWithCleanLayoutHelper(
       AXObject* obj,
-      bool subtree,
       ax::mojom::blink::EventFrom event_from,
       ax::mojom::blink::Action event_from_action);
   void MarkAXSubtreeDirty(AXObject*);
diff --git a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
index eb0e9a6..1a960c8 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
@@ -873,12 +873,14 @@
 }
 
 void AXRelationCache::UpdateRelatedText(Node* node) {
-  if (AXObject* obj = Get(node)) {
-    if (!obj->IsUsedForLabelOrDescription()) {
-      // Nothing to do, because this node is not part of a label or description.
-      return;
-    }
-  }
+  // TODO(accessibility) Restore this optimization, which is currently failing
+  // on All/DumpAccessibilityTreeTest.AccessibilityDisabledWithSubtree/blink.
+  // if (AXObject* obj = Get(node)) {
+  //   if (!obj->IsUsedForLabelOrDescription()) {
+  //     // Nothing to do, as this node is not part of a label or description.
+  //     return;
+  //   }
+  // }
 
   // Walk up ancestor chain from node and refresh text of any related content.
   // TODO(crbug.com/1109265): It's very likely this loop should only walk the
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.idl b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.idl
index e6482c5..60cfac0 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.idl
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.idl
@@ -62,7 +62,7 @@
     // state
     void save(); // push state on state stack
     [NoAllocDirectCall, RaisesException] void restore(); // pop state stack if top state was pushed by save, and restore state
-    [RuntimeEnabled=Canvas2dLayers, CallWith=ScriptState, RaisesException] void beginLayer(optional BeginLayerOptions options = {}); // push state on state stack and creates bitmap for subsequent draw ops
+    [MeasureAs=Canvas2DLayers, RuntimeEnabled=Canvas2dLayers, CallWith=ScriptState, RaisesException] void beginLayer(optional BeginLayerOptions options = {}); // push state on state stack and creates bitmap for subsequent draw ops
     [NoAllocDirectCall, RuntimeEnabled=Canvas2dLayers, RaisesException] void endLayer(); // pop state stack if top state was pushed by beginLayer, restore state and draw the bitmap
     // Clear the canvas and reset the path
     void reset();
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.idl b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.idl
index bc0c486..5025e6b 100644
--- a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.idl
+++ b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.idl
@@ -16,7 +16,7 @@
     // state
     void save(); // push state on state stack
     [NoAllocDirectCall, RaisesException] void restore(); // pop state stack if top state was pushed by save, and restore state
-    [RuntimeEnabled=Canvas2dLayers, CallWith=ScriptState, RaisesException] void beginLayer(optional BeginLayerOptions options = {}); // push state on state stack and creates bitmap for subsequent draw ops
+    [MeasureAs=Canvas2DLayers, RuntimeEnabled=Canvas2dLayers, CallWith=ScriptState, RaisesException] void beginLayer(optional BeginLayerOptions options = {}); // push state on state stack and creates bitmap for subsequent draw ops
     [NoAllocDirectCall, RuntimeEnabled=Canvas2dLayers, RaisesException] void endLayer(); // pop state stack if top state was pushed by beginLayer, restore state and draw the bitmap
     // Clear the canvas and reset the path
     void reset();
diff --git a/third_party/blink/renderer/modules/credentialmanagement/credential_manager_proxy.cc b/third_party/blink/renderer/modules/credentialmanagement/credential_manager_proxy.cc
index aa16bbc..d06c4a8e 100644
--- a/third_party/blink/renderer/modules/credentialmanagement/credential_manager_proxy.cc
+++ b/third_party/blink/renderer/modules/credentialmanagement/credential_manager_proxy.cc
@@ -17,7 +17,8 @@
       credential_manager_(window.GetExecutionContext()),
       webotp_service_(window.GetExecutionContext()),
       payment_credential_(window.GetExecutionContext()),
-      federated_auth_request_(window.GetExecutionContext()) {}
+      federated_auth_request_(window.GetExecutionContext()),
+      digital_identity_request_(window.GetExecutionContext()) {}
 
 CredentialManagerProxy::~CredentialManagerProxy() = default;
 
@@ -98,6 +99,20 @@
   // appropriate error message.
 }
 
+mojom::blink::DigitalIdentityRequest*
+CredentialManagerProxy::DigitalIdentityRequest() {
+  BindRemoteForFedCm(
+      digital_identity_request_,
+      WTF::BindOnce(
+          &CredentialManagerProxy::OnDigitalIdentityRequestConnectionError,
+          WrapWeakPersistent(this)));
+  return digital_identity_request_.get();
+}
+
+void CredentialManagerProxy::OnDigitalIdentityRequestConnectionError() {
+  digital_identity_request_.reset();
+}
+
 // TODO(crbug.com/1372275): Replace From(ScriptState*) with
 // From(ExecutionContext*)
 // static
@@ -139,6 +154,7 @@
   visitor->Trace(webotp_service_);
   visitor->Trace(payment_credential_);
   visitor->Trace(federated_auth_request_);
+  visitor->Trace(digital_identity_request_);
   Supplement<LocalDOMWindow>::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/modules/credentialmanagement/credential_manager_proxy.h b/third_party/blink/renderer/modules/credentialmanagement/credential_manager_proxy.h
index 9a12bb8..d8e6461 100644
--- a/third_party/blink/renderer/modules/credentialmanagement/credential_manager_proxy.h
+++ b/third_party/blink/renderer/modules/credentialmanagement/credential_manager_proxy.h
@@ -10,6 +10,7 @@
 #include "third_party/blink/public/mojom/payments/payment_credential.mojom-blink.h"
 #include "third_party/blink/public/mojom/sms/webotp_service.mojom-blink.h"
 #include "third_party/blink/public/mojom/webauthn/authenticator.mojom-blink.h"
+#include "third_party/blink/public/mojom/webid/digital_identity_request.mojom-blink.h"
 #include "third_party/blink/public/mojom/webid/federated_auth_request.mojom-blink.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
@@ -52,6 +53,8 @@
 
   mojom::blink::FederatedAuthRequest* FederatedAuthRequest();
 
+  mojom::blink::DigitalIdentityRequest* DigitalIdentityRequest();
+
   void Trace(Visitor*) const override;
 
   // Must be called only with argument representing a valid
@@ -66,12 +69,15 @@
   void BindRemoteForFedCm(HeapMojoRemote<Interface>& remote,
                           base::OnceClosure disconnect_closure);
   void OnFederatedAuthRequestConnectionError();
+  void OnDigitalIdentityRequestConnectionError();
 
   HeapMojoRemote<mojom::blink::Authenticator> authenticator_;
   HeapMojoRemote<mojom::blink::CredentialManager> credential_manager_;
   HeapMojoRemote<mojom::blink::WebOTPService> webotp_service_;
   HeapMojoRemote<payments::mojom::blink::PaymentCredential> payment_credential_;
   HeapMojoRemote<mojom::blink::FederatedAuthRequest> federated_auth_request_;
+  HeapMojoRemote<mojom::blink::DigitalIdentityRequest>
+      digital_identity_request_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/credentialmanagement/credentials_container.cc b/third_party/blink/renderer/modules/credentialmanagement/credentials_container.cc
index e341c42..d8a55a6 100644
--- a/third_party/blink/renderer/modules/credentialmanagement/credentials_container.cc
+++ b/third_party/blink/renderer/modules/credentialmanagement/credentials_container.cc
@@ -99,6 +99,7 @@
 using mojom::blink::CredentialManagerError;
 using mojom::blink::CredentialMediationRequirement;
 using mojom::blink::PaymentCredentialInstrument;
+using mojom::blink::RequestDigitalIdentityStatus;
 using mojom::blink::WebAuthnDOMExceptionDetailsPtr;
 using MojoPublicKeyCredentialCreationOptions =
     mojom::blink::PublicKeyCredentialCreationOptions;
@@ -640,6 +641,16 @@
   auth_request->CancelTokenRequest();
 }
 
+// Abort an ongoing WebIdentityDigitalCredential request. This will only be
+// called before the request finishes due to `scoped_abort_state`.
+void AbortDigitalIdentityRequest(ScriptState* script_state) {
+  if (!script_state->ContextIsValid()) {
+    return;
+  }
+
+  CredentialManagerProxy::From(script_state)->DigitalIdentityRequest()->Abort();
+}
+
 void OnRequestToken(ScriptPromiseResolver* resolver,
                     std::unique_ptr<ScopedAbortState> scoped_abort_state,
                     const CredentialRequestOptions* options,
@@ -691,6 +702,49 @@
   }
 }
 
+void OnRequestDigitalIdentity(
+    ScriptPromiseResolver* resolver,
+    std::unique_ptr<ScopedAbortState> scoped_abort_state,
+    RequestDigitalIdentityStatus status,
+    const WTF::String& token) {
+  switch (status) {
+    case RequestDigitalIdentityStatus::kErrorTooManyRequests: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kAbortError,
+          "Only one navigator.credentials.get request may be outstanding at "
+          "one time."));
+      return;
+    }
+    case RequestDigitalIdentityStatus::kErrorCanceled: {
+      AbortSignal* signal =
+          scoped_abort_state ? scoped_abort_state->Signal() : nullptr;
+      if (signal && signal->aborted()) {
+        auto* script_state = resolver->GetScriptState();
+        ScriptState::Scope script_state_scope(script_state);
+        resolver->Reject(signal->reason(script_state));
+      } else {
+        resolver->Reject(MakeGarbageCollected<DOMException>(
+            DOMExceptionCode::kAbortError, "The request has been aborted."));
+      }
+      return;
+    }
+    case RequestDigitalIdentityStatus::kError: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kNetworkError, "Error retrieving a token."));
+      return;
+    }
+    case RequestDigitalIdentityStatus::kSuccess: {
+      IdentityCredential* credential =
+          IdentityCredential::Create(token, /*is_auto_selected=*/false);
+      resolver->Resolve(credential);
+      return;
+    }
+    default: {
+      NOTREACHED();
+    }
+  }
+}
+
 void OnStoreComplete(std::unique_ptr<ScopedPromiseResolver> scoped_resolver) {
   auto* resolver = scoped_resolver->Release();
   AssertSecurityRequirementsBeforeResponse(
@@ -2221,31 +2275,23 @@
 
   std::unique_ptr<ScopedAbortState> scoped_abort_state;
   if (auto* signal = options.getSignalOr(nullptr)) {
-    auto callback = WTF::BindOnce(&AbortIdentityCredentialRequest,
+    auto callback = WTF::BindOnce(&AbortDigitalIdentityRequest,
                                   WrapPersistent(script_state));
 
     auto* handle = signal->AddAlgorithm(std::move(callback));
     scoped_abort_state = std::make_unique<ScopedAbortState>(signal, handle);
   }
 
-  Vector<mojom::blink::IdentityProviderPtr> identity_provider_ptrs;
-  identity_provider_ptrs.push_back(
-      blink::mojom::blink::IdentityProvider::From(first_identity_provider));
+  auto digital_credential_provider =
+      blink::mojom::blink::DigitalCredentialProvider::From(
+          *first_identity_provider.holder());
 
-  mojom::blink::IdentityProviderGetParametersPtr get_params =
-      mojom::blink::IdentityProviderGetParameters::New(
-          std::move(identity_provider_ptrs), mojom::blink::RpContext::kSignIn,
-          mojom::blink::RpMode::kWidget);
-
-  Vector<mojom::blink::IdentityProviderGetParametersPtr> idp_get_params;
-  idp_get_params.push_back(std::move(get_params));
-
-  auto* auth_request =
-      CredentialManagerProxy::From(script_state)->FederatedAuthRequest();
-  auth_request->RequestToken(
-      std::move(idp_get_params), CredentialMediationRequirement::kRequired,
-      WTF::BindOnce(&OnRequestToken, WrapPersistent(resolver),
-                    std::move(scoped_abort_state), WrapPersistent(&options)));
+  auto* request =
+      CredentialManagerProxy::From(script_state)->DigitalIdentityRequest();
+  request->Request(
+      std::move(digital_credential_provider),
+      WTF::BindOnce(&OnRequestDigitalIdentity, WrapPersistent(resolver),
+                    std::move(scoped_abort_state)));
   return promise;
 }
 
diff --git a/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.idl b/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.idl
index 8bc9a2d..99ba9d7 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.idl
+++ b/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.idl
@@ -10,7 +10,7 @@
     // state
     void save(); // push state on state stack
     [RaisesException] void restore(); // pop state stack if top state was pushed by save, and restore state
-    [RuntimeEnabled=Canvas2dLayers, CallWith=ScriptState, RaisesException] void beginLayer(optional BeginLayerOptions options = {}); // push state on state stack and creates bitmap for subsequent draw ops
+    [MeasureAs=Canvas2DLayers, RuntimeEnabled=Canvas2dLayers, CallWith=ScriptState, RaisesException] void beginLayer(optional BeginLayerOptions options = {}); // push state on state stack and creates bitmap for subsequent draw ops
     [RuntimeEnabled=Canvas2dLayers, RaisesException] void endLayer(); // pop state stack if top state was pushed by beginLayer, restore state and draw the bitmap
     // Clear the canvas and reset the path
     void reset();
diff --git a/third_party/blink/renderer/modules/exported/web_ax_object.cc b/third_party/blink/renderer/modules/exported/web_ax_object.cc
index 8581c0d..8e27d63 100644
--- a/third_party/blink/renderer/modules/exported/web_ax_object.cc
+++ b/third_party/blink/renderer/modules/exported/web_ax_object.cc
@@ -209,21 +209,14 @@
   private_->Serialize(node_data, accessibility_mode);
 }
 
-void WebAXObject::MarkSerializerSubtreeDirty() const {
-  if (IsDetached())
-    return;
-  private_->AXObjectCache().MarkSerializerSubtreeDirty(*private_);
-}
-
 void WebAXObject::AddDirtyObjectToSerializationQueue(
-    bool subtree,
     ax::mojom::blink::EventFrom event_from,
     ax::mojom::blink::Action event_from_action,
     std::vector<ui::AXEventIntent> event_intents) const {
   if (IsDetached())
     return;
   private_->AXObjectCache().AddDirtyObjectToSerializationQueue(
-      private_.Get(), subtree, event_from, event_from_action, event_intents);
+      private_.Get(), event_from, event_from_action, event_intents);
 }
 
 void WebAXObject::OnLoadInlineTextBoxes() const {
diff --git a/third_party/blink/renderer/modules/mediasession/media_position_state.idl b/third_party/blink/renderer/modules/mediasession/media_position_state.idl
index f0de23c..142eef7 100644
--- a/third_party/blink/renderer/modules/mediasession/media_position_state.idl
+++ b/third_party/blink/renderer/modules/mediasession/media_position_state.idl
@@ -5,7 +5,7 @@
 // https://w3c.github.io/mediasession/#the-mediapositionstate-dictionary
 
 dictionary MediaPositionState {
-  double duration;
+  unrestricted double duration;
   double playbackRate;
   double position;
 };
diff --git a/third_party/blink/renderer/modules/mediasession/media_session.cc b/third_party/blink/renderer/modules/mediasession/media_session.cc
index 10f9260..b1d7254b 100644
--- a/third_party/blink/renderer/modules/mediasession/media_session.cc
+++ b/third_party/blink/renderer/modules/mediasession/media_session.cc
@@ -288,6 +288,12 @@
     return;
   }
 
+  // The duration cannot be NaN.
+  if (std::isnan(position_state->duration())) {
+    exception_state.ThrowTypeError("The provided duration cannot be NaN.");
+    return;
+  }
+
   // The duration cannot be negative.
   if (position_state->duration() < 0) {
     exception_state.ThrowTypeError(
diff --git a/third_party/blink/renderer/modules/mediasession/media_session_test.cc b/third_party/blink/renderer/modules/mediasession/media_session_test.cc
index 0871f6b..55a5275 100644
--- a/third_party/blink/renderer/modules/mediasession/media_session_test.cc
+++ b/third_party/blink/renderer/modules/mediasession/media_session_test.cc
@@ -86,6 +86,20 @@
     media_session_->setPositionState(position_state, exception_state);
   }
 
+  void SetPositionStateThrowsException(double duration,
+                                       double position,
+                                       double playback_rate) {
+    auto* position_state = MediaPositionState::Create();
+    position_state->setDuration(duration);
+    position_state->setPosition(position);
+    position_state->setPlaybackRate(playback_rate);
+
+    DummyExceptionStateForTesting exception_state;
+    media_session_->setPositionState(position_state, exception_state);
+    EXPECT_TRUE(exception_state.HadException());
+    EXPECT_EQ(ESErrorType::kTypeError, exception_state.CodeAs<ESErrorType>());
+  }
+
   void ClearPositionState() {
     NonThrowableExceptionState exception_state;
     media_session_->setPositionState(MediaPositionState::Create(),
@@ -159,6 +173,28 @@
   loop.Run();
 }
 
+TEST_F(MediaSessionTest, PlaybackPositionState_InfiniteDuration) {
+  base::RunLoop loop;
+  EXPECT_CALL(service(), SetPositionState(_))
+      .WillOnce(testing::Invoke([&](auto position_state) {
+        EXPECT_EQ(base::TimeDelta::Max(), position_state->duration);
+        EXPECT_EQ(base::Seconds(5), position_state->position);
+        EXPECT_EQ(1.0, position_state->playback_rate);
+        EXPECT_EQ(clock().NowTicks(), position_state->last_updated_time);
+
+        loop.Quit();
+      }));
+
+  SetPlaybackState("none");
+  SetPositionState(std::numeric_limits<double>::infinity(), 5, 1.0);
+  loop.Run();
+}
+
+TEST_F(MediaSessionTest, PlaybackPositionState_NaNDuration) {
+  SetPlaybackState("none");
+  SetPositionStateThrowsException(std::nan("10"), 5, 1.0);
+}
+
 TEST_F(MediaSessionTest, PlaybackPositionState_Paused_Clear) {
   {
     base::RunLoop loop;
diff --git a/third_party/blink/renderer/modules/mediasession/media_session_type_converters.cc b/third_party/blink/renderer/modules/mediasession/media_session_type_converters.cc
index dc8ba0e..52aeea55 100644
--- a/third_party/blink/renderer/modules/mediasession/media_session_type_converters.cc
+++ b/third_party/blink/renderer/modules/mediasession/media_session_type_converters.cc
@@ -45,7 +45,9 @@
                                              position) {
   return media_session::mojom::blink::MediaPosition::New(
       position->hasPlaybackRate() ? position->playbackRate() : 1.0,
-      base::Seconds(position->duration()),
+      position->duration() == std::numeric_limits<double>::infinity()
+          ? base::TimeDelta::Max()
+          : base::Seconds(position->duration()),
       position->hasPosition() ? base::Seconds(position->position())
                               : base::TimeDelta(),
       base::TimeTicks::Now());
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc
index 8c5a4b2..380c85e 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc
@@ -62,7 +62,7 @@
   // a requirement. This really just expects montonicity.
   enum ClusterDirection { kAscending, kDescending } cluster_direction;
 };
-using ExpectedBlob = std::vector<const ExpectedRun>;
+using ExpectedBlob = std::vector<ExpectedRun>;
 
 struct ExpectedRange {
   unsigned from;
@@ -71,7 +71,7 @@
 };
 
 void CheckBlobBuffer(const ShapeResultBloberizer::BlobBuffer& blob_buffer,
-                     const std::vector<const ExpectedBlob>& expected_blobs) {
+                     const std::vector<ExpectedBlob>& expected_blobs) {
   EXPECT_EQ(blob_buffer.size(), expected_blobs.size());
   const ShapeResultBloberizer::BlobInfo* blob_info_iter = blob_buffer.begin();
   auto&& expected_blob_iter = expected_blobs.begin();
diff --git a/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.cc b/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.cc
index ec4c8e5..cb4faa46 100644
--- a/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.cc
@@ -20,6 +20,15 @@
 
 // static
 BufferingBytesConsumer* BufferingBytesConsumer::CreateWithDelay(
+    BytesConsumer* bytes_consumer) {
+  return MakeGarbageCollected<BufferingBytesConsumer>(
+      base::PassKey<BufferingBytesConsumer>(), bytes_consumer,
+      base::SingleThreadTaskRunner::GetCurrentDefault(),
+      base::Milliseconds(kDelayMilliseconds));
+}
+
+// static
+BufferingBytesConsumer* BufferingBytesConsumer::CreateWithDelayForTest(
     BytesConsumer* bytes_consumer,
     scoped_refptr<base::SingleThreadTaskRunner> timer_task_runner) {
   return MakeGarbageCollected<BufferingBytesConsumer>(
@@ -44,6 +53,7 @@
       timer_(std::move(timer_task_runner),
              this,
              &BufferingBytesConsumer::OnTimerFired) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   bytes_consumer_->SetClient(this);
   if (buffering_start_delay.is_zero()) {
     MaybeStartBuffering();
@@ -55,6 +65,7 @@
 BufferingBytesConsumer::~BufferingBytesConsumer() = default;
 
 void BufferingBytesConsumer::MaybeStartBuffering() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (buffering_state_ != BufferingState::kDelayed)
     return;
   timer_.Stop();
@@ -63,12 +74,14 @@
 }
 
 void BufferingBytesConsumer::StopBuffering() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   timer_.Stop();
   buffering_state_ = BufferingState::kStopped;
 }
 
 BytesConsumer::Result BufferingBytesConsumer::BeginRead(const char** buffer,
                                                         size_t* available) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // Stop delaying buffering on the first read as it will no longer be safe to
   // drain the underlying |bytes_consumer_| anyway.
   MaybeStartBuffering();
@@ -101,6 +114,7 @@
 }
 
 BytesConsumer::Result BufferingBytesConsumer::EndRead(size_t read_size) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (buffer_.empty()) {
     if (buffering_state_ != BufferingState::kStarted)
       return bytes_consumer_->EndRead(read_size);
@@ -130,14 +144,17 @@
 
 scoped_refptr<BlobDataHandle> BufferingBytesConsumer::DrainAsBlobDataHandle(
     BlobSizePolicy policy) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return bytes_consumer_->DrainAsBlobDataHandle(policy);
 }
 
 scoped_refptr<EncodedFormData> BufferingBytesConsumer::DrainAsFormData() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return bytes_consumer_->DrainAsFormData();
 }
 
 mojo::ScopedDataPipeConsumerHandle BufferingBytesConsumer::DrainAsDataPipe() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (buffering_state_ != BufferingState::kStarted)
     return bytes_consumer_->DrainAsDataPipe();
 
@@ -157,21 +174,25 @@
 }
 
 void BufferingBytesConsumer::Cancel() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   ClearClient();
   bytes_consumer_->Cancel();
 }
 
 BytesConsumer::PublicState BufferingBytesConsumer::GetPublicState() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (buffer_.empty())
     return bytes_consumer_->GetPublicState();
   return PublicState::kReadableOrWaiting;
 }
 
 BytesConsumer::Error BufferingBytesConsumer::GetError() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return bytes_consumer_->GetError();
 }
 
 String BufferingBytesConsumer::DebugName() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   StringBuilder builder;
   builder.Append("BufferingBytesConsumer(");
   builder.Append(bytes_consumer_->DebugName());
@@ -189,6 +210,7 @@
 }
 
 void BufferingBytesConsumer::OnTimerFired(TimerBase*) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   MaybeStartBuffering();
 }
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h b/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h
index 612433ee..db43a83a 100644
--- a/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h
+++ b/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h
@@ -34,7 +34,10 @@
   // opportunity for the data to be drained before buffering begins. The
   // |bytes_consumer| is the original BytesConsumer. |bytes_consumer| must
   // not have a client.
-  static BufferingBytesConsumer* CreateWithDelay(
+  static BufferingBytesConsumer* CreateWithDelay(BytesConsumer* bytes_consumer);
+
+  // CreateWithDelay() that allows setting |timer_task_runner| for test.
+  static BufferingBytesConsumer* CreateWithDelayForTest(
       BytesConsumer* bytes_consumer,
       scoped_refptr<base::SingleThreadTaskRunner> timer_task_runner);
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer_test.cc b/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer_test.cc
index 75dafc3..86f273a7e 100644
--- a/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer_test.cc
@@ -82,7 +82,7 @@
   replaying_bytes_consumer->Add(Command(Command::kData, "8"));
   replaying_bytes_consumer->Add(Command(Command::kDone));
 
-  auto* bytes_consumer = BufferingBytesConsumer::CreateWithDelay(
+  auto* bytes_consumer = BufferingBytesConsumer::CreateWithDelayForTest(
       replaying_bytes_consumer,
       scheduler::GetSingleThreadTaskRunnerForTesting());
 
@@ -150,7 +150,7 @@
   replaying_bytes_consumer->Add(Command(Command::kData, "8"));
   replaying_bytes_consumer->Add(Command(Command::kDone));
 
-  auto* bytes_consumer = BufferingBytesConsumer::CreateWithDelay(
+  auto* bytes_consumer = BufferingBytesConsumer::CreateWithDelayForTest(
       replaying_bytes_consumer,
       scheduler::GetSingleThreadTaskRunnerForTesting());
 
@@ -239,7 +239,7 @@
       MakeGarbageCollected<DataPipeBytesConsumer>(task_runner, MakeDataPipe(),
                                                   &notifier);
 
-  auto* bytes_consumer = BufferingBytesConsumer::CreateWithDelay(
+  auto* bytes_consumer = BufferingBytesConsumer::CreateWithDelayForTest(
       data_pipe_consumer, scheduler::GetSingleThreadTaskRunnerForTesting());
 
   EXPECT_EQ(PublicState::kReadableOrWaiting, bytes_consumer->GetPublicState());
@@ -255,7 +255,7 @@
       MakeGarbageCollected<DataPipeBytesConsumer>(task_runner, MakeDataPipe(),
                                                   &notifier);
 
-  auto* bytes_consumer = BufferingBytesConsumer::CreateWithDelay(
+  auto* bytes_consumer = BufferingBytesConsumer::CreateWithDelayForTest(
       data_pipe_consumer, scheduler::GetSingleThreadTaskRunnerForTesting());
 
   task_environment_.FastForwardBy(base::Milliseconds(51));
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
index 70a6cec..1b3bee9 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
@@ -109,8 +109,9 @@
 
 class ResourceFetcherTest : public testing::Test {
  public:
-  ResourceFetcherTest() {
-    Resource::SetClockForTesting(platform_->test_task_runner()->GetMockClock());
+  ResourceFetcherTest()
+      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
+    Resource::SetClockForTesting(task_environment_.GetMockClock());
   }
   ~ResourceFetcherTest() override {
     MemoryCache::Get()->EvictResources();
@@ -204,10 +205,8 @@
         kTestResourceMimeType, platform_->GetURLLoaderMockFactory());
   }
 
-  ScopedTestingPlatformSupport<FetchTestingPlatformSupport> platform_;
-
- private:
   base::test::SingleThreadTaskEnvironment task_environment_;
+  ScopedTestingPlatformSupport<FetchTestingPlatformSupport> platform_;
 };
 
 TEST_F(ResourceFetcherTest, StartLoadAfterFrameDetach) {
@@ -1240,7 +1239,7 @@
 
   // Advance the clock, make sure the original resource gets removed from the
   // memory cache after the revalidation completes.
-  platform_->AdvanceClockSeconds(1);
+  task_environment_.AdvanceClock(base::Seconds(1));
   ResourceResponse revalidate_response(url);
   revalidate_response.SetHttpStatusCode(200);
   platform_->GetURLLoaderMockFactory()->UnregisterURL(url);
diff --git a/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h b/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h
index bc7fec35..2363b8c9 100644
--- a/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h
+++ b/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h
@@ -8,14 +8,13 @@
 #include <memory>
 
 #include "third_party/blink/renderer/platform/heap/persistent.h"
-#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 
 namespace blink {
 
 class URLLoaderMockFactory;
 
-class FetchTestingPlatformSupport
-    : public TestingPlatformSupportWithMockScheduler {
+class FetchTestingPlatformSupport : public TestingPlatformSupport {
  public:
   FetchTestingPlatformSupport();
   FetchTestingPlatformSupport(const FetchTestingPlatformSupport&) = delete;
diff --git a/third_party/blink/renderer/platform/media/cdm_session_adapter.cc b/third_party/blink/renderer/platform/media/cdm_session_adapter.cc
index 3e31dc0..ac94c2a9 100644
--- a/third_party/blink/renderer/platform/media/cdm_session_adapter.cc
+++ b/third_party/blink/renderer/platform/media/cdm_session_adapter.cc
@@ -8,7 +8,6 @@
 #include <utility>
 
 #include "base/containers/contains.h"
-#include "base/debug/dump_without_crashing.h"
 #include "base/functional/bind.h"
 #include "base/logging.h"
 #include "base/metrics/histogram.h"
@@ -185,17 +184,8 @@
   key_system_uma_prefix_ = std::move(key_system_uma_prefix);
 
   // Only report time for successful CDM creation.
-  auto cdm_creation_duration = base::TimeTicks::Now() - start_time;
   base::UmaHistogramTimes(key_system_uma_prefix_ + kTimeToCreateCdmUMAName,
-                          cdm_creation_duration);
-
-  if (cdm_creation_duration >= base::Seconds(5)) {
-    // Collect crash dumps to investigate why some systems are taking a long
-    // time to complete.
-    // TODO(crbug.com/1506221): remove this block after the investigation.
-    NO_CODE_FOLDING();
-    base::debug::DumpWithoutCrashing();
-  }
+                          base::TimeTicks::Now() - start_time);
 
   cdm_config_ = cdm_config;
 
diff --git a/third_party/blink/renderer/platform/peerconnection/webrtc_connection_matchers.h b/third_party/blink/renderer/platform/peerconnection/webrtc_connection_matchers.h
index a8ced2a..53f7408 100644
--- a/third_party/blink/renderer/platform/peerconnection/webrtc_connection_matchers.h
+++ b/third_party/blink/renderer/platform/peerconnection/webrtc_connection_matchers.h
@@ -139,7 +139,7 @@
 // cricket::Connection objects each, ignoring null cricket::Connections and
 // ordering.
 MATCHER_P(ConnectionSequenceEq,
-          /* std::vector<const blink::IceConnection> arg, */
+          /* std::vector<blink::IceConnection> arg, */
           /* std::vector<const cricket::Connection*> */ connections,
           "") {
   std::vector<const cricket::Connection*> non_null_connections;
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 8c940897..e2a8df6 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -2212,6 +2212,13 @@
       status: "experimental",
     },
     {
+      // This feature makes HTMLLabelElement::DefaultEventHandler always call
+      // HTMLElement::DefaultEventHandler instead of only doing so
+      // conditionally. Shipping in M123 so should be safe to remove in M127.
+      name: "LabelEventHandlerCallSuper",
+      status: "stable",
+    },
+    {
       name: "LangAttributeAwareFormControlUI",
       base_feature: "none",
       settable_from_internals: true,
@@ -2443,7 +2450,7 @@
     },
     {
       name: "MessagePortCloseEvent",
-      status: "stable",
+      status: "test",
     },
     // This is enabled by default on Windows only. The only part that's
     // "experimental" is the support on other platforms.
@@ -3028,7 +3035,7 @@
       // When enabled, gives the option to pretty-print JSON documents to
       // cleanly format the JSON and make it readable.
       name:"PrettyPrintJSONDocument",
-      status: "experimental",
+      status: "stable",
     },
     {
       // Privacy intervention to disable reading the system accent-color via
diff --git a/third_party/blink/renderer/platform/widget/input/scroll_predictor.cc b/third_party/blink/renderer/platform/widget/input/scroll_predictor.cc
index a64a575..55b6975 100644
--- a/third_party/blink/renderer/platform/widget/input/scroll_predictor.cc
+++ b/third_party/blink/renderer/platform/widget/input/scroll_predictor.cc
@@ -31,8 +31,8 @@
 
   if (filtering_enabled_) {
     // Get the filter from feature flags
-    std::string filter_name = GetFieldTrialParamValueByFeature(
-        blink::features::kFilteringScrollPrediction, "filter");
+    std::string filter_name =
+        blink::features::kFilteringScrollPredictionFilterParam.Get();
 
     input_prediction::FilterType filter_type =
         FilterFactory::GetFilterTypeFromName(filter_name);
diff --git a/third_party/blink/renderer/platform/widget/input/scroll_predictor_unittest.cc b/third_party/blink/renderer/platform/widget/input/scroll_predictor_unittest.cc
index 2f3f64ac..0e2879cd 100644
--- a/third_party/blink/renderer/platform/widget/input/scroll_predictor_unittest.cc
+++ b/third_party/blink/renderer/platform/widget/input/scroll_predictor_unittest.cc
@@ -195,10 +195,10 @@
   }
 
   void InitLinearResamplingTest(bool use_frames_based_experimental_prediction) {
-    base::FieldTrialParams params;
-    params["filter"] = ::features::kPredictorNameLinearResampling;
+    base::FieldTrialParams predictor_params;
+    predictor_params["predictor"] = ::features::kPredictorNameLinearResampling;
     base::test::FeatureRefAndParams prediction_params = {
-        features::kResamplingScrollEvents, params};
+        features::kResamplingScrollEvents, predictor_params};
 
     base::FieldTrialParams prediction_type_params;
     prediction_type_params["mode"] =
@@ -209,9 +209,16 @@
         ::features::kResamplingScrollEventsExperimentalPrediction,
         prediction_type_params};
 
+    base::FieldTrialParams filter_params;
+    filter_params["filter"] = "";
+    base::test::FeatureRefAndParams resampling_and_filter = {
+        features::kFilteringScrollPrediction, filter_params};
+
     scoped_feature_list_.Reset();
     scoped_feature_list_.InitWithFeaturesAndParameters(
-        {prediction_params, experimental_prediction_params}, {});
+        {prediction_params, experimental_prediction_params,
+         resampling_and_filter},
+        {});
     scroll_predictor_ = std::make_unique<ScrollPredictor>();
 
     VerifyPredictorType(::features::kPredictorNameLinearResampling);
@@ -255,6 +262,8 @@
 }
 
 TEST_F(ScrollPredictorTest, ResampleGestureScrollEvents) {
+  ConfigurePredictorFieldTrialAndInitialize(features::kResamplingScrollEvents,
+                                            ::features::kPredictorNameEmpty);
   SendGestureScrollBegin();
   EXPECT_FALSE(PredictionAvailable());
 
@@ -294,6 +303,8 @@
 }
 
 TEST_F(ScrollPredictorTest, ScrollInDifferentDirection) {
+  ConfigurePredictorFieldTrialAndInitialize(features::kResamplingScrollEvents,
+                                            ::features::kPredictorNameEmpty);
   SendGestureScrollBegin();
 
   // Scroll down.
@@ -366,6 +377,8 @@
 }
 
 TEST_F(ScrollPredictorTest, LSQPredictorTest) {
+  ConfigureFilterFieldTrialAndInitialize(features::kFilteringScrollPrediction,
+                                         "");
   SetUpLSQPredictor();
   SendGestureScrollBegin();
 
@@ -474,6 +487,8 @@
 }
 
 TEST_F(ScrollPredictorTest, ScrollPredictorNotChangeScrollDirection) {
+  ConfigureFilterFieldTrialAndInitialize(features::kFilteringScrollPrediction,
+                                         "");
   SetUpLSQPredictor();
   SendGestureScrollBegin();
 
diff --git a/third_party/blink/tools/blinkpy/common/net/git_cl_mock.py b/third_party/blink/tools/blinkpy/common/net/git_cl_mock.py
index 6ae8efbd..64fd6f8a 100644
--- a/third_party/blink/tools/blinkpy/common/net/git_cl_mock.py
+++ b/third_party/blink/tools/blinkpy/common/net/git_cl_mock.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from typing import Optional
+
 from blinkpy.common.net.git_cl import CLStatus, GitCL
 from blinkpy.common.net.rpc import BuildbucketClient
 from blinkpy.common.system.executive import ScriptError
@@ -67,7 +69,10 @@
             return None
         return 'closed'
 
-    def latest_try_jobs(self, builder_names=None, **_):
+    def latest_try_jobs(self,
+                        issue_number: Optional[str] = None,
+                        builder_names=None,
+                        **_):
         if builder_names:
             jobs = {
                 build: status
diff --git a/third_party/blink/tools/blinkpy/tool/commands/build_resolver.py b/third_party/blink/tools/blinkpy/tool/commands/build_resolver.py
index 0c7a1783..c1161ee 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/build_resolver.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/build_resolver.py
@@ -192,10 +192,12 @@
             UnresolvedBuildException: If the CL issue number is not set or no
                 try jobs are available but try jobs cannot be triggered.
         """
-        if not self._git_cl.get_issue_number().isdigit():
+        issue_number = self._git_cl.get_issue_number()
+        if not issue_number.isdigit():
             raise UnresolvedBuildException(
                 'No issue number for current branch.')
-        build_statuses = self._git_cl.latest_try_jobs(builder_names=builders,
+        build_statuses = self._git_cl.latest_try_jobs(issue_number,
+                                                      builder_names=builders,
                                                       patchset=patchset)
         if not build_statuses and not self._can_trigger_jobs:
             raise UnresolvedBuildException(
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_results_processor.py b/third_party/blink/tools/blinkpy/w3c/wpt_results_processor.py
index 6ce2f785..8e018db 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_results_processor.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_results_processor.py
@@ -10,8 +10,10 @@
 import logging
 import math
 import os
+import posixpath
 import queue
 import signal
+import textwrap
 import threading
 import time
 from typing import (
@@ -27,6 +29,7 @@
     Tuple,
     TypedDict,
 )
+from urllib.parse import quote_plus, urlsplit, urlunsplit
 
 import mozinfo
 
@@ -67,6 +70,10 @@
     ('SKIP', ResultType.Skip),
     ('NOTRUN', ResultType.Failure),
 ])
+_WPT_DOC_URL: str = ('https://chromium.googlesource.com/chromium/src/+/HEAD'
+                     '/docs/testing/run_web_platform_tests.md')
+_WPT_BASE_FYI_URL: str = urlsplit(
+    'https://wpt.fyi/results/?label=experimental&label=master&aligned')
 
 RunInfo = Dict[str, Any]
 TestType = Literal[tuple(wpttest.manifest_test_cls)]
@@ -76,6 +83,15 @@
     return _status_mapping[status]
 
 
+def wpt_fyi_url(test: str) -> Optional[str]:
+    prefix = 'external/wpt/'
+    if not test.startswith(prefix):
+        return None
+    scheme, netloc, path, query, fragment = _WPT_BASE_FYI_URL
+    path = posixpath.join(path, quote_plus(test[len(prefix):]))
+    return urlunsplit((scheme, netloc, path, query, fragment))
+
+
 @memoized
 def chromium_to_wptrunner_statuses(
     statuses: FrozenSet[str],
@@ -133,6 +149,8 @@
         self.test_type = test_type
         self.has_stderr = False
         self.image_diff_stats = None
+        # TODO(crbug.com/1521922): Populate `self.failure_reason` like
+        # `run_web_tests.py` does to help LUCI cluster failures.
 
     def _maybe_add_testharness_result(self,
                                       status: str,
@@ -183,6 +201,8 @@
         if priority >= self._result_priority(self.actual, self.unexpected):
             self.actual, self.expected = actual, expected
             self.unexpected = unexpected
+            self.is_regression = (self.actual != ResultType.Pass
+                                  and self.unexpected)
 
     def _result_priority(self, status: str,
                          unexpected: bool) -> Tuple[bool, bool, int]:
@@ -227,6 +247,31 @@
             TestharnessLine(LineType.FOOTER),
         ])
 
+    def summarize(self) -> Optional[str]:
+        """Generate a summary of this test result as sanitized HTML.
+
+        See [1] for ResultDB-specific markup features.
+
+        [1]: https://source.chromium.org/chromium/_/chromium/infra/luci/luci-go/+/1f5926756ec235cc63fbed7c7e603e86335998d3:resultdb/proto/v1/test_result.proto;l=99-112
+        """
+        if not self.is_regression:
+            return None
+        # TODO(crbug.com/1521922): Unify result sink reporting with
+        # `blinkpy.web_tests.*`.
+        summary = textwrap.dedent(f"""\
+            <p><strong>This WPT was run against <code>chrome</code> using
+            <code>chromedriver</code>. See <a href="{_WPT_DOC_URL}">these
+            instructions</a> about running these tests locally and triaging
+            failures.</strong></p>""").replace('\n', ' ')
+        url = wpt_fyi_url(self.name)
+        if url:
+            summary += f'<p><a href="{url}">Latest wpt.fyi results</a></p>'
+        for name in ['stderr', 'crash_log']:
+            if name in self.artifacts:
+                summary += f'<h3>{name}</h3>'
+                summary += f'<p><text-artifact artifact-id="{name}"/></p>'
+        return summary
+
 
 class Event(NamedTuple):
     action: str
@@ -541,7 +586,8 @@
             result=result,
             artifact_output_dir=self.fs.dirname(self.artifacts_dir),
             expectations=None,
-            test_file_location=result.file_path)
+            test_file_location=result.file_path,
+            html_summary=result.summarize())
         _log.debug(
             'Reported result for %s, iteration %d (actual: %s, '
             'expected: %s, artifacts: %s)', result.name, self._iteration,
@@ -586,7 +632,6 @@
             expected = ' '.join(results[0].expected)
             actual = [result.actual for result in results]
             is_pass = results[-1].actual == ResultType.Pass
-            is_unexpected = results[-1].unexpected
             if is_pass:
                 num_passes += 1
 
@@ -606,12 +651,11 @@
 
             if self.port.is_slow_wpt_test(test_name):
                 test_dict['is_slow_test'] = True
-
-            if is_unexpected:
+            if results[-1].unexpected:
                 test_dict['is_unexpected'] = True
-                if not is_pass:
-                    test_dict['is_regression'] = True
-                    num_regressions += 1
+            if results[-1].is_regression:
+                test_dict['is_regression'] = True
+                num_regressions += 1
 
             if results[0].image_diff_stats:
                 test_dict['image_diff_stats'] = results[0].image_diff_stats
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_results_processor_unittest.py b/third_party/blink/tools/blinkpy/w3c/wpt_results_processor_unittest.py
index af288b87..f2e53e8 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_results_processor_unittest.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_results_processor_unittest.py
@@ -151,7 +151,8 @@
                                              'Default'),
             expectations=None,
             test_file_location=self.path_finder.path_from_web_tests(
-                'external', 'wpt', 'reftest.html'))
+                'external', 'wpt', 'reftest.html'),
+            html_summary=None)
 
         result = report_mock.call_args.kwargs['result']
         self.assertEqual(result.name, 'external/wpt/reftest.html')
@@ -181,7 +182,8 @@
                                              'Default'),
             expectations=None,
             test_file_location=self.path_finder.path_from_web_tests(
-                'wpt_internal', 'reftest.html'))
+                'wpt_internal', 'reftest.html'),
+            html_summary=mock.ANY)
 
         result = report_mock.call_args.kwargs['result']
         self.assertEqual(result.name, 'wpt_internal/reftest.html')
@@ -191,6 +193,14 @@
         self.assertAlmostEqual(result.took, 0.5)
         # `{expected,actual}_text` is not produced for WPT reftests.
         self.assertEqual(result.artifacts, {})
+        summary = report_mock.call_args.kwargs['html_summary']
+        self.assertRegex(
+            summary,
+            'This WPT was run against .*chrome.* using .*chromedriver')
+        self.assertRegex(
+            summary, 'See .*these instructions.* about running '
+            'these tests locally and triaging failures')
+        self.assertNotIn('wpt.fyi', summary)
 
     def test_report_pass_on_retry(self):
         self._event(action='suite_start', time=0)
@@ -228,7 +238,8 @@
                                                        'Default'),
                       expectations=None,
                       test_file_location=self.path_finder.path_from_web_tests(
-                          'external', 'wpt', 'variant.html')),
+                          'external', 'wpt', 'variant.html'),
+                      html_summary=mock.ANY),
         ] * 2)
 
         fail, ok = [
@@ -256,6 +267,10 @@
                                  'wpt', 'variant_foo=bar_abc-stderr.txt'),
                 ],
             })
+        fail_summary = report_mock.call_args_list[0].kwargs['html_summary']
+        self.assertRegex(
+            fail_summary,
+            'https://wpt.fyi/results/variant.html%3Ffoo%3Dbar%2Fabc')
 
     def test_report_subtest_fail_all_expected(self):
         """Subtest failures should be promoted to the test level.
diff --git a/third_party/blink/tools/blinkpy/web_tests/builder_list.py b/third_party/blink/tools/blinkpy/web_tests/builder_list.py
index 1b05b29..6b850ec5 100644
--- a/third_party/blink/tools/blinkpy/web_tests/builder_list.py
+++ b/third_party/blink/tools/blinkpy/web_tests/builder_list.py
@@ -279,6 +279,13 @@
         the version specifier for the first builder that matches, even
         if it's a try bot builder.
         """
+        # TODO(crbug.com/41484800): Remove this special logic by either:
+        #  1. Implement better way of defining port as per-suite, not
+        #     per-builder, property in `BuilderList`
+        #  2. Replace the `chrome` port with regular platform ports with
+        #     chrome-specific logic (detected via the `driver_name` option).
+        if target_port_name == 'chrome':
+            return 'Chrome'
         for _, builder_info in sorted(self._builders.items()):
             if builder_info['port_name'] == target_port_name:
                 return builder_info['specifiers'][0]
diff --git a/third_party/blink/tools/blinkpy/web_tests/builder_list_unittest.py b/third_party/blink/tools/blinkpy/web_tests/builder_list_unittest.py
index 5adcdfa..0608f4e9 100644
--- a/third_party/blink/tools/blinkpy/web_tests/builder_list_unittest.py
+++ b/third_party/blink/tools/blinkpy/web_tests/builder_list_unittest.py
@@ -297,6 +297,22 @@
                          builders.version_specifier_for_port_name('port-b'))
         self.assertIsNone(builders.version_specifier_for_port_name('port-x'))
 
+    def test_version_specifier_for_chrome_port_name(self):
+        builders = BuilderList({
+            'linux-blink-rel': {
+                'port_name': 'linux',
+                'specifiers': ['Linux', 'Release'],
+                'steps': {
+                    'webdriver_wpt_tests (with patch)': {
+                        'product': 'chrome',
+                    },
+                },
+                'is_try_builder': True,
+            }
+        })
+        self.assertEqual('Chrome',
+                         builders.version_specifier_for_port_name('chrome'))
+
     def test_product_for_build_step(self):
         builders = self.sample_builder_list()
         self.assertEqual(
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/factory.py b/third_party/blink/tools/blinkpy/web_tests/port/factory.py
index be8e951..df0e2180 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/factory.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/factory.py
@@ -223,13 +223,12 @@
     group.add_argument('--chrome-branded',
                        action='store_true',
                        help='Set the configuration as chrome_branded.')
+    group.add_argument('--no-xvfb',
+                       action='store_false',
+                       dest='use_xvfb',
+                       help='Do not run tests with Xvfb')
     add_common_wpt_options(group)
-    if rwt:
-        group.add_argument('--no-xvfb',
-                           action='store_false',
-                           dest='use_xvfb',
-                           help='Do not run tests with Xvfb')
-    else:
+    if not rwt:
         group.add_argument(
             '-p',
             '--product',
@@ -237,12 +236,10 @@
             choices=(product_choices or []),
             metavar='PRODUCT',
             help='Product (browser or browser component) to test.')
-        group.add_argument(
-            '--no-headless',
-            action='store_false',
-            dest='headless',
-            help=('Do not run the browser headlessly; pause after each test '
-                  'until the window is closed. On Linux, do not start Xvfb.'))
+        group.add_argument('--no-headless',
+                           action='store_false',
+                           dest='headless',
+                           help=('Do not run browser in headless mode.'))
         group.add_argument('--webdriver-binary',
                            metavar='PATH',
                            type=str,
diff --git a/third_party/blink/tools/blinkpy/wpt_tests/product.py b/third_party/blink/tools/blinkpy/wpt_tests/product.py
index ba79ce5..a3502167 100644
--- a/third_party/blink/tools/blinkpy/wpt_tests/product.py
+++ b/third_party/blink/tools/blinkpy/wpt_tests/product.py
@@ -69,9 +69,9 @@
         """Product-specific wptrunner parameters needed to run tests."""
         processes = self._options.child_processes
         if not processes:
-            if self._options.wrapper or not self._options.headless:
+            if self._options.wrapper:
                 _log.info('Defaulting to 1 worker because of debugging '
-                          'options (`--wrapper` or `--no-headless`)')
+                          'option `--wrapper`')
                 processes = 1
             else:
                 processes = self._port.default_child_processes()
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 e07c2f4..fab7175 100644
--- a/third_party/blink/tools/blinkpy/wpt_tests/wpt_adapter.py
+++ b/third_party/blink/tools/blinkpy/wpt_tests/wpt_adapter.py
@@ -237,7 +237,8 @@
         runner_options.no_capture_stdio = True
         runner_options.manifest_download = False
         runner_options.manifest_update = False
-        runner_options.headless = True
+        runner_options.pause_after_test = False
+        runner_options.headless = self.options.headless
 
         # Set up logging as early as possible.
         self._set_up_runner_output_options(runner_options)
@@ -376,15 +377,6 @@
                                                      '127.0.0.1.pem')
 
     def _set_up_runner_debugging_options(self, runner_options):
-        self.port.set_option_default('use_xvfb',
-                                     self.port.get_option('headless'))
-        if not self.options.headless:
-            logger.info('Not headless; default to 1 worker to avoid '
-                        'opening too many windows')
-            runner_options.headless = False
-            # Force `--pause-after-test`, since it doesn't make sense to run
-            # tests headfully without giving a chance for interaction.
-            runner_options.pause_after_test = True
         if self.options.wrapper:
             runner_options.debugger = self.options.wrapper[0]
             # `wpt run` expects a plain `str`, not a `List[str]`:
@@ -703,10 +695,6 @@
     # `--no-expectations` to `run_wpt_tests.py`, and skip reporting results when
     # the flag is passed.
     options.no_expectations = False
-    # Directly tie Xvfb usage to headless mode. Xvfb can supercede a real X
-    # server and therefore should never be started in `--no-headless` mode.
-    # Conversely, the default headless mode should always start Xvfb.
-    options.use_xvfb = options.headless
     return options, args
 
 
diff --git a/third_party/blink/web_tests/LeakExpectations b/third_party/blink/web_tests/LeakExpectations
index 7c54cff..752ee24 100644
--- a/third_party/blink/web_tests/LeakExpectations
+++ b/third_party/blink/web_tests/LeakExpectations
@@ -34,13 +34,6 @@
 crbug.com/1219915 [ Linux ] editing/selection/context-menu-text-selection.html [ Failure ]
 crbug.com/1219915 [ Linux ] editing/selection/readonly-disabled-hittest.html [ Failure ]
 crbug.com/1219915 [ Linux ] editing/selection/readonly-disabled-text-selection.html [ Failure ]
-crbug.com/1219915 [ Linux ] fast/events/touch/gesture/gesture-tap-reset-selection-range.html [ Failure ]
-crbug.com/1219915 [ Linux ] fast/events/touch/gesture/long-press-drag-drop-touch-editing-combined-in-iframe.html [ Failure ]
-crbug.com/1219915 [ Linux ] fast/events/touch/gesture/long-press-drag-drop-touch-editing-combined.html [ Failure ]
-crbug.com/1219915 [ Linux ] fast/events/touch/gesture/long-press-focuses-frame.html [ Failure ]
-crbug.com/1219915 [ Linux ] fast/events/touch/gesture/long-press-on-draggable-element-in-iframe-triggers-drag.html [ Failure ]
-crbug.com/1219915 [ Linux ] fast/events/touch/gesture/long-press-on-draggable-element-in-nested-iframes-triggers-drag.html [ Failure ]
-crbug.com/1219915 [ Linux ] fast/events/touch/gesture/long-press-selects-word-when-touch-editing-enabled.html [ Failure ]
 crbug.com/1219915 [ Linux ] touchadjustment/focusout-on-touch.html [ Failure ]
 crbug.com/1219915 [ Linux ] touchadjustment/touch-links-longpress.html [ Failure ]
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index a838cc2..430ebc6 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -5435,6 +5435,7 @@
 
 crbug.com/1455262 virtual/view-transition/external/wpt/soft-navigation-heuristics/navigation-api-view-transition.tentative.html [ Crash Failure Pass Skip ]
 crbug.com/1521474 virtual/disable-intersection-optimization/external/wpt/intersection-observer/callback-cross-realm-report-exception.html [ Failure ]
+crbug.com/324293120 [ Debug Linux ] external/wpt/webstorage/event_case_sensitive.html [ Failure Pass ]
 
 # Suppress http/tests/inspector-protocol/network/navigate-iframe-in2out.js test cluster
 crbug.com/1413112 [ Mac10.15 ] virtual/reduce-accept-language/http/tests/inspector-protocol/network/navigate-iframe-in2out.js [ Failure ]
@@ -6449,6 +6450,14 @@
 crbug.com/1464614 [ Win11-arm64 ] custom-elements/form-validation-bubble-appearance.html [ Failure ]
 crbug.com/1464614 [ Win11-arm64 ] fast/forms/file/file-appearance-bidi-filenames.html [ Failure ]
 crbug.com/1464614 [ Win11-arm64 ] fast/ruby/nested-ruby.html [ Failure ]
+crbug.com/1464614 [ Win11-arm64 ] external/wpt/credential-management/fedcm-disconnect-errors.https.html [ Pass Timeout Failure ] # Flaky
+crbug.com/1464614 [ Win11-arm64 ] virtual/fedcm-login-status/external/wpt/credential-management/fedcm-login-status/cross-origin-status.https.html [ Pass Timeout Failure ] # Flaky
+crbug.com/1464614 [ Win11-arm64 ] virtual/fenced-frame-mparch/external/wpt/fenced-frame/deep-copy-config.https.html [ Pass Timeout Failure ] # Flaky
+crbug.com/1464614 [ Win11-arm64 ] virtual/no-auto-wpt-origin-isolation/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain2.sub.https.html [ Pass Timeout Failure ] # Flaky
+crbug.com/1464614 [ Win11-arm64 ] virtual/pna-permission/external/wpt/fetch/private-network-access/redirect.tentative.https.window.html [ Pass Timeout Failure ] # Flaky
+crbug.com/1464614 [ Win11-arm64 ] virtual/pna-workers-enabled/external/wpt/fetch/private-network-access/redirect.tentative.https.window.html [ Pass Timeout Failure ] # Flaky
+crbug.com/1464614 [ Win11-arm64 ] virtual/shared-storage-fenced-frame-mparch/external/wpt/shared-storage/shared-storage-writable-permissions-policy-default.tentative.https.sub.html [ Pass Timeout Failure ] # Flaky
+crbug.com/1464614 [ Win11-arm64 ] virtual/threaded/external/wpt/css/css-animations/animationevent-types.html [ Pass Timeout Failure ] # Flaky
 
 crbug.com/1411760 [ Debug Mac13-arm64 ] virtual/composite-clip-path-animation/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-fixed-position.html [ Failure Pass ]
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-027-print-ref.html b/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-027-print-ref.html
new file mode 100644
index 0000000..560f137b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-027-print-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<style>
+  body {
+    color: white;
+    margin: 0;
+  }
+</style>
+<div style="height:400vh; background:green;">
+  There should be four green pages.
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-027-print.html b/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-027-print.html
new file mode 100644
index 0000000..c7cfd37
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-027-print.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://issues.chromium.org/41496759">
+<link rel="match" href="monolithic-overflow-027-print-ref.html">
+<style>
+  body {
+    color: white;
+    margin: 0;
+  }
+</style>
+<div style="position:absolute; width:100%; contain:size; height:4in; background:red;">
+  <div style="height:8in; background:green;">
+    There should be four green pages.
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-028-print-ref.html b/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-028-print-ref.html
new file mode 100644
index 0000000..560f137b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-028-print-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<style>
+  body {
+    color: white;
+    margin: 0;
+  }
+</style>
+<div style="height:400vh; background:green;">
+  There should be four green pages.
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-028-print.html b/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-028-print.html
new file mode 100644
index 0000000..c549db6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-028-print.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://issues.chromium.org/41496759">
+<link rel="match" href="monolithic-overflow-028-print-ref.html">
+<style>
+  body {
+    color: white;
+    margin: 0;
+  }
+</style>
+<div style="position:absolute; overflow-y:clip; width:100%; contain:size; height:8in; background:red;">
+  <div style="height:23in; background:green;">
+    There should be four green pages.
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-029-print-ref.html b/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-029-print-ref.html
new file mode 100644
index 0000000..560f137b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-029-print-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<style>
+  body {
+    color: white;
+    margin: 0;
+  }
+</style>
+<div style="height:400vh; background:green;">
+  There should be four green pages.
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-029-print.html b/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-029-print.html
new file mode 100644
index 0000000..c869d87d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-029-print.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://issues.chromium.org/41496759">
+<link rel="match" href="monolithic-overflow-029-print-ref.html">
+<style>
+  body {
+    color: white;
+    margin: 0;
+  }
+</style>
+<div style="position:absolute; width:100%; contain:size; height:4in; background:green;">
+  There should be four green pages.
+</div>
+<div style="position:absolute; width:100%; top:2in; contain:size; height:2in; background:red;">
+  <div style="height:6in; background:green;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-030-print-ref.html b/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-030-print-ref.html
new file mode 100644
index 0000000..d8468e3b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-030-print-ref.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<style>
+  body {
+    margin: 0;
+  }
+</style>
+<div style="break-after:page;">
+  <div style="display:flow-root; height:1.5in;">
+    <p>
+      There should be four pages, and each one should have a green square in the
+      bottom right corner.
+    </p>
+  </div>
+  <div style="margin-left:auto; width:0.5in; height:0.5in; background:green;"></div>
+</div>
+<div style="break-after:page;">
+  <div style="height:1.5in;"></div>
+  <div style="margin-left:auto; width:0.5in; height:0.5in; background:green;"></div>
+</div>
+<div style="break-after:page;">
+  <div style="height:1.5in;"></div>
+  <div style="margin-left:auto; width:0.5in; height:0.5in; background:green;"></div>
+</div>
+<div style="break-after:page;">
+  <div style="height:1.5in;"></div>
+  <div style="margin-left:auto; width:0.5in; height:0.5in; background:green;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-030-print.html b/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-030-print.html
new file mode 100644
index 0000000..e36f2543
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-page/monolithic-overflow-030-print.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://issues.chromium.org/41496759">
+<link rel="match" href="monolithic-overflow-030-print-ref.html">
+<style>
+  body {
+    margin: 0;
+  }
+</style>
+<p>
+  There should be four pages, and each one should have a green square in the
+  bottom right corner.
+</p>
+<div style="position:absolute; top:0; width:100%; contain:size; height:8in;"></div>
+<div style="position:fixed; right:0; bottom:0; width:0.5in; height:0.5in; background:green;"></div>
diff --git a/third_party/blink/web_tests/external/wpt/editing/edit-context/edit-context-input.tentative.html b/third_party/blink/web_tests/external/wpt/editing/edit-context/edit-context-input.tentative.html
index 3a65ab99..762ec59 100644
--- a/third_party/blink/web_tests/external/wpt/editing/edit-context/edit-context-input.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/editing/edit-context/edit-context-input.tentative.html
@@ -13,56 +13,69 @@
       const kBackspaceKey = "\uE003";
       const kDeleteKey = "\uE017";
 
-    promise_test(async function() {
+    async function testBasicTestInput(element) {
       const editContext = new EditContext();
-      const div = document.createElement("div");
-      document.body.appendChild(div);
+      let textForView = "";
+      document.body.appendChild(element);
       let beforeInputType = null;
       let beforeInputTargetRanges = null;
-      div.addEventListener("beforeinput", e => {
+      element.addEventListener("beforeinput", e => {
         beforeInputType = e.inputType;
         beforeInputTargetRanges = e.getTargetRanges().map(
           staticRange => [staticRange.startOffset, staticRange.endOffset]);
       });
       editContext.addEventListener("textupdate", e => {
-        div.innerHTML = `${div.innerHTML.substring(0, e.updateRangeStart)}${e.text}${div.innerHTML.substring(e.updateRangeEnd)}`;
+        textForView = `${textForView.substring(0, e.updateRangeStart)}${e.text}${textForView.substring(e.updateRangeEnd)}`;
       });
-      div.editContext = editContext;
-      div.focus();
-      await test_driver.send_keys(div, 'a');
+      element.editContext = editContext;
+      element.focus();
+      await test_driver.send_keys(element, 'a');
       assert_equals(editContext.text, "a");
-      assert_equals(div.innerHTML, "a");
+      assert_equals(textForView, "a");
       assert_equals(beforeInputType, "insertText");
-      assert_equals(beforeInputTargetRanges.length, 1);
-      assert_array_equals(beforeInputTargetRanges[0], [0, 0]);
-      div.remove();
-    }, 'Testing EditContext English typing');
+      if (element instanceof HTMLCanvasElement) {
+        // DOM selection doesn't work inside <canvas>, so events
+        // in <canvas> can't have target ranges.
+        assert_equals(beforeInputTargetRanges.length, 0);
+      } else {
+        assert_equals(beforeInputTargetRanges.length, 1);
+        assert_array_equals(beforeInputTargetRanges[0], [0, 0]);
+      }
 
-    promise_test(async function() {
+      element.remove();
+    }
+
+    promise_test(testBasicTestInput.bind(null, document.createElement("div")), "Basic text input with div");
+    promise_test(testBasicTestInput.bind(null, document.createElement("canvas")), "Basic text input with canvas");
+
+    async function testBasicTestInputWithExistingSelection(element) {
       const editContext = new EditContext();
-      const div = document.createElement("div");
-      document.body.appendChild(div);
+      let textForView = "";
+      document.body.appendChild(element);
       editContext.addEventListener("textupdate", e => {
-        div.innerHTML = `${div.innerHTML.substring(0, e.updateRangeStart)}${e.text}${div.innerHTML.substring(e.updateRangeEnd)}`;
+        textForView = `${textForView.substring(0, e.updateRangeStart)}${e.text}${textForView.substring(e.updateRangeEnd)}`;
       });
-      div.editContext = editContext;
-      div.focus();
+      element.editContext = editContext;
+      element.focus();
 
       editContext.updateText(0, 0, "abcd");
-      div.innerText = "abcd";
+      textForView = "abcd";
       assert_equals(editContext.text, "abcd");
       editContext.updateSelection(2, 3);
-      await test_driver.send_keys(div, 'Z');
+      await test_driver.send_keys(element, 'Z');
       assert_equals(editContext.text, "abZd");
-      assert_equals(div.innerHTML, "abZd");
+      assert_equals(textForView, "abZd");
 
       editContext.updateSelection(2, 1);
-      await test_driver.send_keys(div, 'Y');
+      await test_driver.send_keys(element, 'Y');
       assert_equals(editContext.text, "aYZd");
-      assert_equals(div.innerHTML, "aYZd");
+      assert_equals(textForView, "aYZd");
 
-      div.remove();
-    }, 'Text insertion with non-collapsed selection');
+      element.remove();
+    }
+
+    promise_test(testBasicTestInputWithExistingSelection.bind(null, document.createElement("div")), "Text insertion with non-collapsed selection with div");
+    promise_test(testBasicTestInputWithExistingSelection.bind(null, document.createElement("canvas")), "Text insertion with non-collapsed selection with canvas");
 
     promise_test(async function() {
       const editContext = new EditContext();
@@ -118,14 +131,13 @@
     div.remove();
   }, "EditContext should not receive events after being detached from element");
 
-  promise_test(async function() {
+  async function testBackspaceAndDelete(element) {
       const editContext = new EditContext();
-      const div = document.createElement("div");
-      div.innerText = "hello there";
-      document.body.appendChild(div);
+      let textForView = "hello there";
+      document.body.appendChild(element);
       let beforeInputType = null;
       let beforeInputTargetRanges = null;
-      div.addEventListener("beforeinput", e => {
+      element.addEventListener("beforeinput", e => {
         beforeInputType = e.inputType;
         beforeInputTargetRanges = e.getTargetRanges().map(
           staticRange => [staticRange.startOffset, staticRange.endOffset]);
@@ -133,35 +145,37 @@
       let textUpdateSelection = null;
       editContext.addEventListener("textupdate", e => {
         textUpdateSelection = [e.selectionStart, e.selectionEnd];
-        div.innerText = `${div.innerText.substring(0, e.updateRangeStart)}${e.text}${div.innerText.substring(e.updateRangeEnd)}`;
+        textForView = `${textForView.substring(0, e.updateRangeStart)}${e.text}${textForView.substring(e.updateRangeEnd)}`;
       });
-      div.editContext = editContext;
+      element.editContext = editContext;
       editContext.updateText(0, 11, "hello there");
       editContext.updateSelection(10, 10);
       const selection = window.getSelection();
-      selection.setBaseAndExtent(div.firstChild, 10, div.firstChild, 10);
 
-      await test_driver.send_keys(div, kBackspaceKey);
-      assert_equals(div.innerHTML, "hello thee");
+      await test_driver.send_keys(element, kBackspaceKey);
+      assert_equals(textForView, "hello thee");
       assert_array_equals(textUpdateSelection, [9, 9]);
       assert_equals(beforeInputType, "deleteContentBackward");
       assert_equals(beforeInputTargetRanges.length, 0, "Backspace should not have a target range in EditContext");
 
-      await test_driver.send_keys(div, kDeleteKey);
-      assert_equals(div.innerHTML, "hello the");
+      await test_driver.send_keys(element, kDeleteKey);
+      assert_equals(textForView, "hello the");
       assert_array_equals(textUpdateSelection, [9, 9]);
       assert_equals(beforeInputType, "deleteContentForward");
       assert_equals(beforeInputTargetRanges.length, 0, "Delete should not have a target range in EditContext");
-      div.remove();
-    }, "Backspace and delete in EditContext");
+      element.remove();
+    }
 
-    promise_test(async function() {
+    promise_test(testBackspaceAndDelete.bind(null, document.createElement("div")), "Backspace and delete in EditContext with div");
+    promise_test(testBackspaceAndDelete.bind(null, document.createElement("canvas")) , "Backspace and delete in EditContext with canvas");
+
+    async function testBackspaceAndDeleteWithExistingSelection(element) {
       const editContext = new EditContext();
-      const div = document.createElement("div");
-      document.body.appendChild(div);
+      let textForView = "hello there";
+      document.body.appendChild(element);
       let beforeInputType = null;
       let beforeInputTargetRanges = null;
-      div.addEventListener("beforeinput", e => {
+      element.addEventListener("beforeinput", e => {
         beforeInputType = e.inputType;
         beforeInputTargetRanges = e.getTargetRanges().map(
           staticRange => [staticRange.startOffset, staticRange.endOffset]);
@@ -169,48 +183,51 @@
       let textUpdateSelection = null;
       editContext.addEventListener("textupdate", e => {
         textUpdateSelection = [e.selectionStart, e.selectionEnd];
-        div.innerText = `${div.innerText.substring(0, e.updateRangeStart)}${e.text}${div.innerText.substring(e.updateRangeEnd)}`;
+        textForView = `${textForView.substring(0, e.updateRangeStart)}${e.text}${textForView.substring(e.updateRangeEnd)}`;
       });
-      div.editContext = editContext;
+      element.editContext = editContext;
       const initialText = "abcdefghijklmnopqrstuvwxyz";
       editContext.updateText(0, initialText.length, initialText);
-      div.innerText = initialText;
-      div.focus();
+      textForView = initialText;
+      element.focus();
 
       editContext.updateSelection(3, 6);
-      await test_driver.send_keys(div, kBackspaceKey);
+      await test_driver.send_keys(element, kBackspaceKey);
       assert_equals(editContext.text, "abcghijklmnopqrstuvwxyz");
-      assert_equals(div.innerHTML, "abcghijklmnopqrstuvwxyz");
+      assert_equals(textForView, "abcghijklmnopqrstuvwxyz");
       assert_array_equals(textUpdateSelection, [3, 3]);
       assert_equals(beforeInputType, "deleteContentBackward");
       assert_equals(beforeInputTargetRanges.length, 0, "Backspace should not have a target range in EditContext");
 
       editContext.updateSelection(3, 6);
-      await test_driver.send_keys(div, kDeleteKey);
+      await test_driver.send_keys(element, kDeleteKey);
       assert_equals(editContext.text, "abcjklmnopqrstuvwxyz");
-      assert_equals(div.innerHTML, "abcjklmnopqrstuvwxyz");
+      assert_equals(textForView, "abcjklmnopqrstuvwxyz");
       assert_array_equals(textUpdateSelection, [3, 3]);
       assert_equals(beforeInputType, "deleteContentForward");
       assert_equals(beforeInputTargetRanges.length, 0, "Delete should not have a target range in EditContext");
 
       editContext.updateSelection(6, 3);
-      await test_driver.send_keys(div, kBackspaceKey);
+      await test_driver.send_keys(element, kBackspaceKey);
       assert_equals(editContext.text, "abcmnopqrstuvwxyz");
-      assert_equals(div.innerHTML, "abcmnopqrstuvwxyz");
+      assert_equals(textForView, "abcmnopqrstuvwxyz");
       assert_array_equals(textUpdateSelection, [3, 3]);
       assert_equals(beforeInputType, "deleteContentBackward");
       assert_equals(beforeInputTargetRanges.length, 0, "Backspace should not have a target range in EditContext");
 
       editContext.updateSelection(6, 3);
-      await test_driver.send_keys(div, kDeleteKey);
+      await test_driver.send_keys(element, kDeleteKey);
       assert_equals(editContext.text, "abcpqrstuvwxyz");
-      assert_equals(div.innerHTML, "abcpqrstuvwxyz");
+      assert_equals(textForView, "abcpqrstuvwxyz");
       assert_array_equals(textUpdateSelection, [3, 3]);
       assert_equals(beforeInputType, "deleteContentForward");
       assert_equals(beforeInputTargetRanges.length, 0, "Delete should not have a target range in EditContext");
 
-      div.remove();
-    }, "Backspace and delete with existing selection");
+      element.remove();
+    }
+
+    promise_test(testBackspaceAndDeleteWithExistingSelection.bind(null, document.createElement("div")), "Backspace and delete with existing selection with div");
+    promise_test(testBackspaceAndDeleteWithExistingSelection.bind(null, document.createElement("canvas")) , "Backspace and delete with existing selection with canvas");
 
     promise_test(async function() {
       const iframe = document.createElement("iframe");
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/label-in-invoker.html b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/label-in-invoker.html
new file mode 100644
index 0000000..bf8ab97
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/label-in-invoker.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1523168">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<button popovertarget=mypopover>
+  <label>label</label>
+</button>
+<div id=mypopover popover=auto>popover</div>
+
+<script>
+promise_test(async() => {
+  const label = document.querySelector('label');
+  assert_false(mypopover.matches(':popover-open'),
+    'Popover should be closed at the start of the test.');
+  await test_driver.click(label);
+  assert_true(mypopover.matches(':popover-open'),
+    'The popover should be opened by clicking on the label.');
+}, 'Buttons with popovertarget should invoke targets even if there is a label in the button.');
+</script>
diff --git a/third_party/blink/web_tests/fast/events/touch/gesture/gesture-tap-reset-selection-range.html b/third_party/blink/web_tests/fast/events/touch/gesture/gesture-tap-reset-selection-range.html
index dfee5fe..f5e4669 100644
--- a/third_party/blink/web_tests/fast/events/touch/gesture/gesture-tap-reset-selection-range.html
+++ b/third_party/blink/web_tests/fast/events/touch/gesture/gesture-tap-reset-selection-range.html
@@ -1,24 +1,20 @@
 <!DOCTYPE html>
 <script src="../../../../resources/testharness.js"></script>
 <script src="../../../../resources/testharnessreport.js"></script>
-<div id="dragme">Make Selection Here</div>
+<script src="../../../../resources/gesture-util.js"></script>
+<script src="../../../../resources/testdriver.js"></script>
+<script src="../../../../resources/testdriver-actions.js"></script>
+<script src="../../../..//resources/testdriver-vendor.js"></script>
+<div><span id="dragme">Make Selection Here<span></div>
 <div id="log"></div>
 <script>
-var dragme = document.getElementById('dragme');
-test(function() {
-    if (!window.eventSender)
-        return;
-
-    var x = dragme.offsetLeft + dragme.offsetWidth / 2;
-    var y = dragme.offsetTop + dragme.offsetHeight / 2;
-
-    // Longpress to create selection
-    eventSender.gestureLongPress(x, y);
-
-    eventSender.gestureTapDown(x, y);
-    eventSender.gestureShowPress(x, y);
-    eventSender.gestureTap(x, y);
-
-    assert_true(window.getSelection().isCollapsed);
-}, "This tests if the selection is reset after tapping of gesture.");
+promise_test(async t => {
+  const target = document.getElementById('dragme');
+  preventContextMenu(t);
+  await touchLongPressElement(target, { duration: 1200 });
+  assert_equals(window.getSelection().toString(), 'Selection');
+  const pos = elementCenter(target);
+  await touchTap(pos.x, pos.y);
+  assert_true(window.getSelection().isCollapsed);
+}, 'Selection is reset after tap gesture.');
 </script>
diff --git a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-drag-drop-touch-editing-combined-expected.txt b/third_party/blink/web_tests/fast/events/touch/gesture/long-press-drag-drop-touch-editing-combined-expected.txt
deleted file mode 100644
index 5c4d323..0000000
--- a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-drag-drop-touch-editing-combined-expected.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-
-This test checks long press behavior when both touch drag/drop and touch editing are enabled.
-1. Check that long press on a draggable element starts drag.
-2. Check that long press on non-draggable text selects the text.
-3. Check that long press on selected text starts drag.
-
-Testing
-PASS
-PASS
-PASS
-Drag me
-Some text
diff --git a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-drag-drop-touch-editing-combined-in-iframe-expected.txt b/third_party/blink/web_tests/fast/events/touch/gesture/long-press-drag-drop-touch-editing-combined-in-iframe-expected.txt
deleted file mode 100644
index 77563f6..0000000
--- a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-drag-drop-touch-editing-combined-in-iframe-expected.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-
-This test checks long press behavior inside an iframe when both touch drag/drop and touch editing are enabled.
-1. Check that long press on a draggable element starts drag.
-2. Check that long press on non-draggable text selects the text.
-3. Check that long press on selected text starts drag.
-
-Testing
-PASS
-FAIL
diff --git a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-drag-drop-touch-editing-combined-in-iframe.html b/third_party/blink/web_tests/fast/events/touch/gesture/long-press-drag-drop-touch-editing-combined-in-iframe.html
index d70d759..02d37a8e7 100644
--- a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-drag-drop-touch-editing-combined-in-iframe.html
+++ b/third_party/blink/web_tests/fast/events/touch/gesture/long-press-drag-drop-touch-editing-combined-in-iframe.html
@@ -1,42 +1,73 @@
 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
 <html>
 <head>
-<script src="../../../../resources/js-test.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
+<script src="../../../../resources/testdriver.js"></script>
+<script src="../../../../resources/testdriver-actions.js"></script>
+<script src="../../../..//resources/testdriver-vendor.js"></script>
+<style>
+  iframe {
+    height: 200px;
+    width: 200px;
+    overflow: hidden;
+  }
+</style>
 </head>
 <body>
+<script>
+  if (window.internals)
+    internals.settings.setTouchDragDropEnabled(true);
+
+// The iframe will call top.dragStarted() or top.mousedownTriggered() on
+// receiving the corresponding event.
+function mousedownTriggered() {
+  rejectDragStartPromise();
+}
+
+function dragStarted(element) {
+  resolveDragStartPromise(element);
+}
+
+let resolveDragStartPromise = undefined;
+let rejectDragStartPromise = undefined;
+function dragStartEvent() {
+  return new Promise((resolve, reject) => {
+    resolveDragStartPromise = resolve;
+    rejectDragStartPromise = () => {
+      reject('missing dragstart event');
+    }
+  });
+}
+</script>
 <iframe src="resources/drag-inside-iframe2.html" id=testIframe></iframe>
 <p>This test checks long press behavior inside an iframe when both touch drag/drop and touch editing are enabled.<br>
 1. Check that long press on a draggable element starts drag.<br>
 2. Check that long press on non-draggable text selects the text.<br>
 3. Check that long press on selected text starts drag.</p>
-<div id="text">Testing</div>
-<div id="draggableDivResult">FAIL</div>
-<div id="draggableTextResult">FAIL</div>
 <script>
-function onTextDivDrag() {
-    document.getElementById("draggableTextResult").innerHTML = "PASS";
-}
-function onDragStart() {
-    document.getElementById("draggableDivResult").innerHTML = "PASS";
-}
-function runTest()
-{
-    if (window.testRunner)
-        testRunner.dumpAsText();
+// The runTest method is called when the iframe is loaded via top.runTest().
+function runTest() {
+  promise_test(async t => {
+    preventContextMenu(t);
+    const target = document.querySelector('iframe');
+    let dragPromise = dragStartEvent();
+    let longpressOptions = { x: -50, y: -50, duration: 1200 };
+    await touchLongPressElement(target, longpressOptions);
+    let result = await dragPromise;
+    assert_equals(result.id, 'draggableDiv');
 
-    if (window.internals)
-        internals.settings.setTouchDragDropEnabled(true);
+    // Reposition long-press to text.
+    longpressOptions = { x: -80, y: 25, duration: 1200 };
+    await touchLongPressElement(target, longpressOptions);
+    assert_equals(target.contentDocument.getSelection().toString(), 'Some');
 
-    if (!window.eventSender)
-        return;
-    if (eventSender.gestureLongPress) {
-        eventSender.gestureLongPress(50, 50);
-        eventSender.gestureLongPress(20, 110);
-        eventSender.gestureLongPress(20, 110);
-    } else {
-        debug("gestureLongPress not implemented by this platform");
-        return;
-    }
+    dragPromise = dragStartEvent();
+    await touchLongPressElement(target, longpressOptions);
+    result = await dragPromise;
+    assert_equals(result.id, 'draggableText');
+  }, 'Long press drag and drop in an iframe');
 }
 </script>
 </body>
diff --git a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-drag-drop-touch-editing-combined.html b/third_party/blink/web_tests/fast/events/touch/gesture/long-press-drag-drop-touch-editing-combined.html
index e2982ad1..7b4dc61e 100644
--- a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-drag-drop-touch-editing-combined.html
+++ b/third_party/blink/web_tests/fast/events/touch/gesture/long-press-drag-drop-touch-editing-combined.html
@@ -1,54 +1,76 @@
 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
 <html>
 <head>
-<script src="../../../../resources/js-test.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
+<script src="../../../../resources/testdriver.js"></script>
+<script src="../../../../resources/testdriver-actions.js"></script>
+<script src="../../../..//resources/testdriver-vendor.js"></script>
+<style>
+  #draggableDiv {
+    background-color: teal;
+    height: 100px;
+    width: 100px;
+  }
+  #draggableText {
+    /* Leave adequate margin to avoid ambiguity due to touch adjustment. */
+    margin-top: 20px;
+  }
+</style>
 </head>
-<body onload="test()">
+<body>
 <p>This test checks long press behavior when both touch drag/drop and touch editing are enabled.<br>
 1. Check that long press on a draggable element starts drag.<br>
 2. Check that long press on non-draggable text selects the text.<br>
 3. Check that long press on selected text starts drag.</p>
-<div id="text">Testing</div>
-<div id="draggableDivResult">FAIL</div>
-<div id="selectTextResult">FAIL</div>
-<div id="draggableTextResult">FAIL</div>
 <div id="draggableDiv" draggable='true'>Drag me</div>
-<div id="textDiv">Some text</div>
+<div><span id='draggableText'>Some text</span></div>
 <script>
-function test()
-{
-    if (window.testRunner)
-        testRunner.dumpAsText();
+if (window.internals)
+  internals.settings.setTouchDragDropEnabled(true);
 
-    if (window.internals)
-        internals.settings.setTouchDragDropEnabled(true);
-
-    var draggableDiv = document.getElementById("draggableDiv");
-    draggableDiv.ondragstart = function() { document.getElementById("draggableDivResult").innerHTML = "PASS"; }
-
-    var textDiv = document.getElementById("textDiv");
-    textDiv.ondragstart = function() { document.getElementById("draggableTextResult").innerHTML = "PASS"; }
-
-    if (!window.eventSender)
-        return;
-    if (eventSender.gestureLongPress) {
-        var x = draggableDiv.offsetParent.offsetLeft + draggableDiv.offsetLeft + 4;
-        var y = draggableDiv.offsetParent.offsetTop + draggableDiv.offsetTop + draggableDiv.offsetHeight / 2;
-        eventSender.gestureLongPress(x, y);
-
-        x = textDiv.offsetParent.offsetLeft + textDiv.offsetLeft + 4;
-        y = textDiv.offsetParent.offsetTop + textDiv.offsetTop + textDiv.offsetHeight / 2;
-
-        eventSender.gestureLongPress(x, y);
-        if (document.getSelection().toString().length > 0)
-            document.getElementById("selectTextResult").innerHTML = "PASS";
-
-        eventSender.gestureLongPress(x, y);
-    } else {
-        debug("gestureLongPress not implemented by this platform");
-        return;
+function dragStartEvent(test, target) {
+  return new Promise((resolve, reject) => {
+    const dragStartListener = (event) => {
+      event.preventDefault();
+      resolve(event.target);
     }
+    const pointerupListener = (event) => {
+      reject('missing dragstart event');
+    }
+    target.addEventListener('dragstart', dragStartListener);
+    document.addEventListener('pointerup', pointerupListener);
+    test.add_cleanup(() => {
+      target.removeEventListener('dragstart', dragStartListener);
+      document.removeEventListener('pointerup', pointerupListener);
+    });
+  });
 }
+
+promise_test(async t => {
+  preventContextMenu(t);
+  const draggableDiv = document.getElementById('draggableDiv');
+  const dragPromise = dragStartEvent(t, draggableDiv);
+  const longpressOptions = { duration: 1200 };
+  await touchLongPressElement(draggableDiv, longpressOptions);
+  const result = await dragPromise;
+  assert_equals(result.id, 'draggableDiv');
+}, 'Long-press on a draggable element');
+
+promise_test(async t => {
+  preventContextMenu(t);
+  const draggableText =  document.getElementById('draggableText');
+  // Position long-press off-center to select the second word.
+  const longpressOptions = { x: 20, duration: 1200 };
+  await touchLongPressElement(draggableText, longpressOptions);
+  assert_equals(document.getSelection().toString(), 'text');
+
+  const dragPromise = dragStartEvent(t, draggableText);
+  await touchLongPressElement(draggableText, longpressOptions);
+  const result = await dragPromise;
+  assert_equals(result.data, 'Some text');
+}, 'Long-press text editing');
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-focuses-frame-expected.txt b/third_party/blink/web_tests/fast/events/touch/gesture/long-press-focuses-frame-expected.txt
deleted file mode 100644
index 64df201b..0000000
--- a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-focuses-frame-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-PASS document.activeElement == frame1 is true
-PASS document.activeElement == frame2 is true
-PASS document.activeElement == document.body is true
-This test checks that long press to select text focuses the pressed frame.
- 
diff --git a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-focuses-frame.html b/third_party/blink/web_tests/fast/events/touch/gesture/long-press-focuses-frame.html
index 92b3e95..f6a521f 100644
--- a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-focuses-frame.html
+++ b/third_party/blink/web_tests/fast/events/touch/gesture/long-press-focuses-frame.html
@@ -1,18 +1,19 @@
 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
 <html>
 <head>
-<script src="../../../../resources/js-test.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
+<script src="../../../../resources/testdriver.js"></script>
+<script src="../../../../resources/testdriver-actions.js"></script>
+<script src="../../../..//resources/testdriver-vendor.js"></script>
 </head>
 <body>
-<div id="mainFrameText">This test checks that long press to select text focuses the pressed frame.</div>
+<div id="mainFrameText">
+This test checks that long press to select text focuses the pressed frame.
+</div>
 <script>
 
-function longPressElement(el) {
-    x = el.offsetLeft + el.offsetWidth / 2;
-    y = el.offsetTop + el.offsetHeight / 2;
-    eventSender.gestureLongPress(x, y);
-}
-
 var framesLoaded = 0;
 function frameLoaded() {
   framesLoaded++;
@@ -20,29 +21,24 @@
     runTest();
 }
 
-function runTest()
-{
-    if (window.testRunner)
-        testRunner.dumpAsText();
-
-    if (!window.eventSender)
-        return;
-
-    if (eventSender.gestureLongPress) {
-        mainFrameText = document.getElementById("mainFrameText");
-        frame1 = document.getElementById("frame1");
-        frame2 = document.getElementById("frame2");
-
-        longPressElement(frame1);
-        shouldBeTrue("document.activeElement == frame1");
-        longPressElement(frame2);
-        shouldBeTrue("document.activeElement == frame2");
-        longPressElement(mainFrameText);
-        shouldBeTrue("document.activeElement == document.body");
-    } else {
-        debug("gestureLongPress not implemented by this platform");
-    }
+function runTest() {
+  promise_test(async t => {
+    // Text selection requires a longer long-press duration than a context menu
+    // popup.
+    const longPressOptions = { duration: 1200 };
+    preventContextMenu(t);
+    mainFrameText = document.getElementById("mainFrameText");
+    frame1 = document.getElementById("frame1");
+    frame2 = document.getElementById("frame2");
+    await touchLongPressElement(frame1, longPressOptions);
+    assert_equals(document.activeElement, frame1);
+    await touchLongPressElement(frame2, longPressOptions);
+    assert_equals(document.activeElement, frame2);
+    await touchLongPressElement(mainFrameText, longPressOptions);
+    assert_equals(document.activeElement, document.body);
+  }, 'Long press to select text focuses the pressed frame');
 }
+
 </script>
 <iframe id="frame1" onload="frameLoaded()" src="data:text/html,
 <html>
diff --git a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-on-draggable-element-in-iframe-triggers-drag-expected.txt b/third_party/blink/web_tests/fast/events/touch/gesture/long-press-on-draggable-element-in-iframe-triggers-drag-expected.txt
deleted file mode 100644
index 0fcf7182..0000000
--- a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-on-draggable-element-in-iframe-triggers-drag-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-
-This test checks that a long press gesture on a draggable element in an iframe starts a drag/drop session.
-
-Testing
-FAIL
-PASS
diff --git a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-on-draggable-element-in-iframe-triggers-drag.html b/third_party/blink/web_tests/fast/events/touch/gesture/long-press-on-draggable-element-in-iframe-triggers-drag.html
index 2d757483..487edd16 100644
--- a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-on-draggable-element-in-iframe-triggers-drag.html
+++ b/third_party/blink/web_tests/fast/events/touch/gesture/long-press-on-draggable-element-in-iframe-triggers-drag.html
@@ -1,37 +1,59 @@
 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
 <html>
 <head>
-<script src="../../../../resources/js-test.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
+<script src="../../../../resources/testdriver.js"></script>
+<script src="../../../../resources/testdriver-actions.js"></script>
+<script src="../../../..//resources/testdriver-vendor.js"></script>
 </head>
 <body>
-<iframe src="resources/drag-inside-iframe2.html"></iframe>
-<p>This test checks that a long press gesture on a draggable element in an iframe starts a drag/drop session.</p>
-<div id="text">Testing</div>
-<div id="dragResult">FAIL</div>
-<div id="mouseDownResult">PASS</div>
 <script>
-function onDragStart() {
-    document.getElementById("dragResult").innerHTML = "PASS";
-}
-function onMouseDown() {
-    document.getElementById("mouseDownResult").innerHTML = "FAIL";
-}
-function runTest()
-{
-    if (window.testRunner)
-        testRunner.dumpAsText();
+if (window.internals)
+  internals.settings.setTouchDragDropEnabled(true);
 
-    if (window.internals)
-        internals.settings.setTouchDragDropEnabled(true);
+// The iframe will call top.dragStarted() or top.mousedownTriggered() on
+// receiving the corresponding event.
+function mousedownTriggered() {
+  rejectDragStartPromise();
+}
 
-    if (!window.eventSender)
-        return;
-    if (eventSender.gestureLongPress) {
-        eventSender.gestureLongPress(50, 50);
-    } else {
-        debug("gestureLongPress not implemented by this platform");
-        return;
+function dragStarted() {
+  resolveDragStartPromise();
+}
+
+let resolveDragStartPromise = undefined;
+let rejectDragStartPromise = undefined;
+function dragStartEvent() {
+  return new Promise((resolve, reject) => {
+    resolveDragStartPromise = resolve;
+    rejectDragStartPromise = () => {
+      reject('missing dragstart event');
     }
+  });
+}
+</script>
+<iframe src="resources/drag-inside-iframe2.html">
+</iframe>
+<p>This test checks that a long press gesture on a draggable element in an
+iframe starts a drag/drop session.</p>
+<script>
+
+// The runTest method is triggered when the iframe is loaded.
+function runTest() {
+  promise_test(async t => {
+    preventContextMenu(t);
+    const target = document.querySelector('iframe');
+    const dragPromise = dragStartEvent();
+
+    // Shift the long-press position to be over a draggable div within the
+    // iframe.
+    const longpressOptions = { x: -50, y: -50, duration: 1200 };
+    await touchLongPressElement(target, longpressOptions);
+    return dragPromise;
+  }, 'Long press gesture on a draggable element in an iframe starts a ' +
+     'drag/drop session.');
 }
 </script>
 </body>
diff --git a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-on-draggable-element-in-nested-iframes-triggers-drag-expected.txt b/third_party/blink/web_tests/fast/events/touch/gesture/long-press-on-draggable-element-in-nested-iframes-triggers-drag-expected.txt
deleted file mode 100644
index 0fcf7182..0000000
--- a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-on-draggable-element-in-nested-iframes-triggers-drag-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-
-This test checks that a long press gesture on a draggable element in an iframe starts a drag/drop session.
-
-Testing
-FAIL
-PASS
diff --git a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-on-draggable-element-in-nested-iframes-triggers-drag.html b/third_party/blink/web_tests/fast/events/touch/gesture/long-press-on-draggable-element-in-nested-iframes-triggers-drag.html
index a2f7958..78fe281 100644
--- a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-on-draggable-element-in-nested-iframes-triggers-drag.html
+++ b/third_party/blink/web_tests/fast/events/touch/gesture/long-press-on-draggable-element-in-nested-iframes-triggers-drag.html
@@ -1,37 +1,61 @@
 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
 <html>
 <head>
-<script src="../../../../resources/js-test.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
+<script src="../../../../resources/testdriver.js"></script>
+<script src="../../../../resources/testdriver-actions.js"></script>
+<script src="../../../..//resources/testdriver-vendor.js"></script>
+<style>
+  iframe {
+    height: 200px;
+    width: 200px;
+    overflow: hidden;
+  }
+</style>
 </head>
 <body>
+<script>
+if (window.internals)
+  internals.settings.setTouchDragDropEnabled(true);
+
+// The iframe will call top.dragStarted() or top.mousedownTriggered() on
+// receiving the corresponding event.
+function mousedownTriggered() {
+  rejectDragStartPromise();
+}
+
+function dragStarted(element) {
+  resolveDragStartPromise(element);
+}
+
+let resolveDragStartPromise = undefined;
+let rejectDragStartPromise = undefined;
+function dragStartEvent() {
+  return new Promise((resolve, reject) => {
+    resolveDragStartPromise = resolve;
+    rejectDragStartPromise = () => {
+      reject('missing dragstart event');
+    }
+  });
+}
+</script>
 <iframe src="resources/drag-inside-nested-iframes2.html"></iframe>
 <p>This test checks that a long press gesture on a draggable element in an iframe starts a drag/drop session.</p>
-<div id="text">Testing</div>
-<div id="dragResult">FAIL</div>
-<div id="mouseDownResult">PASS</div>
 <script>
-function onDragStart() {
-    document.getElementById("dragResult").innerHTML = "PASS";
-}
-function onMouseDown() {
-    document.getElementById("mouseDownResult").innerHTML = "FAIL";
-}
-function runTest()
-{
-    if (window.testRunner)
-        testRunner.dumpAsText();
-
-    if (window.internals)
-        internals.settings.setTouchDragDropEnabled(true);
-
-    if (!window.eventSender)
-        return;
-    if (eventSender.gestureLongPress) {
-        eventSender.gestureLongPress(20, 20);
-    } else {
-        debug("gestureLongPress not implemented by this platform");
-        return;
-    }
+// The runTest method is triggered via top.runTest when the iframe is loaded.
+function runTest() {
+  promise_test(async t => {
+    preventContextMenu(t);
+    const target = document.querySelector('iframe');
+    let dragPromise = dragStartEvent();
+    let longpressOptions = { x: -50, y: -50, duration: 1200 };
+    await touchLongPressElement(target, longpressOptions);
+    let result = await dragPromise;
+    assert_equals(result.id, 'draggableDiv');
+  }, 'Long press gesture on a draggable element in a nested iframe starts a ' +
+     'drag/drop session.');
 }
 </script>
 </body>
diff --git a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-selects-word-when-touch-editing-enabled-expected.txt b/third_party/blink/web_tests/fast/events/touch/gesture/long-press-selects-word-when-touch-editing-enabled-expected.txt
deleted file mode 100644
index 5c08bd9b..0000000
--- a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-selects-word-when-touch-editing-enabled-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-
-This test checks that a long press gesture selects word when touch editing is enabled.
-
-Testing
-PASS
-Some text
diff --git a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-selects-word-when-touch-editing-enabled.html b/third_party/blink/web_tests/fast/events/touch/gesture/long-press-selects-word-when-touch-editing-enabled.html
index 0f13c73..de1ecac4 100644
--- a/third_party/blink/web_tests/fast/events/touch/gesture/long-press-selects-word-when-touch-editing-enabled.html
+++ b/third_party/blink/web_tests/fast/events/touch/gesture/long-press-selects-word-when-touch-editing-enabled.html
@@ -1,35 +1,28 @@
 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
 <html>
 <head>
-<script src="../../../../resources/js-test.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
+<script src="../../../../resources/testdriver.js"></script>
+<script src="../../../../resources/testdriver-actions.js"></script>
+<script src="../../../..//resources/testdriver-vendor.js"></script>
 </head>
-<body onload="test()">
+<body onload="runTest()">
 <p>This test checks that a long press gesture selects word when touch editing is enabled.</p>
-<div id="text">Testing</div>
-<div id="result">FAIL</div>
-<div id="textDiv">Some text</div>
+<div><span id="target">Some selectable text</span></div>
 <script>
-function test()
-{
-    if (window.testRunner)
-        testRunner.dumpAsText();
-
-    var textDiv = document.getElementById("textDiv");
-
-    var x = textDiv.offsetParent.offsetLeft + textDiv.offsetLeft + 4;
-    var y = textDiv.offsetParent.offsetTop + textDiv.offsetTop + textDiv.offsetHeight / 2;
-
-    if (!window.eventSender)
-        return;
-    if (eventSender.gestureLongPress) {
-        eventSender.gestureLongPress(x, y);
-        var selectedText = document.getSelection();
-        if (selectedText.toString().length > 0)
-            document.getElementById("result").innerHTML = "PASS";
-    } else {
-        debug("gestureLongPress not implemented by this platform");
-        return;
-    }
+function runTest() {
+  promise_test(async t => {
+    preventContextMenu(t);
+    const target = document.getElementById("target");
+    await touchLongPressElement(target, {
+        // Touch based text editing requires a hefty longpress duration.
+        duration: 1200
+    });
+    const selectedText = document.getSelection();
+    assert_equals(selectedText.toString(), "selectable");
+  }, 'Long press gesture selects word when touch editing is enabled');
 }
 </script>
 </body>
diff --git a/third_party/blink/web_tests/fast/events/touch/gesture/resources/drag-inside-iframe2.html b/third_party/blink/web_tests/fast/events/touch/gesture/resources/drag-inside-iframe2.html
index 562531e..c0c177d 100644
--- a/third_party/blink/web_tests/fast/events/touch/gesture/resources/drag-inside-iframe2.html
+++ b/third_party/blink/web_tests/fast/events/touch/gesture/resources/drag-inside-iframe2.html
@@ -1,18 +1,46 @@
+<!DOCTYPE html>
 <html>
+<style>
+  #draggableDiv {
+    width: 100px;
+    height: 100px;
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    background-color: teal;
+  }
+  #draggableText {
+    position: absolute;
+    /* Leave sufficient gap between the draggable div and the text to avoid
+       ambiguity due to touch fuzzing. */
+    top: 120px;
+    left: 0px;
+    background-color: aqua;
+  }
+</style>
 <body onload="top.runTest()">
-<div id='mydiv' draggable='true' ondragstart='drag(event)' onmousedown='mousedown(event)' style='width:100px;height:100px;position:absolute;top:0px; left:0px; background-color:blue;'>
+<div id='draggableDiv' draggable='true'>
 Drag me</div>
-<div id='textdiv' ondragstart='textDivDrag(event)' style='position:absolute;top:100px; left:0px; background-color:blue;'>Some text</div>
+<div><span id='draggableText'>Some text</span></div>
 <script type='text/javascript'>
-    function drag(event) {
-        top.onDragStart();
-    }
-    function mousedown(event) {
-        top.onMouseDown();
-    }
-    function textDivDrag(event) {
-        top.onTextDivDrag();
-    }
+  const block = document.getElementById('draggableDiv');
+  block.addEventListener('dragstart', (event) => {
+    event.preventDefault();
+    top.dragStarted(block);
+  });
+  block.addEventListener('mousedown', (event) => {
+    top.mousedownTriggered();
+  });
+
+  const text = document.getElementById('draggableText');
+  text.addEventListener('dragstart', (event) => {
+    event.preventDefault();
+    top.dragStarted(text);
+  });
+
+  document.addEventListener('contextmenu', event => {
+    event.preventDefault();
+  });
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/fast/events/touch/gesture/resources/drag-inside-nested-iframes2.html b/third_party/blink/web_tests/fast/events/touch/gesture/resources/drag-inside-nested-iframes2.html
index 3c652a4a..3be5e80a 100644
--- a/third_party/blink/web_tests/fast/events/touch/gesture/resources/drag-inside-nested-iframes2.html
+++ b/third_party/blink/web_tests/fast/events/touch/gesture/resources/drag-inside-nested-iframes2.html
@@ -1,5 +1,17 @@
 <html>
+<style>
+  iframe {
+    height: 200px;
+    width: 200px;
+    overflow: hidden;
+  }
+</style>
 <body>
 <iframe src="drag-inside-nested-iframes3.html"></iframe>
+<script>
+  document.addEventListener('contextmenu', event => {
+    event.preventDefault();
+  });
+</script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/fast/events/touch/gesture/resources/drag-inside-nested-iframes3.html b/third_party/blink/web_tests/fast/events/touch/gesture/resources/drag-inside-nested-iframes3.html
index 01bf645..366bcfc 100644
--- a/third_party/blink/web_tests/fast/events/touch/gesture/resources/drag-inside-nested-iframes3.html
+++ b/third_party/blink/web_tests/fast/events/touch/gesture/resources/drag-inside-nested-iframes3.html
@@ -1,11 +1,29 @@
 <html>
+<style>
+#draggableDiv {
+  width: 100px;
+  height: 100px;
+  position: absolute;
+  top: 0px;
+  left: 0px;
+  background-color: teal;
+}
+</style>
 <body onload="top.runTest()">
-<div id='mydiv' draggable='true' ondragstart='drag(event)' style='width:100px;height:100px;position:absolute;top:0px; left:0px; background-color:blue;'>
+<div id='draggableDiv' draggable='true'>
 Drag me</div>
 <script type='text/javascript'>
-    function drag(event) {
-        top.onDragStart();
-    }
+  const draggableDiv = document.getElementById('draggableDiv');
+  draggableDiv.addEventListener('dragstart', (event) => {
+    event.preventDefault();
+    top.dragStarted(draggableDiv);
+  });
+  draggableDiv.addEventListener('mousedown', (event) => {
+    top.mousedownTriggered();
+  });
+  document.addEventListener('contextmenu', event => {
+    event.preventDefault();
+  });
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/platform/win11-arm64/fast/loader/json-document-appearance-expected.png b/third_party/blink/web_tests/platform/win11-arm64/fast/loader/json-document-appearance-expected.png
new file mode 100644
index 0000000..1972d6431
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win11-arm64/fast/loader/json-document-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win11-arm64/http/tests/devtools/console/console-stack-overflow-expected.txt b/third_party/blink/web_tests/platform/win11-arm64/http/tests/devtools/console/console-stack-overflow-expected.txt
deleted file mode 100644
index fd30766..0000000
--- a/third_party/blink/web_tests/platform/win11-arm64/http/tests/devtools/console/console-stack-overflow-expected.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-Tests that when stack overflow exception happens when inspector is open the stack trace is correctly shown in console.
-
-console-stack-overflow.js:19 Uncaught RangeError: Maximum call stack size exceeded
-    at overflow (console-stack-overflow.js:19:22)
-    at overflow (console-stack-overflow.js:19:27)
-    at overflow (console-stack-overflow.js:19:27)
-    at overflow (console-stack-overflow.js:19:27)
-    at overflow (console-stack-overflow.js:19:27)
-    at overflow (console-stack-overflow.js:19:27)
-    at overflow (console-stack-overflow.js:19:27)
-    at overflow (console-stack-overflow.js:19:27)
-    at overflow (console-stack-overflow.js:19:27)
-    at overflow (console-stack-overflow.js:19:27)
-overflow @ console-stack-overflow.js:19
-overflow @ console-stack-overflow.js:19
-overflow @ console-stack-overflow.js:19
-overflow @ console-stack-overflow.js:19
-overflow @ console-stack-overflow.js:19
-overflow @ console-stack-overflow.js:19
-overflow @ console-stack-overflow.js:19
-overflow @ console-stack-overflow.js:19
-overflow @ console-stack-overflow.js:19
-overflow @ console-stack-overflow.js:19
-overflow @ console-stack-overflow.js:19
-o
-
diff --git a/third_party/blink/web_tests/platform/win11-arm64/http/tests/inspector-protocol/fetch-later/activate-post-when-document-alive.https-expected.txt b/third_party/blink/web_tests/platform/win11-arm64/http/tests/inspector-protocol/fetch-later/activate-post-when-document-alive.https-expected.txt
new file mode 100644
index 0000000..9ff7efb3
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win11-arm64/http/tests/inspector-protocol/fetch-later/activate-post-when-document-alive.https-expected.txt
@@ -0,0 +1,110 @@
+Tests that appropriate Network domain events are dispatched for a fetchLater POST request when its Document is alive.
+Network.requestWillBeSent{
+    documentURL : <string>
+    frameId : <string>
+    hasUserGesture : false
+    initiator : {
+        type : script
+        url : https://127.0.0.1:8443/
+    }
+    loaderId : <string>
+    redirectHasExtraInfo : false
+    request : {
+        hasPostData : true
+        headers : {
+            Accept : */*
+            Content-Type : text/plain;charset=UTF-8
+            Referer : https://127.0.0.1:8443/inspector-protocol/resources/inspector-protocol-page.html
+            User-Agent : <string>
+            sec-ch-ua : "content_shell";v="999"
+            sec-ch-ua-mobile : ?0
+            sec-ch-ua-platform : "Unknown"
+        }
+        initialPriority : High
+        method : POST
+        mixedContentType : blockable
+        postData : {"foo":"bar"}
+        postDataEntries : [
+            [0] : {
+                bytes : eyJmb28iOiJiYXIifQ==
+            }
+        ]
+        referrerPolicy : strict-origin-when-cross-origin
+        url : https://127.0.0.1:8443/devtools/network/resources/resource.php
+    }
+    requestId : <string>
+    timestamp : <number>
+    type : Fetch
+    wallTime : <number>
+}
+Network.responseReceived{
+    frameId : <string>
+    hasExtraInfo : false
+    loaderId : <string>
+    requestId : <string>
+    response : {
+        alternateProtocolUsage : unspecifiedReason
+        charset : 
+        connectionId : <number>
+        connectionReused : true
+        encodedDataLength : 333
+        fromDiskCache : false
+        fromPrefetchCache : false
+        fromServiceWorker : false
+        headers : {
+            Access-Control-Allow-Origin : *
+            Cache-Control : no-store, no-cache, must-revalidate
+            Connection : Keep-Alive
+            Content-Length : 11
+            Content-Type : text/plain
+            Date : <string>
+            Expires : Thu, 01 Dec 2003 16:00:00 GMT
+            Keep-Alive : timeout=10
+            Pragma : no-cache
+            Server : Apache
+            X-Powered-By : <string>
+        }
+        mimeType : text/plain
+        protocol : http/1.1
+        remoteIPAddress : 127.0.0.1
+        remotePort : 8443
+        responseTime : <number>
+        securityState : insecure
+        status : 200
+        statusText : OK
+        timing : {
+            connectEnd : -1
+            connectStart : -1
+            dnsEnd : -1
+            dnsStart : -1
+            proxyEnd : -1
+            proxyStart : -1
+            pushEnd : 0
+            pushStart : 0
+            receiveHeadersEnd : <number>
+            receiveHeadersStart : <number>
+            requestTime : <number>
+            sendEnd : <number>
+            sendStart : <number>
+            sslEnd : -1
+            sslStart : -1
+            workerFetchStart : -1
+            workerReady : -1
+            workerRespondWithSettled : -1
+            workerStart : -1
+        }
+        url : https://127.0.0.1:8443/devtools/network/resources/resource.php
+    }
+    timestamp : <number>
+    type : Fetch
+}
+Network.loadingFinished{
+    encodedDataLength : 344
+    requestId : <string>
+    timestamp : <number>
+}
+Unable to get fetchLater response body{
+    code : -32000
+    message : No resource with given identifier found
+}
+
diff --git a/third_party/blink/web_tests/platform/win11-arm64/http/tests/inspector-protocol/timeline/auction-worklet-network-expected.txt b/third_party/blink/web_tests/platform/win11-arm64/http/tests/inspector-protocol/timeline/auction-worklet-network-expected.txt
index 16abd1d3..905ba371 100644
--- a/third_party/blink/web_tests/platform/win11-arm64/http/tests/inspector-protocol/timeline/auction-worklet-network-expected.txt
+++ b/third_party/blink/web_tests/platform/win11-arm64/http/tests/inspector-protocol/timeline/auction-worklet-network-expected.txt
@@ -11,6 +11,16 @@
     encodedDataLength : 230
     fromCache : false
     fromServiceWorker : false
+    headers : {
+        Ad-Auction-Allowed : true
+        Connection : Keep-Alive
+        Content-Length : 598
+        Content-Type : application/javascript
+        Date : <date>
+        Keep-Alive : timeout=10
+        Server : Apache
+        X-Powered-By : <x-powered-by>
+    }
     mimeType : application/javascript
     requestId : <string>
     responseTime : <absolute timestamp>
@@ -52,6 +62,16 @@
     encodedDataLength : 230
     fromCache : false
     fromServiceWorker : false
+    headers : {
+        Ad-Auction-Allowed : true
+        Connection : Keep-Alive
+        Content-Length : 522
+        Content-Type : application/javascript
+        Date : <date>
+        Keep-Alive : timeout=10
+        Server : Apache
+        X-Powered-By : <x-powered-by>
+    }
     mimeType : application/javascript
     requestId : <string>
     responseTime : <absolute timestamp>
diff --git a/third_party/blink/web_tests/platform/win11-arm64/media/alpha-video-playback-expected.png b/third_party/blink/web_tests/platform/win11-arm64/media/alpha-video-playback-expected.png
index f662b900..f1b65cd 100644
--- a/third_party/blink/web_tests/platform/win11-arm64/media/alpha-video-playback-expected.png
+++ b/third_party/blink/web_tests/platform/win11-arm64/media/alpha-video-playback-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win11-arm64/paint/invalidation/shadow-multiple-expected.png b/third_party/blink/web_tests/platform/win11-arm64/paint/invalidation/shadow-multiple-expected.png
new file mode 100644
index 0000000..4715288
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win11-arm64/paint/invalidation/shadow-multiple-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win11-arm64/virtual/keepalive-in-browser-migration/external/wpt/xhr/send-content-type-charset-expected.png b/third_party/blink/web_tests/platform/win11-arm64/virtual/keepalive-in-browser-migration/external/wpt/xhr/send-content-type-charset-expected.png
deleted file mode 100644
index f301594..0000000
--- a/third_party/blink/web_tests/platform/win11-arm64/virtual/keepalive-in-browser-migration/external/wpt/xhr/send-content-type-charset-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/resources/gesture-util.js b/third_party/blink/web_tests/resources/gesture-util.js
index 45aa05c..de9105ad 100644
--- a/third_party/blink/web_tests/resources/gesture-util.js
+++ b/third_party/blink/web_tests/resources/gesture-util.js
@@ -1051,6 +1051,56 @@
   return pointerTap(x, y, options);
 }
 
+// Long press on the target element. The options are of the form:
+// {
+//    x: horizontal offset from midpoint of the element (default 0)
+//    y: vertical offset from the midpoint of the element (default 0)
+//    duration: duration of the press in milliseconds (default 400)
+// }
+//
+// Be sure to call preventContextMenu during test setup to avoid a memory leak
+// before calling this method. If event handling is permitted to transfer to the
+// browser process, we are unable to fully tear down the test resulting in a
+// leak.
+function touchLongPressElement(target, options) {
+  // Conservative long-press duration based on timing for a context menu popup.
+  // Some long-press operations require longer.
+  const LONG_PRESS_DURATION = 400;
+  const x = (options && options.x)? options.x : 0;
+  const y = (options && options.y)? options.y : 0;
+  const duration = (options && options.duration !== undefined)
+                       ? options.duration
+                       : LONG_PRESS_DURATION;
+  verifyTestDriverLoaded();
+  const pointerPromise = new Promise((resolve) => {
+    const listener = () => {
+      document.removeEventListener('pointerup', listener);
+      document.removeEventListener('pointercancel', listener);
+      resolve();
+    }
+    document.addEventListener('pointerup', listener);
+    document.addEventListener('pointercancel', listener);
+  });
+  const actionPromise = new test_driver.Actions()
+      .addPointer('pointer1', 'touch')
+      .pointerMove(x, y, {origin: target})
+      .pointerDown()
+      .pause(duration)
+      .pointerUp()
+      .send();
+  return actionPromise.then(pointerPromise);
+}
+
+function preventContextMenu(test) {
+  const listener = (event) => {
+    event.preventDefault();
+  }
+  document.addEventListener('contextmenu', listener);
+  test.add_cleanup(() => {
+    document.removeEventListener('contextmenu', listener);
+  });
+}
+
 // Perform a click action where a scroll is expected such as on a scrollbar
 // arrow or on a scrollbar track. The promise will timeout if no scrolling is
 // triggered.
diff --git a/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index bfc9b04..25f083c4 100644
--- a/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -789,14 +789,12 @@
     method initMessageEvent
 interface MessagePort : EventTarget
     attribute @@toStringTag
-    getter onclose
     getter onmessage
     getter onmessageerror
     method close
     method constructor
     method postMessage
     method start
-    setter onclose
     setter onmessage
     setter onmessageerror
 interface NavigationPreloadManager
diff --git a/third_party/blink/web_tests/virtual/stable/http/tests/worklet/webexposed/global-interface-listing-audio-worklet-expected.txt b/third_party/blink/web_tests/virtual/stable/http/tests/worklet/webexposed/global-interface-listing-audio-worklet-expected.txt
index 80134b0..a3749e8 100644
--- a/third_party/blink/web_tests/virtual/stable/http/tests/worklet/webexposed/global-interface-listing-audio-worklet-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/http/tests/worklet/webexposed/global-interface-listing-audio-worklet-expected.txt
@@ -61,14 +61,12 @@
 CONSOLE MESSAGE:     method initMessageEvent
 CONSOLE MESSAGE: interface MessagePort : EventTarget
 CONSOLE MESSAGE:     attribute @@toStringTag
-CONSOLE MESSAGE:     getter onclose
 CONSOLE MESSAGE:     getter onmessage
 CONSOLE MESSAGE:     getter onmessageerror
 CONSOLE MESSAGE:     method close
 CONSOLE MESSAGE:     method constructor
 CONSOLE MESSAGE:     method postMessage
 CONSOLE MESSAGE:     method start
-CONSOLE MESSAGE:     setter onclose
 CONSOLE MESSAGE:     setter onmessage
 CONSOLE MESSAGE:     setter onmessageerror
 CONSOLE MESSAGE: interface ReadableByteStreamController
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 5fdc139..d0464cc 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -1175,14 +1175,12 @@
 [Worker]     method initMessageEvent
 [Worker] interface MessagePort : EventTarget
 [Worker]     attribute @@toStringTag
-[Worker]     getter onclose
 [Worker]     getter onmessage
 [Worker]     getter onmessageerror
 [Worker]     method close
 [Worker]     method constructor
 [Worker]     method postMessage
 [Worker]     method start
-[Worker]     setter onclose
 [Worker]     setter onmessage
 [Worker]     setter onmessageerror
 [Worker] interface NavigationPreloadManager
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 c184070c..eea9c5acb 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
@@ -5513,14 +5513,12 @@
     method initMessageEvent
 interface MessagePort : EventTarget
     attribute @@toStringTag
-    getter onclose
     getter onmessage
     getter onmessageerror
     method close
     method constructor
     method postMessage
     method start
-    setter onclose
     setter onmessage
     setter onmessageerror
 interface MimeType
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
index 0709f03..766086f 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -727,14 +727,12 @@
 [Worker]     method initMessageEvent
 [Worker] interface MessagePort : EventTarget
 [Worker]     attribute @@toStringTag
-[Worker]     getter onclose
 [Worker]     getter onmessage
 [Worker]     getter onmessageerror
 [Worker]     method close
 [Worker]     method constructor
 [Worker]     method postMessage
 [Worker]     method start
-[Worker]     setter onclose
 [Worker]     setter onmessage
 [Worker]     setter onmessageerror
 [Worker] interface NavigationPreloadManager
diff --git a/third_party/catapult b/third_party/catapult
index 6d39c23..27e029f 160000
--- a/third_party/catapult
+++ b/third_party/catapult
@@ -1 +1 @@
-Subproject commit 6d39c23fce9fdf9e5ce7c5730de4bc14556ec159
+Subproject commit 27e029f52955bf8ac9e35a1dacf4f56bb39d3eb8
diff --git a/third_party/chromite b/third_party/chromite
index b48f979..0ab00d0 160000
--- a/third_party/chromite
+++ b/third_party/chromite
@@ -1 +1 @@
-Subproject commit b48f979f2c28a68c21787fb94bb3eca15bc8a955
+Subproject commit 0ab00d0ab4c48eb09ff9a359365c55528ef2bbb5
diff --git a/third_party/depot_tools b/third_party/depot_tools
index 9305434c..784db7a 160000
--- a/third_party/depot_tools
+++ b/third_party/depot_tools
@@ -1 +1 @@
-Subproject commit 9305434c21feace5efde065e50151596484b7bc5
+Subproject commit 784db7a5f04f5093941b6c2228e40b9a10d553b2
diff --git a/third_party/ipcz/src/test/multinode_test.cc b/third_party/ipcz/src/test/multinode_test.cc
index fa6be82..f66a89aa 100644
--- a/third_party/ipcz/src/test/multinode_test.cc
+++ b/third_party/ipcz/src/test/multinode_test.cc
@@ -67,7 +67,7 @@
                               TestDriver* test_driver,
                               std::unique_ptr<TestNode> test_node)
       : source_(source),
-        client_thread_(absl::in_place,
+        client_thread_(std::in_place,
                        &RunTestNode,
                        test_driver,
                        std::move(test_node)) {}
diff --git a/third_party/pdfium b/third_party/pdfium
index 4282836..35d21ad 160000
--- a/third_party/pdfium
+++ b/third_party/pdfium
@@ -1 +1 @@
-Subproject commit 4282836f334034e852b8bc4e38459fdf8ae644db
+Subproject commit 35d21add50698046463a32f3c6c486913b3b2f9b
diff --git a/third_party/perfetto b/third_party/perfetto
index 7cf1c2d..74cdba3 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit 7cf1c2d0f40780828dae7a1590484cfc3055b48c
+Subproject commit 74cdba3b28c2a48bb3c5b5cae32812eb6c76a8da
diff --git a/third_party/skia b/third_party/skia
index 7777976..9b103a4 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit 7777976cd518253e939f4dc39d0b992cf6a7419b
+Subproject commit 9b103a4148e291537909a1c0765b43a5125676d2
diff --git a/third_party/webrtc b/third_party/webrtc
index 22b6564..aaa123d 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit 22b6564d77af5aa637c0da59219bfb681cc55156
+Subproject commit aaa123debbd106669fc849425b92607edbd5d26f
diff --git a/third_party/wpt_tools/README.chromium b/third_party/wpt_tools/README.chromium
index 2b526dd..8098a46 100644
--- a/third_party/wpt_tools/README.chromium
+++ b/third_party/wpt_tools/README.chromium
@@ -1,7 +1,7 @@
 Name: web-platform-tests - Test Suites for Web Platform specifications
 Short Name: wpt
 URL: https://github.com/web-platform-tests/wpt/
-Version: a68f313a50899795e1e6660b7398544d340f1652
+Version: 09de1c20d1d31f66bb7174c896f0a63fb41b59b4
 License: LICENSES FOR W3C TEST SUITES (https://www.w3.org/Consortium/Legal/2008/03-bsd-license.html)
 Security Critical: no
 Shipped: no
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/chrome.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/chrome.py
index 06f3d34..05f8146 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/chrome.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/chrome.py
@@ -147,7 +147,8 @@
     # Pass the --headless=new flag to Chrome if WPT's own --headless flag was
     # set. '--headless' should always mean the new headless mode, as the old
     # headless mode is not used anyway.
-    if kwargs["headless"] and "--headless=new" not in chrome_options["args"]:
+    if kwargs["headless"] and ("--headless=new" not in chrome_options["args"] and
+                               "--headless" not in chrome_options["args"]):
         chrome_options["args"].append("--headless=new")
 
     if test_type == "wdspec":
diff --git a/tools/binary_size/libsupersize/linker_map_parser.py b/tools/binary_size/libsupersize/linker_map_parser.py
index edea1e0..0ae4257 100755
--- a/tools/binary_size/libsupersize/linker_map_parser.py
+++ b/tools/binary_size/libsupersize/linker_map_parser.py
@@ -563,8 +563,7 @@
 
               is_partial = False
               cur_obj = None
-            elif (cur_obj == 'lto.tmp' or 'thinlto-cache' in cur_obj
-                  or '.lto.' in cur_obj):
+            elif cur_obj == 'lto.tmp' or 'thinlto-cache' in cur_obj:
               thin_map[address] = os.path.basename(cur_obj)
               cur_obj = None
 
diff --git a/tools/clang/DIR_METADATA b/tools/clang/DIR_METADATA
index 03fa4c23..24ff85f 100644
--- a/tools/clang/DIR_METADATA
+++ b/tools/clang/DIR_METADATA
@@ -1,11 +1,6 @@
-# Metadata information for this directory.
-#
-# For more information on DIR_METADATA files, see:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
-#
-# For the schema of this file, see Metadata message:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
-
-monorail {
+monorail: {
   component: "Tools"
-}
\ No newline at end of file
+}
+buganizer_public: {
+  component_id: 1456784
+}
diff --git a/tools/clang/blink_gc_plugin/DIR_METADATA b/tools/clang/blink_gc_plugin/DIR_METADATA
index b8ee9be3..a9fd745 100644
--- a/tools/clang/blink_gc_plugin/DIR_METADATA
+++ b/tools/clang/blink_gc_plugin/DIR_METADATA
@@ -1,12 +1,7 @@
-# Metadata information for this directory.
-#
-# For more information on DIR_METADATA files, see:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
-#
-# For the schema of this file, see Metadata message:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
-
-monorail {
+monorail: {
   component: "Blink>GarbageCollection"
 }
-team_email: "oilpan-reviews@chromium.org"
\ No newline at end of file
+team_email: "oilpan-reviews@chromium.org"
+buganizer_public: {
+  component_id: 1456254
+}
diff --git a/tools/clang/scripts/build.py b/tools/clang/scripts/build.py
index 339900e..ac6a9e2 100755
--- a/tools/clang/scripts/build.py
+++ b/tools/clang/scripts/build.py
@@ -721,6 +721,11 @@
 
   global CLANG_REVISION, PACKAGE_VERSION, LLVM_BUILD_DIR
 
+  # TODO(crbug.com/1517549): Remove in next Clang roll.
+  if args.llvm_force_head_revision:
+    global RELEASE_VERSION
+    RELEASE_VERSION = '19'
+
   if (args.pgo or args.thinlto) and not args.bootstrap:
     print('--pgo/--thinlto requires --bootstrap')
     return 1
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 11466e8..12e332a4 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -35,11 +35,12 @@
 # https://chromium.googlesource.com/chromium/src/+/main/docs/updating_clang.md
 # Reverting problematic clang rolls is safe, though.
 # This is the output of `git describe` and is usable as a commit-ish.
-CLANG_REVISION = 'llvmorg-19-init-817-g3f5fcb59'
-CLANG_SUB_REVISION = 1
+CLANG_REVISION = 'llvmorg-18-init-17730-gf670112a'
+CLANG_SUB_REVISION = 2
 
 PACKAGE_VERSION = '%s-%s' % (CLANG_REVISION, CLANG_SUB_REVISION)
-RELEASE_VERSION = '19'
+RELEASE_VERSION = '18'
+# TODO(crbug.com/1517549): Bump to 19 in next Clang roll.
 
 CDS_URL = os.environ.get('CDS_CLANG_BUCKET_OVERRIDE',
     'https://commondatastorage.googleapis.com/chromium-browser-clang')
@@ -334,6 +335,11 @@
                       help='Verify that clang has the passed-in version.')
   args = parser.parse_args()
 
+  # TODO(crbug.com/1517549): Remove in next Clang roll.
+  if args.llvm_force_head_revision:
+    global RELEASE_VERSION
+    RELEASE_VERSION = '19'
+
   if args.verify_version and args.verify_version != RELEASE_VERSION:
     print('RELEASE_VERSION is %s but --verify-version argument was %s.' % (
         RELEASE_VERSION, args.verify_version))
diff --git a/tools/metrics/DIR_METADATA b/tools/metrics/DIR_METADATA
index 1867dd61ee..feca9c13 100644
--- a/tools/metrics/DIR_METADATA
+++ b/tools/metrics/DIR_METADATA
@@ -1,11 +1,6 @@
-# Metadata information for this directory.
-#
-# For more information on DIR_METADATA files, see:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
-#
-# For the schema of this file, see Metadata message:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
-
-monorail {
+monorail: {
   component: "Internals>Metrics"
-}
\ No newline at end of file
+}
+buganizer_public: {
+  component_id: 1456863
+}
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 3dc5845..74bd19f 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -11281,6 +11281,7 @@
   <int value="4834" label="CSSCustomStateDeprecatedSyntax"/>
   <int value="4835" label="FullscreenAllowedByContentSetting"/>
   <int value="4836" label="SharedStorageAPI_CreateWorklet_Method"/>
+  <int value="4837" label="Canvas2DLayers"/>
 </enum>
 
 <enum name="FeaturePolicyFeature">
@@ -17811,8 +17812,6 @@
   <int value="-1338237485" label="HardwareSecureDecryptionExperiment:disabled"/>
   <int value="-1337433513" label="FilesGoogleDriveSettingsPage:disabled"/>
   <int value="-1337185440" label="enable-webvr"/>
-  <int value="-1336974270"
-      label="EnableBookmarkFoldersForAccountStorage:enabled"/>
   <int value="-1336110789" label="CastMediaRouteProvider:disabled"/>
   <int value="-1335360580" label="ReduceTransferSizeUpdatedIPC:enabled"/>
   <int value="-1335017208" label="KeyboardLockAPI:enabled"/>
@@ -19879,6 +19878,7 @@
   <int value="-373365335" label="JourneysOmniboxAction:disabled"/>
   <int value="-373233195" label="SyncUSSAutofillWalletData:enabled"/>
   <int value="-372171039" label="BlinkGenPropertyTrees:disabled"/>
+  <int value="-371771269" label="ShortcutsNotApps:enabled"/>
   <int value="-371321069" label="SyncPromoAfterSigninIntercept:enabled"/>
   <int value="-371103035" label="OmniboxTouchDownTriggerForPrefetch:enabled"/>
   <int value="-370876369" label="ContextualSearchLiteralTapSearch:enabled"/>
@@ -20104,6 +20104,7 @@
   <int value="-276907217" label="EventPath:enabled"/>
   <int value="-276260362" label="GamepadVibration:enabled"/>
   <int value="-276196379" label="ShimlessRMASkuDescription:enabled"/>
+  <int value="-276042128" label="ShortcutsNotApps:disabled"/>
   <int value="-275870837"
       label="OmniboxUIExperimentHideSuggestionUrlPath:enabled"/>
   <int value="-275619817"
@@ -20896,6 +20897,7 @@
   <int value="90673685" label="cast-mirroring-target-playout-delay"/>
   <int value="91916443" label="ManagedConfiguration:disabled"/>
   <int value="91938915" label="enable-suggestions-service"/>
+  <int value="92082387" label="WebAppUniversalInstall:disabled"/>
   <int value="92106937" label="CloseTabSuggestions:disabled"/>
   <int value="92327255" label="DisplayMoveWindowAccels:disabled"/>
   <int value="92466801" label="UploadOfficeToCloud:disabled"/>
@@ -22117,8 +22119,6 @@
   <int value="659871291"
       label="ClearCrossSiteCrossBrowsingContextGroupWindowName:enabled"/>
   <int value="661020875" label="AutofillSaveCardShowNoThanks:disabled"/>
-  <int value="661034193"
-      label="EnableBookmarkFoldersForAccountStorage:disabled"/>
   <int value="661537613" label="WebNotesPublish:enabled"/>
   <int value="662001716" label="ToolbarIphAndroid:disabled"/>
   <int value="662331859" label="ConversionMeasurement:enabled"/>
@@ -24911,6 +24911,7 @@
   <int value="1939413645" label="enable-invalid-cert-collection"/>
   <int value="1939884866" label="web-otp-backend"/>
   <int value="1940625534" label="NewOverviewUi:disabled"/>
+  <int value="1941193767" label="WebAppUniversalInstall:enabled"/>
   <int value="1941845443" label="ScrollableTabStrip:enabled"/>
   <int value="1942174659" label="ArcSyncInstallPriority:enabled"/>
   <int value="1942542793" label="CrosPrivacyHubV0:disabled"/>
diff --git a/tools/metrics/histograms/metadata/accessibility/histograms.xml b/tools/metrics/histograms/metadata/accessibility/histograms.xml
index 2349b75..46c8026c 100644
--- a/tools/metrics/histograms/metadata/accessibility/histograms.xml
+++ b/tools/metrics/histograms/metadata/accessibility/histograms.xml
@@ -1847,7 +1847,7 @@
 
 <histogram
     name="Accessibility.Performance.AXObjectCacheImpl.Incremental.{DataType}"
-    units="bytes" expires_after="2024-02-11">
+    units="bytes" expires_after="2024-07-21">
   <owner>kevers@chromium.org</owner>
   <owner>chrome-a11y-core@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index 8a92613f..c66572b5 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -1688,6 +1688,18 @@
   </summary>
 </histogram>
 
+<histogram name="Android.GridTabSwitcher.TimeToTabStateInitializedFromShown"
+    units="ms" expires_after="2024-06-30">
+  <owner>ckitagawa@chromium.org</owner>
+  <owner>clank-tab-dev@google.com</owner>
+  <summary>
+    Records the elapsed realtime in milliseconds between the tab switcher being
+    shown and the tab state being initialized. This is not recorded if the tab
+    state was already initialized. Recorded at most once per session once the
+    tab state is initialized.
+  </summary>
+</histogram>
+
 <histogram name="Android.HistoryPage.ClearBrowsingData.PerProfileType"
     enum="BrowserProfileType" expires_after="2024-06-30">
   <owner>roagarwal@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index 653748b..48553fb 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -133,9 +133,13 @@
       summary="Feature is trying to be updated from full server card checkout"/>
   <variant name="CheckoutLocalCard"
       summary="Feature is trying to be updated from local card checkout"/>
+  <variant name="CheckoutLocalIban"
+      summary="Feature is trying to be updated from local IBAN checkout"/>
   <variant name="CheckoutMaskedServerCard"
       summary="Feature is trying to be updated from masked server card
                checkout"/>
+  <variant name="CheckoutServerIban"
+      summary="Feature is trying to be updated from server IBAN checkout"/>
   <variant name="CheckoutVirtualCard"
       summary="Feature is trying to be updated from virtual card checkout"/>
   <variant name="SettingsPage"
diff --git a/tools/metrics/histograms/metadata/commerce/DIR_METADATA b/tools/metrics/histograms/metadata/commerce/DIR_METADATA
index 135b414c..f7647e29 100644
--- a/tools/metrics/histograms/metadata/commerce/DIR_METADATA
+++ b/tools/metrics/histograms/metadata/commerce/DIR_METADATA
@@ -1,11 +1,6 @@
-# Metadata information for this directory.
-#
-# For more information on DIR_METADATA files, see:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
-#
-# For the schema of this file, see Metadata message:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
-
-monorail {
+monorail: {
   component: "UI>Browser>Shopping"
-}
\ No newline at end of file
+}
+buganizer_public: {
+  component_id: 1457127
+}
diff --git a/tools/metrics/histograms/metadata/data/DIR_METADATA b/tools/metrics/histograms/metadata/data/DIR_METADATA
index 798c4cf..dc1bc71 100644
--- a/tools/metrics/histograms/metadata/data/DIR_METADATA
+++ b/tools/metrics/histograms/metadata/data/DIR_METADATA
@@ -1,11 +1,6 @@
-# Metadata information for this directory.
-#
-# For more information on DIR_METADATA files, see:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
-#
-# For the schema of this file, see Metadata message:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
-
-monorail {
+monorail: {
   component: "Internals>Network>DataProxy"
-}
\ No newline at end of file
+}
+buganizer_public: {
+  component_id: 1456388
+}
diff --git a/tools/metrics/histograms/metadata/enterprise/enums.xml b/tools/metrics/histograms/metadata/enterprise/enums.xml
index 81d3537..39ae524 100644
--- a/tools/metrics/histograms/metadata/enterprise/enums.xml
+++ b/tools/metrics/histograms/metadata/enterprise/enums.xml
@@ -2026,6 +2026,7 @@
   <int value="1210" label="RemoteAccessHostAllowUrlForwarding"/>
   <int value="1211" label="ScreenCaptureLocation"/>
   <int value="1212" label="AllowDomainsForAppsList"/>
+  <int value="1213" label="ChromeForTestingAllowed"/>
 </enum>
 
 <enum name="EnterprisePoliciesSources">
diff --git a/tools/metrics/histograms/metadata/image/histograms.xml b/tools/metrics/histograms/metadata/image/histograms.xml
index e186e8c..1d1c894 100644
--- a/tools/metrics/histograms/metadata/image/histograms.xml
+++ b/tools/metrics/histograms/metadata/image/histograms.xml
@@ -578,37 +578,6 @@
   </summary>
 </histogram>
 
-<histogram name="ImageLoader.Client.Cache.HitMiss" enum="BooleanCacheHit"
-    expires_after="2019-01-01">
-  <owner>tapted@chromium.org</owner>
-  <owner>src/ui/file_manager/OWNERS</owner>
-  <summary>
-    For each image load request that requested caching, records whether or not
-    it was found in the client-side cache. A hit means the request was not
-    forwarded to the ImageLoader extension.
-  </summary>
-</histogram>
-
-<histogram name="ImageLoader.Client.Cache.Usage" units="%"
-    expires_after="2019-01-01">
-  <owner>tapted@chromium.org</owner>
-  <owner>src/ui/file_manager/OWNERS</owner>
-  <summary>
-    Returns the percentage of the client-side cache that is used for loading
-    images, before they are sent to the ImageLoader extension. Expressed as a
-    percentage of ImageLoaderClient.CACHE_MEMORY_LIMIT (e.g. 20MB).
-  </summary>
-</histogram>
-
-<histogram name="ImageLoader.Client.Cached" enum="BooleanRequested"
-    expires_after="2019-01-01">
-  <owner>tapted@chromium.org</owner>
-  <owner>src/ui/file_manager/OWNERS</owner>
-  <summary>
-    For each image load request records whether or not it requested caching.
-  </summary>
-</histogram>
-
 </histograms>
 
 </histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/language/DIR_METADATA b/tools/metrics/histograms/metadata/language/DIR_METADATA
index 256e29a..4157a21 100644
--- a/tools/metrics/histograms/metadata/language/DIR_METADATA
+++ b/tools/metrics/histograms/metadata/language/DIR_METADATA
@@ -1,11 +1,6 @@
-# Metadata information for this directory.
-#
-# For more information on DIR_METADATA files, see:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
-#
-# For the schema of this file, see Metadata message:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
-
-monorail {
+monorail: {
   component: "UI>Browser>Language"
-}
\ No newline at end of file
+}
+buganizer_public: {
+  component_id: 1457105
+}
diff --git a/tools/metrics/histograms/metadata/navigation/enums.xml b/tools/metrics/histograms/metadata/navigation/enums.xml
index c2b54a9..232d942 100644
--- a/tools/metrics/histograms/metadata/navigation/enums.xml
+++ b/tools/metrics/histograms/metadata/navigation/enums.xml
@@ -1724,6 +1724,7 @@
   <int value="2" label="kReadingMetadataFileFailed"/>
   <int value="3" label="kParsingToProtoFailed"/>
   <int value="4" label="kErroneousSpec"/>
+  <int value="5" label="kErroneousSource"/>
 </enum>
 
 <enum name="UserAgentStringType">
diff --git a/tools/metrics/histograms/metadata/network/enums.xml b/tools/metrics/histograms/metadata/network/enums.xml
index 0d0fa5c..10dab4b8 100644
--- a/tools/metrics/histograms/metadata/network/enums.xml
+++ b/tools/metrics/histograms/metadata/network/enums.xml
@@ -1212,6 +1212,16 @@
   <int value="6" label="Unknown"/>
 </enum>
 
+<enum name="NetworkTechnologyMeterSubtype">
+  <int value="0" label="Ethernet"/>
+  <int value="1" label="WiFi"/>
+  <int value="2" label="WiFi Metered"/>
+  <int value="3" label="Cellular"/>
+  <int value="4" label="Cellular Metered"/>
+  <int value="5" label="Tether"/>
+  <int value="6" label="Tether Metered"/>
+</enum>
+
 <enum name="NotificationSuppressionState">
   <int value="0" label="NotSuppressed"/>
   <int value="1" label="UserSuppressed"/>
diff --git a/tools/metrics/histograms/metadata/network/histograms.xml b/tools/metrics/histograms/metadata/network/histograms.xml
index 37070ec..1d36a9f9 100644
--- a/tools/metrics/histograms/metadata/network/histograms.xml
+++ b/tools/metrics/histograms/metadata/network/histograms.xml
@@ -460,6 +460,20 @@
   </token>
 </histogram>
 
+<histogram name="Network.Ash.DefaultNetwork.MeterSubtype"
+    enum="NetworkTechnologyMeterSubtype" expires_after="2025-01-28">
+  <owner>hsuregan@google.com</owner>
+  <owner>cros-connectivity@google.com</owner>
+  <summary>
+    Tracks the technology type of the default network, which considers the
+    technology type and whether the network is metered. The default network here
+    means the first default/active non VPN network. Note that this metric is
+    recorded whenever the default network changes or when the metered property
+    changes. Note that if the default/active network drops and then reconnects
+    without another network taking its place, this metric will not be emitted.
+  </summary>
+</histogram>
+
 <histogram name="Network.Ash.Hotspot.SetConfig.OperationResult"
     enum="HotspotSetConfigResult" expires_after="2024-06-02">
   <owner>jiajunz@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/optimization/DIR_METADATA b/tools/metrics/histograms/metadata/optimization/DIR_METADATA
index 8ec3a933..c23828c 100644
--- a/tools/metrics/histograms/metadata/optimization/DIR_METADATA
+++ b/tools/metrics/histograms/metadata/optimization/DIR_METADATA
@@ -1,11 +1,6 @@
-# Metadata information for this directory.
-#
-# For more information on DIR_METADATA files, see:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
-#
-# For the schema of this file, see Metadata message:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
-
-monorail {
+monorail: {
   component: "Internals>OptimizationGuide"
-}
\ No newline at end of file
+}
+buganizer_public: {
+  component_id: 1456757
+}
diff --git a/tools/metrics/histograms/metadata/prefetch/DIR_METADATA b/tools/metrics/histograms/metadata/prefetch/DIR_METADATA
index 9c19fed..64ed0d3 100644
--- a/tools/metrics/histograms/metadata/prefetch/DIR_METADATA
+++ b/tools/metrics/histograms/metadata/prefetch/DIR_METADATA
@@ -1,11 +1,6 @@
-# Metadata information for this directory.
-#
-# For more information on DIR_METADATA files, see:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
-#
-# For the schema of this file, see Metadata message:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
-
-monorail {
+monorail: {
   component: "Internals>Preload>PrefetchProxy"
 }
+buganizer_public: {
+  component_id: 1456735
+}
diff --git a/tools/metrics/histograms/metadata/preloading/DIR_METADATA b/tools/metrics/histograms/metadata/preloading/DIR_METADATA
index f0c1039..2ef4f22 100644
--- a/tools/metrics/histograms/metadata/preloading/DIR_METADATA
+++ b/tools/metrics/histograms/metadata/preloading/DIR_METADATA
@@ -1,11 +1,6 @@
-# Metadata information for this directory.
-#
-# For more information on DIR_METADATA files, see:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
-#
-# For the schema of this file, see Metadata message:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
-
-monorail {
+monorail: {
   component: "Internals>Preload"
 }
+buganizer_public: {
+  component_id: 1457018
+}
diff --git a/tools/metrics/histograms/metadata/privacy/histograms.xml b/tools/metrics/histograms/metadata/privacy/histograms.xml
index 419f816d..44fca19 100644
--- a/tools/metrics/histograms/metadata/privacy/histograms.xml
+++ b/tools/metrics/histograms/metadata/privacy/histograms.xml
@@ -692,7 +692,7 @@
 </histogram>
 
 <histogram
-    name="PrivacySandbox.AggregationService.Storage.Sql.RequestDelayFromUpdatedReportTime"
+    name="PrivacySandbox.AggregationService.Storage.Sql.RequestDelayFromUpdatedReportTime2"
     units="ms" expires_after="2024-07-21">
   <owner>dmcardle@chromium.org</owner>
   <owner>alexmt@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/quick_answers/OWNERS b/tools/metrics/histograms/metadata/quick_answers/OWNERS
index 8961bd82..acafdc2 100644
--- a/tools/metrics/histograms/metadata/quick_answers/OWNERS
+++ b/tools/metrics/histograms/metadata/quick_answers/OWNERS
@@ -2,4 +2,5 @@
 
 # Prefer sending CLs to the owners listed below.
 # Use chromium-metrics-reviews@google.com as a backup.
+angelaxiao@chromium.org
 xiaohuic@chromium.org
diff --git a/tools/metrics/histograms/metadata/quick_answers/histograms.xml b/tools/metrics/histograms/metadata/quick_answers/histograms.xml
index a755776..b587bde 100644
--- a/tools/metrics/histograms/metadata/quick_answers/histograms.xml
+++ b/tools/metrics/histograms/metadata/quick_answers/histograms.xml
@@ -50,7 +50,7 @@
 
 <histogram
     name="QuickAnswers.ActiveImpression.Duration{QuickAnswersClickResultType}"
-    units="ms" expires_after="2024-02-20">
+    units="ms" expires_after="2025-02-01">
   <owner>angelaxiao@chromium.org</owner>
   <owner>yawano@google.com</owner>
   <owner>llin@google.com</owner>
@@ -77,7 +77,7 @@
 </histogram>
 
 <histogram name="QuickAnswers.Click.Duration{QuickAnswersClickResultType}"
-    units="ms" expires_after="2023-10-15">
+    units="ms" expires_after="2025-02-01">
   <owner>angelaxiao@chromium.org</owner>
   <owner>yawano@google.com</owner>
   <owner>llin@google.com</owner>
@@ -85,6 +85,8 @@
   <summary>
     For every quick answer click event, records the duration between fetch
     finish and user clicks. ChromeOS only. {QuickAnswersClickResultType}
+    Warning: this histogram was expired from 2023-10-15 to 2024-02-07; data may
+    be missing.
   </summary>
   <token key="QuickAnswersClickResultType"
       variants="QuickAnswersClickResultType">
@@ -107,14 +109,15 @@
 </histogram>
 
 <histogram name="QuickAnswers.ContextMenu.Close.Duration{InteractionType}"
-    units="ms" expires_after="2023-10-31">
+    units="ms" expires_after="2025-02-01">
   <owner>angelaxiao@chromium.org</owner>
   <owner>yawano@google.com</owner>
   <owner>croissant-eng@chromium.org</owner>
   <summary>
     Records the amount of time the context menu was shown before close if the
     user {InteractionType} (via clicking) with the context menu. Recorded on
-    ChromeOS only.
+    ChromeOS only. Warning: this histogram was expired from 2023-10-31 to
+    2024-02-07; data may be missing.
   </summary>
   <token key="InteractionType">
     <variant name="WithClick" summary="has interacted"/>
@@ -123,103 +126,113 @@
 </histogram>
 
 <histogram name="QuickAnswers.DictionaryIntent.Language" enum="LanguageName"
-    expires_after="2023-07-01">
+    expires_after="2025-02-01">
   <owner>angelaxiao@chromium.org</owner>
   <owner>yawano@google.com</owner>
   <owner>croissant-eng@chromium.org</owner>
   <summary>
     For Quick answers fetch, records the query text language of dictionary
-    intent generated on device. ChromeOS only.
+    intent generated on device. ChromeOS only. Warning: this histogram was
+    expired from 2023-07-01 to 2024-02-07; data may be missing.
   </summary>
 </histogram>
 
 <histogram name="QuickAnswers.DictionaryIntent.Source"
-    enum="QuickAnswersDictionaryIntentSource" expires_after="2023-07-01">
+    enum="QuickAnswersDictionaryIntentSource" expires_after="2025-02-01">
   <owner>angelaxiao@chromium.org</owner>
   <owner>yawano@google.com</owner>
   <owner>croissant-eng@chromium.org</owner>
   <summary>
     For Quick answers fetch, records the source type of dictionary intent
-    generated on device. ChromeOS only.
+    generated on device. ChromeOS only. Warning: this histogram was expired from
+    2023-07-01 to 2024-02-07; data may be missing.
   </summary>
 </histogram>
 
 <histogram name="QuickAnswers.ExitPoint" enum="QuickAnswersExitPoint"
-    expires_after="2023-08-15">
+    expires_after="2025-02-01">
   <owner>angelaxiao@chromium.org</owner>
   <owner>yawano@google.com</owner>
   <owner>croissant-eng@chromium.org</owner>
   <summary>
     Record the Quick Answers exit point point when the Quick Answers UI is
-    dismissed. ChromeOS only.
+    dismissed. ChromeOS only. Warning: this histogram was expired from
+    2023-08-15 to 2024-02-07; data may be missing.
   </summary>
 </histogram>
 
 <histogram name="QuickAnswers.FeatureEnabled" enum="BooleanEnabled"
-    expires_after="2023-10-15">
+    expires_after="2025-02-01">
   <owner>angelaxiao@chromium.org</owner>
   <owner>yawano@google.com</owner>
   <owner>croissant-eng@chromium.org</owner>
   <summary>
     Record the Quick Answers feature enabled status when the first user session
-    starts. ChromeOS only.
+    starts. ChromeOS only. Warning: this histogram was expired from 2023-10-15
+    to 2024-02-07; data may be missing.
   </summary>
 </histogram>
 
 <histogram name="QuickAnswers.Intent" enum="QuickAnswersIntentType"
-    expires_after="2023-11-12">
+    expires_after="2025-02-01">
   <owner>angelaxiao@chromium.org</owner>
   <owner>yawano@google.com</owner>
   <owner>llin@google.com</owner>
   <owner>croissant-eng@chromium.org</owner>
   <summary>
     For quick answer fetch, records the intent generated on-device. ChromeOS
-    only.
+    only. Warning: this histogram was expired from 2023-11-12 to 2024-02-07;
+    data may be missing.
   </summary>
 </histogram>
 
 <histogram name="QuickAnswers.Loading.Duration" units="ms"
-    expires_after="2023-12-01">
+    expires_after="2025-02-01">
   <owner>angelaxiao@chromium.org</owner>
   <owner>yawano@google.com</owner>
   <owner>llin@google.com</owner>
   <owner>croissant-eng@chromium.org</owner>
   <summary>
     For every quick answers fetch, records the duration between fetch start and
-    fetch finish. ChromeOS only.
+    fetch finish. ChromeOS only. Warning: this histogram was expired from
+    2023-12-01 to 2024-02-07; data may be missing.
   </summary>
 </histogram>
 
 <histogram name="QuickAnswers.Loading.Status" enum="QuickAnswersLoadStatus"
-    expires_after="2024-01-14">
+    expires_after="2025-02-01">
   <owner>angelaxiao@chromium.org</owner>
   <owner>yawano@google.com</owner>
   <owner>llin@google.com</owner>
   <owner>croissant-eng@chromium.org</owner>
   <summary>
     For every quick answers fetch, records the result status. ChromeOS only.
+    Warning: this histogram was expired from 2024-01-14 to 2024-02-07; data may
+    be missing.
   </summary>
 </histogram>
 
 <histogram name="QuickAnswers.NetworkError.IntentType"
-    enum="QuickAnswersIntentType" expires_after="2023-10-08">
+    enum="QuickAnswersIntentType" expires_after="2025-02-01">
   <owner>angelaxiao@chromium.org</owner>
   <owner>yawano@google.com</owner>
   <owner>croissant-eng@chromium.org</owner>
   <summary>
     Records the intent type when network error occurs during the quick answers
-    fetch. ChromeOS only.
+    fetch. ChromeOS only. Warning: this histogram was expired from 2023-10-08 to
+    2024-02-07; data may be missing.
   </summary>
 </histogram>
 
 <histogram name="QuickAnswers.NetworkError.ResponseCode.{IntentType}"
-    enum="HttpResponseCode" expires_after="2023-10-21">
+    enum="HttpResponseCode" expires_after="2025-02-01">
   <owner>angelaxiao@chromium.org</owner>
   <owner>yawano@google.com</owner>
   <owner>croissant-eng@chromium.org</owner>
   <summary>
     Records the Http response codes when network error occurs when fetching
-    {IntentType} results. ChromeOS only.
+    {IntentType} results. ChromeOS only. Warning: this histogram was expired
+    from 2023-10-21 to 2024-02-07; data may be missing.
   </summary>
   <token key="IntentType">
     <variant name="Definition" summary="definition"/>
@@ -229,13 +242,15 @@
 </histogram>
 
 <histogram name="QuickAnswers.RequestTextLength.{IntentType}"
-    units="characters" expires_after="2023-08-15">
+    units="characters" expires_after="2025-02-01">
   <owner>angelaxiao@chromium.org</owner>
   <owner>yawano@google.com</owner>
   <owner>croissant-eng@chromium.org</owner>
   <summary>
     For every quick answer request, records the length of the selected text that
-    is being used for fetching {IntentType} results. ChromeOS only.
+    is being used for fetching {IntentType} results. ChromeOS only. Warning:
+    this histogram was expired from 2023-08-15 to 2024-02-07; data may be
+    missing.
   </summary>
   <token key="IntentType">
     <variant name="Definition" summary="definition"/>
@@ -257,7 +272,7 @@
 </histogram>
 
 <histogram name="QuickAnswers.Result.Duration{QuickAnswersClickResultType}"
-    units="ms" expires_after="2023-10-31">
+    units="ms" expires_after="2025-02-01">
   <owner>angelaxiao@chromium.org</owner>
   <owner>yawano@google.com</owner>
   <owner>llin@google.com</owner>
@@ -265,6 +280,8 @@
   <summary>
     For every quick answer result received event, records the duration between
     fetch start and fetch finish. ChromeOS only. {QuickAnswersClickResultType}
+    Warning: this histogram was expired from 2023-10-31 to 2024-02-07; data may
+    be missing.
   </summary>
   <token key="QuickAnswersClickResultType"
       variants="QuickAnswersClickResultType">
@@ -297,25 +314,28 @@
 </histogram>
 
 <histogram name="QuickAnswers.V2.Consent" units="impressions"
-    expires_after="2023-10-22">
+    expires_after="2025-02-01">
   <owner>angelaxiao@chromium.org</owner>
   <owner>yawano@google.com</owner>
   <owner>croissant-eng@chromium.org</owner>
   <summary>
     For every quick answers consent impression, records how many times the user
-    has seen the consent. ChromeOS only.
+    has seen the consent. ChromeOS only. Warning: this histogram was expired
+    from 2023-10-22 to 2024-02-07; data may be missing.
   </summary>
 </histogram>
 
 <histogram
     name="QuickAnswers.V2.Consent.Duration{QuickAnswersV2ConsentResultType}"
-    units="ms" expires_after="2023-08-15">
+    units="ms" expires_after="2025-02-01">
   <owner>angelaxiao@chromium.org</owner>
   <owner>yawano@google.com</owner>
   <owner>croissant-eng@chromium.org</owner>
   <summary>
     For every quick answers consent result event, records how long the user has
     seen the consent before. ChromeOS only. {QuickAnswersV2ConsentResultType}
+    Warning: this histogram was expired from 2023-08-15 to 2024-02-07; data may
+    be missing.
   </summary>
   <token key="QuickAnswersV2ConsentResultType"
       variants="QuickAnswersV2ConsentResultType"/>
@@ -323,14 +343,15 @@
 
 <histogram
     name="QuickAnswers.V2.Consent.Impression{QuickAnswersV2ConsentResultType}"
-    units="impressions" expires_after="2023-08-15">
+    units="impressions" expires_after="2025-02-01">
   <owner>angelaxiao@chromium.org</owner>
   <owner>yawano@google.com</owner>
   <owner>croissant-eng@chromium.org</owner>
   <summary>
     For every quick answers consent result event, records how many times the
     user has seen the consent before. ChromeOS only.
-    {QuickAnswersV2ConsentResultType}
+    {QuickAnswersV2ConsentResultType} Warning: this histogram was expired from
+    2023-08-15 to 2024-02-07; data may be missing.
   </summary>
   <token key="QuickAnswersV2ConsentResultType"
       variants="QuickAnswersV2ConsentResultType"/>
diff --git a/tools/metrics/histograms/metadata/signin/enums.xml b/tools/metrics/histograms/metadata/signin/enums.xml
index 681060f..1f255ca9 100644
--- a/tools/metrics/histograms/metadata/signin/enums.xml
+++ b/tools/metrics/histograms/metadata/signin/enums.xml
@@ -322,7 +322,7 @@
       label="Cookie rotation success (request completed before cookie fresh
              notification)"/>
   <int value="2" label="Cookie rotation failure"/>
-  <int value="3" label="Network connection offline"/>
+  <int value="3" label="(obsolete) Network connection offline"/>
   <int value="4" label="Timeout"/>
   <int value="5" label="Shutdown or session termination"/>
   <int value="6" label="Cookie is already fresh"/>
diff --git a/tools/metrics/histograms/metadata/sync/DIR_METADATA b/tools/metrics/histograms/metadata/sync/DIR_METADATA
index 5099a4e6..1908696 100644
--- a/tools/metrics/histograms/metadata/sync/DIR_METADATA
+++ b/tools/metrics/histograms/metadata/sync/DIR_METADATA
@@ -1,11 +1,6 @@
-# Metadata information for this directory.
-#
-# For more information on DIR_METADATA files, see:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
-#
-# For the schema of this file, see Metadata message:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
-
-monorail {
+monorail: {
   component: "Services>Sync"
-}
\ No newline at end of file
+}
+buganizer_public: {
+  component_id: 1457192
+}
diff --git a/tools/metrics/histograms/metadata/tab/histograms.xml b/tools/metrics/histograms/metadata/tab/histograms.xml
index c5d5dd5..777d0a9 100644
--- a/tools/metrics/histograms/metadata/tab/histograms.xml
+++ b/tools/metrics/histograms/metadata/tab/histograms.xml
@@ -577,6 +577,17 @@
   </summary>
 </histogram>
 
+<histogram name="TabGroups.NumberOfRootIdsFixed" units="groups"
+    expires_after="2024-07-14">
+  <owner>ckitagawa@chromium.org</owner>
+  <owner>clank-tab-dev@chromium.org</owner>
+  <summary>
+    [Android] Records the number of tab groups where the root identifier did not
+    match a tab from its tab group and the root identifier had to be corrected.
+    Emitted once per start during tab state initialization.
+  </summary>
+</histogram>
+
 <histogram name="TabGroups.SavedTabGroupAge" units="minutes"
     expires_after="2024-07-07">
   <owner>dljames@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/translate/DIR_METADATA b/tools/metrics/histograms/metadata/translate/DIR_METADATA
index 6734540c..e3931561 100644
--- a/tools/metrics/histograms/metadata/translate/DIR_METADATA
+++ b/tools/metrics/histograms/metadata/translate/DIR_METADATA
@@ -1,11 +1,6 @@
-# Metadata information for this directory.
-#
-# For more information on DIR_METADATA files, see:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
-#
-# For the schema of this file, see Metadata message:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
-
-monorail {
+monorail: {
   component: "UI>Browser>Language>Translate"
-}
\ No newline at end of file
+}
+buganizer_public: {
+  component_id: 1457298
+}
diff --git a/tools/metrics/histograms/metadata/trusted_vault/DIR_METADATA b/tools/metrics/histograms/metadata/trusted_vault/DIR_METADATA
index 5099a4e6..1908696 100644
--- a/tools/metrics/histograms/metadata/trusted_vault/DIR_METADATA
+++ b/tools/metrics/histograms/metadata/trusted_vault/DIR_METADATA
@@ -1,11 +1,6 @@
-# Metadata information for this directory.
-#
-# For more information on DIR_METADATA files, see:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
-#
-# For the schema of this file, see Metadata message:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
-
-monorail {
+monorail: {
   component: "Services>Sync"
-}
\ No newline at end of file
+}
+buganizer_public: {
+  component_id: 1457192
+}
diff --git a/tools/metrics/histograms/metadata/xr/DIR_METADATA b/tools/metrics/histograms/metadata/xr/DIR_METADATA
index 8177548..fbd0b29 100644
--- a/tools/metrics/histograms/metadata/xr/DIR_METADATA
+++ b/tools/metrics/histograms/metadata/xr/DIR_METADATA
@@ -1,11 +1,6 @@
-# Metadata information for this directory.
-#
-# For more information on DIR_METADATA files, see:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
-#
-# For the schema of this file, see Metadata message:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
-
-monorail {
+monorail: {
   component: "Internals>XR"
 }
+buganizer_public: {
+  component_id: 1456683
+}
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index daf22b3..4b37447f 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -16618,6 +16618,16 @@
       Cluster is of a boosted category.
     </summary>
   </metric>
+  <metric name="BelongsToMostSeenCategory" enum="Boolean">
+    <summary>
+      Cluster is of the most frequently seen category.
+    </summary>
+  </metric>
+  <metric name="BelongsToMostUsedCategory" enum="Boolean">
+    <summary>
+      Cluster is of the most frequently used category.
+    </summary>
+  </metric>
   <metric name="DidDisableModule" enum="Boolean">
     <summary>
       Whether the user disabled the module.
@@ -16650,11 +16660,28 @@
       bucketed using GetExponentialBucketMin with a spacing of 1.3.
     </summary>
   </metric>
+  <metric name="MostFrequentSeenCategoryCount">
+    <summary>
+      The number of times the most frequently seen category by a user occurred
+      in a fixed time period.
+    </summary>
+  </metric>
+  <metric name="MostFrequentUsedCategoryCount">
+    <summary>
+      The number of times the most frequently used category by a user occurred
+      in a fixed time period.
+    </summary>
+  </metric>
   <metric name="NumAbandonedCarts">
     <summary>
       Number of abandoned carts associated with the cluster.
     </summary>
   </metric>
+  <metric name="NumAssociatedCategories">
+    <summary>
+      Number of categories associated with the cluster.
+    </summary>
+  </metric>
   <metric name="NumTimesSeenLast24h">
     <summary>
       Number of times the cluster was seen by the user in the last 24 hours.
@@ -18952,7 +18979,9 @@
   <metric name="PaintTiming.LargestContentfulPaintImageDiscoveryTime">
     <summary>
       Measures the time in milliseconds from navigation timing's navigation
-      start to the time when the image LCP element is requested. This metric is
+      start to the time when the image LCP element is requested. If the image
+      LCP element is from a subframe, the difference in the navigation start of
+      the main frame and that of the subframe is accounted for. This metric is
       not recorded when the LCP element is text or video.
     </summary>
   </metric>
@@ -18962,7 +18991,9 @@
       start to the time when the LCP element(image) finishes loading. For
       animated images, this is when the image resource finishes loading
       completely, not the first frame time. See web.dev/optimize-lcp for more
-      details. This metric is not recorded when the LCP element is text or
+      details. If the image LCP element is from a subframe, the difference in
+      the navaigation start of the main frame and that of the subframe is
+      accounted for. This metric is not recorded when the LCP element is text or
       video.
     </summary>
   </metric>
@@ -18970,7 +19001,9 @@
     <summary>
       Measures the time in milliseconds from navigation timing's navigation
       start to the time when the LCP element(image) starts loading. See
-      web.dev/optimize-lcp for more details. This metric is not recorded when
+      web.dev/optimize-lcp for more details. If the image LCP element is from a
+      subframe, the difference in the navaigation start of the main frame and
+      that of the subframe is accounted for. This metric is not recorded when
       the LCP element is text or video.
     </summary>
   </metric>
diff --git a/tools/perf/DIR_METADATA b/tools/perf/DIR_METADATA
index 628bfe71..03b2712 100644
--- a/tools/perf/DIR_METADATA
+++ b/tools/perf/DIR_METADATA
@@ -1,11 +1,6 @@
-# Metadata information for this directory.
-#
-# For more information on DIR_METADATA files, see:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
-#
-# For the schema of this file, see Metadata message:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
-
-monorail {
+monorail: {
   component: "Speed>Benchmarks"
-}
\ No newline at end of file
+}
+buganizer_public: {
+  component_id: 1457389
+}
diff --git a/tools/perf/contrib/shared_storage/README.md b/tools/perf/contrib/shared_storage/README.md
index 191dc89..c310f4b 100644
--- a/tools/perf/contrib/shared_storage/README.md
+++ b/tools/perf/contrib/shared_storage/README.md
@@ -40,7 +40,9 @@
         Only use stories whose names match the given filter regexp.
 * `--iterations=ITERATIONS`
         Override the default number of action iterations per story with the
-        given number.
+        given number. Default is 10. Maximum allowed is 10.
+* `--pageset-repeat=PAGESET_REPEAT`
+        Number of times to repeat the entire story set. Default is 10.
 * `--verbose-cpu-metrics`
         Enables non-UMA CPU metrics.
 * `--verbose-memory-metrics`
@@ -56,5 +58,5 @@
 
 For example, a modified version of the original benchmark command is:
 ```bash
-tools/perf/run_benchmark shared_storage.small --browser=system --story-filter=Append --iterations=5 --verbose-cpu-metrics --verbose-memory-metrics --verbose
+tools/perf/run_benchmark shared_storage.small --browser=system --story-filter=Append --iterations=5 --pageset-repeat=1 --verbose-cpu-metrics --verbose-memory-metrics --verbose
 ```
diff --git a/tools/perf/contrib/shared_storage/page_set.py b/tools/perf/contrib/shared_storage/page_set.py
index 5fda3fd..c879396 100644
--- a/tools/perf/contrib/shared_storage/page_set.py
+++ b/tools/perf/contrib/shared_storage/page_set.py
@@ -8,6 +8,7 @@
 import py_utils
 import six
 from socket import timeout
+import time
 
 from telemetry import story
 from telemetry.internal.backends.chrome_inspector import websocket
@@ -18,6 +19,9 @@
 _ACTION_TIMEOUT = 2
 _NAVIGATION_TIMEOUT = 90
 
+# Time in seconds to sleep at end of story to let histograms finish recording.
+_SLEEP_TIME = 1
+
 # Placeholder substring for index value in the action script template.
 _INDEX_PLACEHOLDER = '{{ index }}'
 
@@ -162,6 +166,9 @@
             _NAVIGATION_TIMEOUT)
         action_runner.tab.ClearSharedStorageNotifications()
 
+    # Sleep for a little to allow histograms to finish recording.
+    time.sleep(_SLEEP_TIME)
+
     if self._enable_memory_metric:
       action_runner.MeasureMemory(deterministic_mode=True)
 
diff --git a/tools/perf/contrib/shared_storage/pages.py b/tools/perf/contrib/shared_storage/pages.py
index 3223a287..49098b9 100644
--- a/tools/perf/contrib/shared_storage/pages.py
+++ b/tools/perf/contrib/shared_storage/pages.py
@@ -79,6 +79,7 @@
   }, {
       'type': 'documentClear'
   }]
+  RENAVIGATE_AFTER_ACTION = True
 
 
 class SharedStorageWorkletRunSetStory(SharedStorageStory):
diff --git a/tools/perf/contrib/shared_storage/shared_storage.py b/tools/perf/contrib/shared_storage/shared_storage.py
index f65be15..d8da0433 100644
--- a/tools/perf/contrib/shared_storage/shared_storage.py
+++ b/tools/perf/contrib/shared_storage/shared_storage.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import logging
+
 from benchmarks import memory
 from contrib.shared_storage import page_set
 from core import perf_benchmark
@@ -21,7 +23,6 @@
     "Storage.SharedStorage.Document.Timing.SelectURL",
     "Storage.SharedStorage.Document.Timing.SelectURL.ExecutedInWorklet",
     "Storage.SharedStorage.Document.Timing.Set",
-    "Storage.SharedStorage.OnShutdown.NumSqlErrors",
     "Storage.SharedStorage.Worklet.Timing.Append",
     "Storage.SharedStorage.Worklet.Timing.Clear",
     "Storage.SharedStorage.Worklet.Timing.Delete",
@@ -34,9 +35,22 @@
     "Storage.SharedStorage.Worklet.Timing.Values.Next",
 ]
 
+# Features to enable via command line.
+_ENABLED_FEATURES = [
+    'SharedStorageAPI:SharedStorageDebugDisabledMessage/true',
+    'FencedFrames:implementation_type/mparch', 'FencedFramesDefaultMode',
+    'PrivacySandboxAdsAPIsOverride', 'DefaultAllowPrivacySandboxAttestations'
+]
+
 # Default number of times to run each shared storage action in a story.
 _DEFAULT_NUM_ITERATIONS = 10
 
+# Maximum number of times to run each shared storage action in a story.
+_MAX_NUM_ITERATIONS = 10
+
+# Default number of times to run each story.
+_DEFAULT_NUM_REPEAT = 10
+
 # Use maximum allowed trace buffer size (in KB).
 _TRACE_BUFFER_SIZE = 2**32 - 1
 
@@ -48,6 +62,8 @@
   iterations = _DEFAULT_NUM_ITERATIONS
   verbosity = 0
 
+  options = {'pageset_repeat': _DEFAULT_NUM_REPEAT}
+
   @classmethod
   def AddBenchmarkCommandLineArgs(cls, parser):
     parser.add_option('--user-agent',
@@ -61,12 +77,14 @@
     parser.add_option('--verbose-memory-metrics',
                       action='store_true',
                       help='Enables non-UMA memory metrics.')
-    parser.add_option(
-        '--iterations',
-        action='store',
-        type='int',
-        default=_DEFAULT_NUM_ITERATIONS,
-        help='Number of times to repeat the test action for each test run.')
+    iter_help = ('Number of times (default %d, max %d)' %
+                 (_DEFAULT_NUM_ITERATIONS, _MAX_NUM_ITERATIONS))
+    iter_help += ' to repeat action for each story run.'
+    parser.add_option('--iterations',
+                      action='store',
+                      type='int',
+                      default=_DEFAULT_NUM_ITERATIONS,
+                      help=iter_help)
 
   @classmethod
   def ProcessCommandLineArgs(cls, parser, args):
@@ -75,6 +93,10 @@
     cls.iterations = args.iterations
     if cls.iterations <= 0:
       raise ValueError('Got invalid value %d for iterations' % cls.iterations)
+    if cls.iterations > _MAX_NUM_ITERATIONS:
+      logging.warning('The maximum allowed number of iterations is 10. ' +
+                      'Increase pageset_repeat instead.')
+      cls.iterations = _MAX_NUM_ITERATIONS
 
   def SetExtraBrowserOptions(self, options):
     # `options` is an instance of `browser_options.BrowserOptions`.
@@ -82,13 +104,8 @@
       memory.SetExtraBrowserOptionsForMemoryMeasurement(options)
 
     options.AppendExtraBrowserArgs([
-        ''.join([
-            '--enable-features=',
-            'SharedStorageAPI:SharedStorageDebugDisabledMessage/true,',
-            'FencedFrames:implementation_type/mparch,',
-            'FencedFramesDefaultMode,', 'PrivacySandboxAdsAPIsOverride,',
-            'DefaultAllowPrivacySandboxAttestations'
-        ]), '--enable-privacy-sandbox-ads-apis'
+        '--enable-features=' + ','.join(_ENABLED_FEATURES),
+        '--enable-privacy-sandbox-ads-apis'
     ])
 
   def CustomizeOptions(self, finder_options, possible_browser=None):
@@ -103,7 +120,8 @@
     self.verbosity = finder_options.verbosity
 
   def CreateCoreTimelineBasedMeasurementOptions(self):
-    category_filter = chrome_trace_category_filter.CreateLowOverheadFilter()
+    category_filter = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        filter_string="benchmark")
     if self.verbose_memory_metrics:
       tbm_options = memory.CreateCoreTimelineBasedMemoryMeasurementOptions()
 
diff --git a/tools/perf/contrib/vr_benchmarks/DIR_METADATA b/tools/perf/contrib/vr_benchmarks/DIR_METADATA
index b51319c6..738cdef 100644
--- a/tools/perf/contrib/vr_benchmarks/DIR_METADATA
+++ b/tools/perf/contrib/vr_benchmarks/DIR_METADATA
@@ -1,12 +1,7 @@
-# Metadata information for this directory.
-#
-# For more information on DIR_METADATA files, see:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
-#
-# For the schema of this file, see Metadata message:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
-
-monorail {
+monorail: {
   component: "Internals>XR>VR"
 }
-team_email: "xr-dev@chromium.org"
\ No newline at end of file
+team_email: "xr-dev@chromium.org"
+buganizer_public: {
+  component_id: 1456705
+}
diff --git a/tools/perf/core/perfetto_binary_roller/DIR_METADATA b/tools/perf/core/perfetto_binary_roller/DIR_METADATA
index 5fac972..73ed78a 100644
--- a/tools/perf/core/perfetto_binary_roller/DIR_METADATA
+++ b/tools/perf/core/perfetto_binary_roller/DIR_METADATA
@@ -1,11 +1,6 @@
-# Metadata information for this directory.
-#
-# For more information on DIR_METADATA files, see:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
-#
-# For the schema of this file, see Metadata message:
-#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
-
-monorail {
+monorail: {
   component: "Speed>Tracing"
-}
\ No newline at end of file
+}
+buganizer_public: {
+  component_id: 1457213
+}
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index db3fc99c..75c15de 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,24 +5,24 @@
             "full_remote_path": "perfetto-luci-artifacts/v42.0/linux-arm64/trace_processor_shell"
         },
         "win": {
-            "hash": "a9083e5c3e683195f256e93d970b2667aadbb2dc",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/450c05cd3bd3e596f4757cca3217182d764b9ba9/trace_processor_shell.exe"
+            "hash": "59e4abe43023a57a761547b00f0fe42414594049",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/9c3cfdb0dacc59243527a5cef0989a3db5c30c69/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "46739eeb4b8f2a65a8a0aac57743767e6407f7bb",
             "full_remote_path": "perfetto-luci-artifacts/v42.0/linux-arm/trace_processor_shell"
         },
         "mac": {
-            "hash": "94966647fc1513e66be81163d51d25d8e6373c8c",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/12e755cd67e88a5dee90096cd49d95405252fcea/trace_processor_shell"
+            "hash": "d5f9f140e7d3ae096bba9b9008cb9589bd5912b6",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/74cdba3b28c2a48bb3c5b5cae32812eb6c76a8da/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "789f24a091d50faafdd5d7c5096bb923d073f138",
             "full_remote_path": "perfetto-luci-artifacts/v42.0/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "e273b68c92e32a067651e612cd156e534922fa46",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/12e755cd67e88a5dee90096cd49d95405252fcea/trace_processor_shell"
+            "hash": "e483814f76f9b7eec355f7b8a0ef6a2d191c25bd",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/74cdba3b28c2a48bb3c5b5cae32812eb6c76a8da/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/polymer/css_to_wrapper.py b/tools/polymer/css_to_wrapper.py
index f604ded..8e18048 100644
--- a/tools/polymer/css_to_wrapper.py
+++ b/tools/polymer/css_to_wrapper.py
@@ -75,6 +75,13 @@
     'vars-lit': _LIT_VARS_TEMPLATE,
 }
 
+# A suffix used for style files that are copies of Polymer styles ported into
+# Lit. It is treated specially below so that the Polymer file acts as a source
+# of truth, to avoid duplication while both files styles need to be available.
+# TODO(crbug.com/40943652): Remove special handling when having the same styles
+# available in both Polymer and Lit is no longer needed.
+_LIT_SUFFIX = "_lit.css"
+
 
 def _parse_style_line(line, metadata):
   include_match = re.search(_INCLUDE_REGEX, line)
@@ -218,9 +225,21 @@
     # only exist there.
     metadata = _extract_metadata(path.join(in_folder, in_file))
 
-    # Extract the CSS content from either the original or the minified files.
-    content = _extract_content(path.join(wrapper_in_folder, in_file), metadata,
-                               args.minify)
+    content = ''
+
+    if in_file.endswith(_LIT_SUFFIX):
+      # Treat the _LIT_SUFFIX in a special way, so that the CSS content is
+      # actually extracted from the equivalent Polymer file instead.
+      polymer_in_file = in_file.replace(_LIT_SUFFIX, '.css')
+      assert polymer_in_file in args.in_files
+      polymer_metadata = _extract_metadata(path.join(in_folder,
+                                                     polymer_in_file))
+      content = _extract_content(path.join(wrapper_in_folder, polymer_in_file),
+                                 polymer_metadata, args.minify)
+    else:
+      # Extract the CSS content from either the original or the minified files.
+      content = _extract_content(path.join(wrapper_in_folder, in_file),
+                                 metadata, args.minify)
 
     # Extract the URL scheme that should be used for absolute URL imports.
     scheme = None
diff --git a/tools/polymer/css_to_wrapper_test.py b/tools/polymer/css_to_wrapper_test.py
index adeab81..abccd80 100755
--- a/tools/polymer/css_to_wrapper_test.py
+++ b/tools/polymer/css_to_wrapper_test.py
@@ -31,14 +31,16 @@
                 wrapper_file,
                 wrapper_file_expected,
                 minify=False,
-                use_js=False):
+                use_js=False,
+                extra_css_files=[]):
     assert not self._out_folder
     self._out_folder = tempfile.mkdtemp(dir=_HERE_DIR)
+
     args = [
         '--in_folder',
         os.path.join(_HERE_DIR, 'tests'), '--out_folder', self._out_folder,
         '--in_files', css_file
-    ]
+    ] + extra_css_files
 
     if minify:
       args.append('--minify')
@@ -61,9 +63,20 @@
                    'css_to_wrapper/expected/foo_style.css.ts')
 
   def testCssToWrapperStyleLit(self):
-    self._run_test('css_to_wrapper/foo_style_lit.css',
-                   'css_to_wrapper/foo_style_lit.css.ts',
-                   'css_to_wrapper/expected/foo_style_lit.css.ts')
+    self._run_test('css_to_wrapper/foo_style_lit_only.css',
+                   'css_to_wrapper/foo_style_lit_only.css.ts',
+                   'css_to_wrapper/expected/foo_style_lit_only.css.ts')
+
+  # Test case where a Lit style file is generated from the equivalent Polymer
+  # file.
+  def testCssToWrapperStyleLitCopy(self):
+    self._run_test(
+        'css_to_wrapper/foo_style_lit.css',
+        'css_to_wrapper/foo_style_lit.css.ts',
+        'css_to_wrapper/expected/foo_style_lit.css.ts',
+        # Need to pass the Polymer file as well, to satisfy an
+        # assertion in css_to_wrapper.py.
+        extra_css_files=['css_to_wrapper/foo_style.css'])
 
   def testCssToWrapperStyleNoIncludes(self):
     self._run_test('css_to_wrapper/foo_no_includes_style.css',
@@ -76,9 +89,9 @@
                    'css_to_wrapper/expected/foo_vars.css.ts')
 
   def testCssToWrapperVarsLit(self):
-    self._run_test('css_to_wrapper/foo_vars_lit.css',
-                   'css_to_wrapper/foo_vars_lit.css.ts',
-                   'css_to_wrapper/expected/foo_vars_lit.css.ts')
+    self._run_test('css_to_wrapper/foo_vars_lit_only.css',
+                   'css_to_wrapper/foo_vars_lit_only.css.ts',
+                   'css_to_wrapper/expected/foo_vars_lit_only.css.ts')
 
   def testCssToWrapperMinify(self):
     self._run_test('css_to_wrapper/foo_style.css',
@@ -86,6 +99,18 @@
                    'css_to_wrapper/expected/foo_style.min.css.ts',
                    minify=True)
 
+  # Test case where a Lit style file is generated from the equivalent Polymer
+  # file and minification is turned on.
+  def testCssToWrapperStyleLitCopyMinify(self):
+    self._run_test(
+        'css_to_wrapper/foo_style_lit.css',
+        'css_to_wrapper/foo_style_lit.css.ts',
+        'css_to_wrapper/expected/foo_style_lit.min.css.ts',
+        minify=True,
+        # Need to pass the Polymer file as well, to satisfy an
+        # assertion in css_to_wrapper.py.
+        extra_css_files=['css_to_wrapper/foo_style.css'])
+
   def testCssToWrapperUseJs(self):
     self._run_test('css_to_wrapper/foo_style.css',
                    'css_to_wrapper/foo_style.css.js',
diff --git a/tools/polymer/html_minifier.js b/tools/polymer/html_minifier.js
index e86ef11..3ab8f90 100644
--- a/tools/polymer/html_minifier.js
+++ b/tools/polymer/html_minifier.js
@@ -13,8 +13,9 @@
         '../../third_party/node/node_modules/html-minifier/src/htmlminifier.js')
         .minify;
 
-const path = require('path');
+const assert = require('assert');
 const fs = require('fs/promises');
+const path = require('path');
 
 // Regex to extract the CSS contents out of the HTML string. It matches anything
 // that is wrapped by a '<style>...</style>' pair.
@@ -40,7 +41,15 @@
     // If this is a CSS file, remove the <style>...</style> wrapper that was
     // added above.
     const match = result.match(REGEX);
-    result = match.groups['content'];
+
+    if (match === null) {
+      // If this is a '_lit.css' file, allow it to be an empty file, since it is
+      // handled specially by the parent script, css_to_wrapper.py.
+      assert.ok(inputFile.endsWith('_lit.css'));
+      result = '';
+    } else {
+      result = match.groups['content'];
+    }
   }
 
   // Save result.
diff --git a/tools/polymer/tests/css_to_wrapper/expected/foo_style_lit.css.ts b/tools/polymer/tests/css_to_wrapper/expected/foo_style_lit.css.ts
index ea65be7..9ded20d 100644
--- a/tools/polymer/tests/css_to_wrapper/expected/foo_style_lit.css.ts
+++ b/tools/polymer/tests/css_to_wrapper/expected/foo_style_lit.css.ts
@@ -1,8 +1,11 @@
 import {css} from 'chrome://resources/lit/v3_0/lit.rollup.js';
-import './other_vars.css.js';
+
 
 export function getCss() {
   return css`div {
-  color: blue;
+  font-size: 2rem;
+  --foo-bar: calc(var(--foo-bar1)
+      - var(--foo-bar2)
+      - 3 * var(--foo-bar3));
 }`;
 }
\ No newline at end of file
diff --git a/tools/polymer/tests/css_to_wrapper/expected/foo_style_lit.min.css.ts b/tools/polymer/tests/css_to_wrapper/expected/foo_style_lit.min.css.ts
new file mode 100644
index 0000000..b32048799
--- /dev/null
+++ b/tools/polymer/tests/css_to_wrapper/expected/foo_style_lit.min.css.ts
@@ -0,0 +1,8 @@
+import {css} from 'chrome://resources/lit/v3_0/lit.rollup.js';
+
+
+export function getCss() {
+  return css`div{font-size:2rem;--foo-bar:calc(var(--foo-bar1)
+      - var(--foo-bar2)
+      - 3 * var(--foo-bar3))}`;
+}
\ No newline at end of file
diff --git a/tools/polymer/tests/css_to_wrapper/expected/foo_style_lit_only.css.ts b/tools/polymer/tests/css_to_wrapper/expected/foo_style_lit_only.css.ts
new file mode 100644
index 0000000..ea65be7
--- /dev/null
+++ b/tools/polymer/tests/css_to_wrapper/expected/foo_style_lit_only.css.ts
@@ -0,0 +1,8 @@
+import {css} from 'chrome://resources/lit/v3_0/lit.rollup.js';
+import './other_vars.css.js';
+
+export function getCss() {
+  return css`div {
+  color: blue;
+}`;
+}
\ No newline at end of file
diff --git a/tools/polymer/tests/css_to_wrapper/expected/foo_vars_lit.css.ts b/tools/polymer/tests/css_to_wrapper/expected/foo_vars_lit_only.css.ts
similarity index 100%
rename from tools/polymer/tests/css_to_wrapper/expected/foo_vars_lit.css.ts
rename to tools/polymer/tests/css_to_wrapper/expected/foo_vars_lit_only.css.ts
diff --git a/tools/polymer/tests/css_to_wrapper/foo_style_lit.css b/tools/polymer/tests/css_to_wrapper/foo_style_lit.css
index ee093f2..a1bebbbc 100644
--- a/tools/polymer/tests/css_to_wrapper/foo_style_lit.css
+++ b/tools/polymer/tests/css_to_wrapper/foo_style_lit.css
@@ -4,8 +4,7 @@
 
 /* #css_wrapper_metadata_start
  * #type=style-lit
- * #import=./other_vars.css.js
  * #css_wrapper_metadata_end */
-div {
-  color: blue;
-}
+
+/* Purposefully empty since this style is generated at build time from the
+ * equivalent Polymer version. */
diff --git a/tools/polymer/tests/css_to_wrapper/foo_style_lit_only.css b/tools/polymer/tests/css_to_wrapper/foo_style_lit_only.css
new file mode 100644
index 0000000..ee093f2
--- /dev/null
+++ b/tools/polymer/tests/css_to_wrapper/foo_style_lit_only.css
@@ -0,0 +1,11 @@
+/* Copyright 2023 The Chromium Authors
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+/* #css_wrapper_metadata_start
+ * #type=style-lit
+ * #import=./other_vars.css.js
+ * #css_wrapper_metadata_end */
+div {
+  color: blue;
+}
diff --git a/tools/polymer/tests/css_to_wrapper/foo_vars_lit.css b/tools/polymer/tests/css_to_wrapper/foo_vars_lit_only.css
similarity index 100%
rename from tools/polymer/tests/css_to_wrapper/foo_vars_lit.css
rename to tools/polymer/tests/css_to_wrapper/foo_vars_lit_only.css
diff --git a/tools/rust/update_rust.py b/tools/rust/update_rust.py
index a9adcfc1..9d504f3 100755
--- a/tools/rust/update_rust.py
+++ b/tools/rust/update_rust.py
@@ -34,7 +34,7 @@
 # In the case that a Rust roll fails and you want to roll Clang alone, reset
 # this back to its previous value _AND_ set `OVERRIDE_CLANG_REVISION` below
 # to the `CLANG_REVISION` that was in place before the roll.
-RUST_REVISION = 'cdaa12e3dff109f72a5a8a0a67ea225052122a79'
+RUST_REVISION = 'cd6d8f2a04528f827ad3d399581c0f3502b15a72'
 RUST_SUB_REVISION = 1
 
 # If not None, this overrides the `CLANG_REVISION` in
diff --git a/tools/typescript/definitions/runtime.d.ts b/tools/typescript/definitions/runtime.d.ts
index 72251b71..b0c0763 100644
--- a/tools/typescript/definitions/runtime.d.ts
+++ b/tools/typescript/definitions/runtime.d.ts
@@ -27,12 +27,25 @@
         origin?: string;
       }
 
+      export interface Port {
+        name: string;
+        disconnect: () => void;
+        postMessage: (message: any) => void;
+        sender?: MessageSender;
+        onDisconnect: ChromeEvent<(port: Port) => void>;
+        onMessage: ChromeEvent<(message: any, port: Port) => void>;
+      }
+
       export interface ExtensionMessageEvent extends ChromeEvent<
           (message: any, sender: MessageSender,
            sendResponse: (response?: any) => void) => void> {}
 
       export const onMessageExternal: ExtensionMessageEvent;
 
+      export interface PortEvent extends ChromeEvent<(port: Port) => void> {}
+
+      export const onConnectNative: PortEvent;
+
       export function getURL(path: string): string;
 
       export interface SerializedContentScripts {
diff --git a/tools/typescript/validate_tsconfig.py b/tools/typescript/validate_tsconfig.py
index b4fdab01b..9baac48e 100644
--- a/tools/typescript/validate_tsconfig.py
+++ b/tools/typescript/validate_tsconfig.py
@@ -193,12 +193,14 @@
       'ash/webui',
       'chrome/browser/resources/ash',
       'chrome/browser/resources/chromeos',
+      'chrome/browser/resources/nearby_internals',
       'chrome/browser/resources/nearby_share',
       'ui/file_manager',
 
       # Test folders
       'chrome/test/data/webui/chromeos',
       'chrome/test/data/webui/cr_components/chromeos',
+      'chrome/test/data/webui/nearby_share',
       'chrome/test/data/webui/settings/chromeos',
   ]
   return any(path.startswith(folder) for folder in ash_folders)
diff --git a/ui/accessibility/ax_generated_tree_unittest.cc b/ui/accessibility/ax_generated_tree_unittest.cc
index 32c2663..b6cc4fd 100644
--- a/ui/accessibility/ax_generated_tree_unittest.cc
+++ b/ui/accessibility/ax_generated_tree_unittest.cc
@@ -292,7 +292,7 @@
 
           // Mark as dirty the subtree rooted at one of the nodes.
           if (l > 0)
-            serializer.MarkSubtreeDirty(tree1.GetFromId(l));
+            serializer.MarkSubtreeDirty(l);
 
           // Serialize a sequence of updates to |dst_tree| to match.
           for (int k_index = 0; k_index < tree_size; ++k_index) {
diff --git a/ui/accessibility/ax_tree_serializer.h b/ui/accessibility/ax_tree_serializer.h
index 29c550c..8882088 100644
--- a/ui/accessibility/ax_tree_serializer.h
+++ b/ui/accessibility/ax_tree_serializer.h
@@ -111,7 +111,7 @@
   // Invalidate the subtree rooted at this node, ensuring that the entire
   // subtree is re-serialized the next time any of those nodes end up
   // being serialized.
-  void MarkSubtreeDirty(AXSourceNode node);
+  void MarkSubtreeDirty(AXNodeID node);
 
   // Return whether or not this node is in the client tree. If you call
   // this immediately after serializing, this indicates whether a given
@@ -123,9 +123,6 @@
   // from the accessibility tree, this would return false.
   bool IsInClientTree(AXSourceNode node);
 
-  // Return true if this node is marked dirty.
-  bool IsDirty(AXSourceNode node);
-
   // Only for unit testing. Normally this class relies on getting a call
   // to SerializeChanges() every time the source tree changes. For unit
   // testing, it's convenient to create a static AXTree for the initial
@@ -607,8 +604,8 @@
 
 template <typename AXSourceNode, typename AXSourceNodeVectorType>
 void AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::MarkSubtreeDirty(
-    AXSourceNode node) {
-  ClientTreeNode* client_node = ClientTreeNodeById(tree_->GetId(node));
+    AXNodeID id) {
+  ClientTreeNode* client_node = ClientTreeNodeById(id);
   if (client_node)
     MarkClientSubtreeDirty(client_node);
 }
@@ -620,13 +617,6 @@
 }
 
 template <typename AXSourceNode, typename AXSourceNodeVectorType>
-bool AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::IsDirty(
-    AXSourceNode node) {
-  ClientTreeNode* client_node = ClientTreeNodeById(tree_->GetId(node));
-  return client_node ? client_node->IsDirty() : false;
-}
-
-template <typename AXSourceNode, typename AXSourceNodeVectorType>
 void AXTreeSerializer<AXSourceNode, AXSourceNodeVectorType>::
     MarkClientSubtreeDirty(ClientTreeNode* client_node) {
   // Return early if already marked dirty, in order to avoid duplicate work in
diff --git a/ui/accessibility/ax_tree_serializer_unittest.cc b/ui/accessibility/ax_tree_serializer_unittest.cc
index f63a7b0..6ef6c953 100644
--- a/ui/accessibility/ax_tree_serializer_unittest.cc
+++ b/ui/accessibility/ax_tree_serializer_unittest.cc
@@ -231,7 +231,7 @@
 
   CreateTreeSerializer();
   AXTreeUpdate update;
-  serializer_->MarkSubtreeDirty(tree1_->GetFromId(1));
+  serializer_->MarkSubtreeDirty(1);
   ASSERT_TRUE(serializer_->SerializeChanges(tree1_->GetFromId(4), &update));
 
   // The update should unserialize without errors.
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index 58f4976..d2bb8e8 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -1018,7 +1018,7 @@
               L"group"};
 
     case ax::mojom::Role::kFigcaption:
-      return {UIALocalizationStrategy::kSupply, UIA_TextControlTypeId,
+      return {UIALocalizationStrategy::kSupply, UIA_GroupControlTypeId,
               L"description"};
 
     case ax::mojom::Role::kFigure:
@@ -1091,7 +1091,7 @@
               L"description"};
 
     case ax::mojom::Role::kLegend:
-      return {UIALocalizationStrategy::kSupply, UIA_TextControlTypeId,
+      return {UIALocalizationStrategy::kSupply, UIA_GroupControlTypeId,
               L"description"};
 
     case ax::mojom::Role::kLayoutTable:
@@ -1403,7 +1403,7 @@
               L"combobox"};
 
     case ax::mojom::Role::kAbbr:
-      return {UIALocalizationStrategy::kSupply, UIA_TextControlTypeId,
+      return {UIALocalizationStrategy::kSupply, UIA_GroupControlTypeId,
               L"description"};
 
     case ax::mojom::Role::kTime:
diff --git a/ui/base/clipboard/clipboard_ios.mm b/ui/base/clipboard/clipboard_ios.mm
index 571efe67..13d3bb9 100644
--- a/ui/base/clipboard/clipboard_ios.mm
+++ b/ui/base/clipboard/clipboard_ios.mm
@@ -380,13 +380,10 @@
 void ClipboardIOS::WriteHTML(
     base::StringPiece markup,
     absl::optional<base::StringPiece> /* source_url */) {
-  // We need to mark it as utf-8. (see crbug.com/11957)
-  std::string html_fragment_str("<meta charset='utf-8'>");
-  html_fragment_str += markup;
-  NSString* html = base::SysUTF8ToNSString(html_fragment_str);
-
-  NSDictionary<NSString*, id>* html_item =
-      @{ClipboardFormatType::HtmlType().ToNSString() : html};
+  NSDictionary<NSString*, id>* html_item = @{
+    ClipboardFormatType::HtmlType().ToNSString() :
+        base::SysUTF8ToNSString(markup)
+  };
   [GetPasteboard() addItems:@[ html_item ]];
 }
 
diff --git a/ui/base/clipboard/clipboard_mac.mm b/ui/base/clipboard/clipboard_mac.mm
index 10f33c9d..ebc4151 100644
--- a/ui/base/clipboard/clipboard_mac.mm
+++ b/ui/base/clipboard/clipboard_mac.mm
@@ -479,12 +479,8 @@
 void ClipboardMac::WriteHTML(
     base::StringPiece markup,
     absl::optional<base::StringPiece> /* source_url */) {
-  // We need to mark it as utf-8. (see crbug.com/11957)
-  std::string html_fragment_str("<meta charset='utf-8'>");
-  html_fragment_str.append(markup);
-  NSString* html_fragment = base::SysUTF8ToNSString(html_fragment_str);
-
-  [GetPasteboard() setString:html_fragment forType:NSPasteboardTypeHTML];
+  [GetPasteboard() setString:base::SysUTF8ToNSString(markup)
+                     forType:NSPasteboardTypeHTML];
 }
 
 void ClipboardMac::WriteSvg(base::StringPiece markup) {
diff --git a/ui/base/idle/idle_polling_service.cc b/ui/base/idle/idle_polling_service.cc
index 15a503a..29f28b2 100644
--- a/ui/base/idle/idle_polling_service.cc
+++ b/ui/base/idle/idle_polling_service.cc
@@ -14,8 +14,6 @@
 
 namespace {
 
-constexpr base::TimeDelta kPollInterval = base::Seconds(1);
-
 // Default provider implementation. Everything is delegated to
 // ui::CalculateIdleTime and ui::CheckIdleStateIsLocked.
 class DefaultIdleProvider : public IdleTimeProvider {
@@ -51,7 +49,9 @@
   if (observers_.empty()) {
     DCHECK(!timer_.IsRunning());
     PollIdleState();
-    timer_.Reset();
+    timer_.Start(FROM_HERE, poll_interval_,
+                 base::BindRepeating(&IdlePollingService::PollIdleState,
+                                     base::Unretained(this)));
   }
 
   observers_.AddObserver(observer);
@@ -75,6 +75,11 @@
   }
 }
 
+void IdlePollingService::SetPollIntervalForTest(base::TimeDelta poll_interval) {
+  DCHECK(!timer_.IsRunning());
+  poll_interval_ = poll_interval;
+}
+
 bool IdlePollingService::IsPollingForTest() {
   return timer_.IsRunning();
 }
@@ -85,10 +90,7 @@
 }
 
 IdlePollingService::IdlePollingService()
-    : timer_(FROM_HERE,
-             kPollInterval,
-             base::BindRepeating(&IdlePollingService::PollIdleState,
-                                 base::Unretained(this))),
+    : poll_interval_(kPollInterval),
       provider_(std::make_unique<DefaultIdleProvider>()) {
   DCHECK(!timer_.IsRunning());
 }
diff --git a/ui/base/idle/idle_polling_service.h b/ui/base/idle/idle_polling_service.h
index 1c0d88891..ea3b0322 100644
--- a/ui/base/idle/idle_polling_service.h
+++ b/ui/base/idle/idle_polling_service.h
@@ -29,6 +29,8 @@
  public:
   static IdlePollingService* GetInstance();
 
+  static constexpr base::TimeDelta kPollInterval = base::Seconds(15);
+
   struct State {
     bool locked;
     base::TimeDelta idle_time;
@@ -48,6 +50,7 @@
   const State& GetIdleState();
 
   void SetProviderForTest(std::unique_ptr<IdleTimeProvider> provider);
+  void SetPollIntervalForTest(base::TimeDelta interval);
   bool IsPollingForTest();
   void SetTaskRunnerForTest(
       scoped_refptr<base::SequencedTaskRunner> task_runner);
@@ -60,6 +63,7 @@
 
   void PollIdleState();
 
+  base::TimeDelta poll_interval_;
   base::RepeatingTimer timer_;
   std::unique_ptr<IdleTimeProvider> provider_;
   State last_state_;
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc
index db77c9c..b0eea6e 100644
--- a/ui/compositor/layer.cc
+++ b/ui/compositor/layer.cc
@@ -213,7 +213,6 @@
       layer_mask_back_link_(nullptr),
       zoom_(1),
       zoom_inset_(0),
-      delegate_(nullptr),
       owner_(nullptr),
       cc_layer_(nullptr),
       device_scale_factor_(1.0f),
diff --git a/ui/compositor/layer.h b/ui/compositor/layer.h
index f9e293e..388d622 100644
--- a/ui/compositor/layer.h
+++ b/ui/compositor/layer.h
@@ -814,7 +814,7 @@
 
   std::string name_;
 
-  raw_ptr<LayerDelegate, DanglingUntriaged> delegate_;
+  raw_ptr<LayerDelegate> delegate_ = nullptr;
 
   base::ObserverList<LayerObserver>::Unchecked observer_list_;
 
diff --git a/ui/display/types/native_display_delegate.cc b/ui/display/types/native_display_delegate.cc
index 63c477f1..a9fd637 100644
--- a/ui/display/types/native_display_delegate.cc
+++ b/ui/display/types/native_display_delegate.cc
@@ -8,4 +8,20 @@
 
 NativeDisplayDelegate::~NativeDisplayDelegate() {}
 
+void NativeDisplayDelegate::SetColorCalibration(
+    int64_t display_id,
+    const ColorCalibration& calibration) {}
+
+bool NativeDisplayDelegate::SetColorMatrix(
+    int64_t display_id,
+    const std::vector<float>& color_matrix) {
+  return false;
+}
+
+bool NativeDisplayDelegate::SetGammaCorrection(int64_t display_id,
+                                               const GammaCurve& degamma,
+                                               const GammaCurve& gamma) {
+  return false;
+}
+
 }  // namespace display
diff --git a/ui/display/types/native_display_delegate.h b/ui/display/types/native_display_delegate.h
index 4b887f82..bcf485f0 100644
--- a/ui/display/types/native_display_delegate.h
+++ b/ui/display/types/native_display_delegate.h
@@ -89,7 +89,7 @@
 
   // Sets the color calibration for the specified display.
   virtual void SetColorCalibration(int64_t display_id,
-                                   const ColorCalibration& calibration) = 0;
+                                   const ColorCalibration& calibration);
 
   // Sets the display profile space gamma adjustment for the specified display.
   virtual void SetGammaAdjustment(int64_t display_id,
@@ -99,14 +99,14 @@
   // This doesn't affect gamma or degamma. It returns true the color matrix was
   // sent to the GPU process successfully.
   virtual bool SetColorMatrix(int64_t display_id,
-                              const std::vector<float>& color_matrix) = 0;
+                              const std::vector<float>& color_matrix);
 
   // Sets the given |gamma_lut| and |degamma_lut| on the display with
   // |display_id|. Returns true if the given tables were sent to the GPU process
   // successfully.
   virtual bool SetGammaCorrection(int64_t display_id,
                                   const GammaCurve& degamma,
-                                  const GammaCurve& gamma) = 0;
+                                  const GammaCurve& gamma);
 
   // Sets the privacy screen state on the display with |display_id|.
   virtual void SetPrivacyScreen(int64_t display_id,
diff --git a/ui/events/ozone/evdev/event_device_info.cc b/ui/events/ozone/evdev/event_device_info.cc
index b5e3a69..dadde6b 100644
--- a/ui/events/ozone/evdev/event_device_info.cc
+++ b/ui/events/ozone/evdev/event_device_info.cc
@@ -114,7 +114,6 @@
     {0x0951, 0x1727},  // HyperX Pulsefire Haste Gaming Mouse
     {0x0b05, 0x1949},  // ASUS ROG Strix Impact II
     {0x0b33, 0x3022},  // Contour Design RollerMouse Pro
-    {0x0c45, 0x7403},  // RDing FootSwitch1F1
     {0x1038, 0x0470},  // SteelSeries Reaper Edge
     {0x1038, 0x0471},  // SteelSeries Rival Rescuer
     {0x1038, 0x0472},  // SteelSeries Rival 150 net café
diff --git a/ui/file_manager/file_names.gni b/ui/file_manager/file_names.gni
index 78501810..adac2dac 100644
--- a/ui/file_manager/file_names.gni
+++ b/ui/file_manager/file_names.gni
@@ -1,18 +1,14 @@
 # Static files are used as-is from the repository.
 image_loader_static_js_files = [
-  "image_loader/image_loader.js",
   "image_loader/image_loader_util.js",
   "image_loader/image_request_task.js",
   "image_loader/load_image_request.js",
-  "image_loader/piex_loader.js",
 ]
 
 image_loader_ts = [
   "image_loader/background.ts",
   "image_loader/cache.ts",
-
-  # "image_loader/image_loader.ts",
-
+  "image_loader/image_loader.ts",
   "image_loader/image_loader_client.ts",
 
   # "image_loader/image_loader_util.ts",
@@ -23,7 +19,7 @@
 
   # "image_loader/load_image_request.ts",
 
-  # "image_loader/piex_loader.ts",
+  "image_loader/piex_loader.ts",
 
   "image_loader/scheduler.ts",
 ]
diff --git a/ui/file_manager/image_loader/BUILD.gn b/ui/file_manager/image_loader/BUILD.gn
index 388da8c..4201e056 100644
--- a/ui/file_manager/image_loader/BUILD.gn
+++ b/ui/file_manager/image_loader/BUILD.gn
@@ -2,16 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//tools/typescript/ts_definitions.gni")
 import("//tools/typescript/ts_library.gni")
 import("//ui/file_manager/file_names.gni")
 import("//ui/webui/resources/tools/optimize_webui.gni")
 import("//ui/webui/webui_features.gni")
 
-ts_definitions("generate_definitions") {
-  js_files = [ "piex_loader.js" ]
-}
-
 preprocess_folder = "$target_gen_dir/preprocessed"
 tsc_folder = "$target_gen_dir/tsc"
 files_app_path = rebase_path("$tsc_folder/file_manager", root_build_dir)
@@ -39,13 +34,13 @@
   sources = [
     "background.ts",
     "cache.ts",
-    "image_loader.js",
+    "image_loader.ts",
     "image_loader_client.ts",
     "image_loader_util.js",
     "image_orientation.ts",
     "image_request_task.js",
     "load_image_request.js",
-    "piex_loader.js",
+    "piex_loader.ts",
     "scheduler.ts",
   ]
   outputs = [ "$preprocess_folder/image_loader/{{source_file_part}}" ]
@@ -62,7 +57,10 @@
   tsconfig_base = "tsconfig_base.json"
   composite = true
 
-  deps = [ "//ash/webui/common/resources:build_ts" ]
+  deps = [
+    "//ash/webui/common/resources:build_ts",
+    "//ui/webui/resources/js:build_ts",
+  ]
 
   extra_deps = [
     ":copy_src",
diff --git a/ui/file_manager/image_loader/image_loader.js b/ui/file_manager/image_loader/image_loader.js
deleted file mode 100644
index f8c4640..0000000
--- a/ui/file_manager/image_loader/image_loader.js
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright 2013 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {assert} from 'chrome://resources/ash/common/assert.js';
-
-import {ImageCache} from './cache.js';
-import {ImageOrientation} from './image_orientation.js';
-import {ImageRequestTask} from './image_request_task.js';
-import {LoadImageRequest, LoadImageResponse} from './load_image_request.js';
-import {Scheduler} from './scheduler.js';
-
-/**
- * Loads and resizes an image.
- */
-export class ImageLoader {
-  constructor() {
-    /**
-     * Persistent cache object.
-     * @type {ImageCache}
-     * @private
-     */
-    this.cache_ = new ImageCache();
-
-    /**
-     * Manages pending requests and runs them in order of priorities.
-     * @type {Scheduler}
-     * @private
-     */
-    this.scheduler_ = new Scheduler();
-
-    // Initialize the cache and then start the scheduler.
-    this.cache_.initialize(() => this.scheduler_.start());
-
-    // Listen for incoming requests.
-    chrome.runtime.onMessageExternal.addListener(
-        (msg, sender, sendResponse) => {
-          if (!sender.origin || !msg) {
-            return;
-          }
-
-          if (ALLOWED_CLIENT_ORIGINS.indexOf(sender.origin) === -1) {
-            return;
-          }
-
-          this.onIncomingRequest_(msg, sender.origin, sendResponse);
-        });
-
-    // @ts-ignore: error TS7006: Parameter 'port' implicitly has an 'any' type.
-    chrome.runtime.onConnectNative.addListener((port) => {
-      if (port.sender.nativeApplication !== 'com.google.ash_thumbnail_loader') {
-        port.disconnect();
-        return;
-      }
-
-      // @ts-ignore: error TS7006: Parameter 'msg' implicitly has an 'any' type.
-      port.onMessage.addListener((msg) => {
-        // Each connection is expected to handle a single request only.
-        const started = this.onIncomingRequest_(
-            msg, port.sender.nativeApplication, response => {
-              port.postMessage(response);
-              port.disconnect();
-            });
-
-        if (!started) {
-          port.disconnect();
-        }
-      });
-    });
-  }
-
-  /**
-   * Handler for incoming requests.
-   *
-   * @param {*} request_data A LoadImageRequest (received untyped).
-   * @param {!string} senderOrigin
-   * @param {function(*): void} sendResponse
-   */
-  onIncomingRequest_(request_data, senderOrigin, sendResponse) {
-    const request = /** @type {!LoadImageRequest} */ (request_data);
-
-    // Sending a response may fail if the receiver already went offline.
-    // This is not an error, but a normal and quite common situation.
-    // @ts-ignore: error TS7006: Parameter 'response' implicitly has an 'any'
-    // type.
-    const failSafeSendResponse = function(response) {
-      try {
-        sendResponse(response);
-      } catch (e) {
-        // Ignore the error.
-      }
-    };
-    // Incoming requests won't have the full type.
-    assert(!(request.orientation instanceof ImageOrientation));
-    assert(!(typeof request.orientation === 'number'));
-
-    if (request.orientation) {
-      request.orientation =
-          // @ts-ignore: error TS2345: Argument of type 'ImageTransformParam |
-          // ImageOrientation' is not assignable to parameter of type
-          // 'ImageTransformParam'.
-          ImageOrientation.fromRotationAndScale(request.orientation);
-    } else {
-      request.orientation = new ImageOrientation(1, 0, 0, 1);
-    }
-    return this.onMessage_(senderOrigin, request, failSafeSendResponse);
-  }
-
-  /**
-   * Handles a request. Depending on type of the request, starts or stops
-   * an image task.
-   *
-   * @param {string} senderOrigin Sender's origin.
-   * @param {!LoadImageRequest} request Pre-processed request.
-   * @param {(r: LoadImageResponse) => void} callback Callback to be called to
-   *     return response.
-   * @return {boolean} True if the message channel should stay alive until the
-   *     callback is called.
-   * @private
-   */
-  onMessage_(senderOrigin, request, callback) {
-    const requestId = senderOrigin + ':' + request.taskId;
-    if (request.cancel) {
-      // Cancel a task.
-      this.scheduler_.remove(requestId);
-      return false;  // No callback calls.
-    } else {
-      // Create a request task and add it to the scheduler (queue).
-      const requestTask =
-          new ImageRequestTask(requestId, this.cache_, request, callback);
-      this.scheduler_.add(requestTask);
-      return true;  // Request will call the callback.
-    }
-  }
-
-  /**
-   * Returns the singleton instance.
-   * @return {ImageLoader} ImageLoader object.
-   */
-  static getInstance() {
-    // @ts-ignore: error TS2339: Property 'instance_' does not exist on type
-    // 'typeof ImageLoader'.
-    if (!ImageLoader.instance_) {
-      // @ts-ignore: error TS2339: Property 'instance_' does not exist on type
-      // 'typeof ImageLoader'.
-      ImageLoader.instance_ = new ImageLoader();
-    }
-    // @ts-ignore: error TS2339: Property 'instance_' does not exist on type
-    // 'typeof ImageLoader'.
-    return ImageLoader.instance_;
-  }
-}
-
-/**
- * List of extensions allowed to perform image requests.
- *
- * @const
- * @type {Array<string>}
- */
-const ALLOWED_CLIENT_ORIGINS = [
-  'chrome://file-manager',  // File Manager SWA
-];
diff --git a/ui/file_manager/image_loader/image_loader.ts b/ui/file_manager/image_loader/image_loader.ts
new file mode 100644
index 0000000..9a4ce4d
--- /dev/null
+++ b/ui/file_manager/image_loader/image_loader.ts
@@ -0,0 +1,136 @@
+// Copyright 2013 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {assert} from 'chrome://resources/js/assert.js';
+
+import {ImageCache} from './cache.js';
+import {ImageOrientation} from './image_orientation.js';
+import type {ImageTransformParam} from './image_orientation.js';
+import {ImageRequestTask} from './image_request_task.js';
+import {LoadImageRequest, LoadImageResponse} from './load_image_request.js';
+import {Scheduler} from './scheduler.js';
+
+let instance: ImageLoader|null = null;
+
+/**
+ * Loads and resizes an image.
+ */
+export class ImageLoader {
+  /**
+   * Persistent cache object.
+   */
+  private cache_: ImageCache = new ImageCache();
+
+  /**
+   * Manages pending requests and runs them in order of priorities.
+   */
+  private scheduler_: Scheduler = new Scheduler();
+
+  constructor() {
+    // Initialize the cache and then start the scheduler.
+    this.cache_.initialize(() => this.scheduler_.start());
+
+    // Listen for incoming requests.
+    chrome.runtime.onMessageExternal.addListener(
+        (msg, sender, sendResponse) => {
+          if (!sender.origin || !msg) {
+            return;
+          }
+
+          if (ALLOWED_CLIENT_ORIGINS.indexOf(sender.origin) === -1) {
+            return;
+          }
+
+          this.onIncomingRequest_(msg, sender.origin, sendResponse);
+        });
+
+    chrome.runtime.onConnectNative.addListener(port => {
+      assert(port.sender);
+      if (port.sender.nativeApplication !== 'com.google.ash_thumbnail_loader') {
+        port.disconnect();
+        return;
+      }
+
+      port.onMessage.addListener(msg => {
+        // Each connection is expected to handle a single request only.
+        const started = this.onIncomingRequest_(
+            msg, port.sender!.nativeApplication!, response => {
+              port.postMessage(response);
+              port.disconnect();
+            });
+
+        if (!started) {
+          port.disconnect();
+        }
+      });
+    });
+  }
+
+  /**
+   * Handler for incoming requests.
+   */
+  private onIncomingRequest_(
+      request: LoadImageRequest, senderOrigin: string,
+      sendResponse: (r: unknown) => void) {
+    // Sending a response may fail if the receiver already went offline.
+    // This is not an error, but a normal and quite common situation.
+    const failSafeSendResponse = function(response: unknown) {
+      try {
+        sendResponse(response);
+      } catch (e) {
+        // Ignore the error.
+      }
+    };
+    // Incoming requests won't have the full type.
+    assert(!(request.orientation instanceof ImageOrientation));
+    assert(typeof request.orientation !== 'number');
+
+    if (request.orientation) {
+      request.orientation = ImageOrientation.fromRotationAndScale(
+          request.orientation as ImageTransformParam);
+    } else {
+      request.orientation = new ImageOrientation(1, 0, 0, 1);
+    }
+    return this.onMessage_(senderOrigin, request, failSafeSendResponse);
+  }
+
+  /**
+   * Handles a request. Depending on type of the request, starts or stops
+   * an image task.
+   * @return True if the message channel should stay alive until the
+   *     callback is called.
+   */
+  private onMessage_(
+      senderOrigin: string, request: LoadImageRequest,
+      callback: (r: LoadImageResponse) => void): boolean {
+    const requestId = senderOrigin + ':' + request.taskId;
+    if (request.cancel) {
+      // Cancel a task.
+      this.scheduler_.remove(requestId);
+      return false;  // No callback calls.
+    }
+    // Create a request task and add it to the scheduler (queue).
+    const requestTask =
+        new ImageRequestTask(requestId, this.cache_, request, callback);
+    this.scheduler_.add(requestTask);
+    return true;  // Request will call the callback.
+  }
+
+  /**
+   * Returns a singleton instance.
+   */
+  static getInstance(): ImageLoader {
+    if (!instance) {
+      instance = new ImageLoader();
+    }
+    return instance;
+  }
+}
+
+/**
+ * List of extensions allowed to perform image requests.
+ */
+const ALLOWED_CLIENT_ORIGINS = [
+  'chrome://file-manager',  // File Manager SWA
+];
diff --git a/ui/file_manager/image_loader/piex_loader.js b/ui/file_manager/image_loader/piex_loader.ts
similarity index 71%
rename from ui/file_manager/image_loader/piex_loader.js
rename to ui/file_manager/image_loader/piex_loader.ts
index cbcce3a..bbaec7b 100644
--- a/ui/file_manager/image_loader/piex_loader.js
+++ b/ui/file_manager/image_loader/piex_loader.ts
@@ -5,50 +5,45 @@
 /**
  * Declares the piex-wasm Module interface. The Module has many interfaces
  * but only declare the parts required for PIEX work.
- *
- * @typedef {{
- *  calledRun: boolean,
- *  HEAP8: !Uint8Array,
- *  _malloc: function(number):number,
- *  _free: function(number):undefined,
- *  image: function(number, number):!PiexWasmImageResult
- * }}
  */
-// @ts-ignore: error TS7005: Variable 'PiexWasmModule' implicitly has an 'any'
-// type.
-export let PiexWasmModule;
+export interface PiexWasmModule {
+  calledRun: boolean;
+  HEAP8: Uint8Array;
+  _malloc: (size: number) => number;
+  _free: (size: number) => void;
+  image: (width: number, height: number) => PiexWasmImageResult;
+}
+
+interface VoidCallback {
+  (): void;
+}
 
 /**
  * Subset of the Emscripten Module API required for initialization. See
  * https://emscripten.org/docs/api_reference/module.html#module.
- * @typedef {{
- *  onAbort: function((!Error|string)):undefined,
- * }}
  */
-let ModuleInitParams;
+interface ModuleInitParams {
+  onAbort: (result: Error|string) => void;
+}
 
 /**
  * Module defined by 'piex.js.wasm' script upon initialization.
- * @type {!PiexWasmModule}
  */
-let PiexModule;
+let PiexModule: PiexWasmModule;
+
+declare global {
+  function createPiexModule(params: ModuleInitParams): Promise<PiexWasmModule>;
+}
 
 /**
  * Module constructor defined by 'piex.js.wasm' script.
- * @type {function(!ModuleInitParams): !Promise<!PiexWasmModule>}
  */
-const initPiexModule =
-    /** @type {function(!ModuleInitParams): !Promise<!PiexWasmModule>} */ (
-        // @ts-ignore: error TS7053: Element implicitly has an 'any' type
-        // because expression of type '"createPiexModule"' can't be used to
-        // index type 'typeof globalThis'.
-        globalThis['createPiexModule']);
+const initPiexModule = globalThis.createPiexModule;
 
 console.log(`[PiexLoader] available [init=${typeof initPiexModule}]`);
 
 /**
  * Set true if the Module.onAbort() handler is called.
- * @type {boolean}
  */
 let piexFailed = false;
 
@@ -57,23 +52,21 @@
    * Installs an (Emscripten) Module.onAbort handler. Record that the
    * Module has failed in piexFailed and re-throw the error.
    *
-   * @param {!Error|string} error
    * @throws {!Error|string}
    */
-  onAbort: (error) => {
+  onAbort: (error: Error|string) => {
     piexFailed = true;
     throw error;
   },
 };
 
-/** @type {?Promise<void>} */
-let initPiexModulePromise = null;
+let initPiexModulePromise: Promise<void>|null = null;
+
 /**
  * Returns a promise that resolves once initialization is complete. PiexModule
  * may be undefined before this promise resolves.
- * @return {!Promise<void>}
  */
-function piexModuleInitialized() {
+function piexModuleInitialized(): Promise<void> {
   if (!initPiexModulePromise) {
     initPiexModulePromise = new Promise(resolve => {
       initPiexModule(MODULE_SETTINGS).then(module => {
@@ -95,9 +88,8 @@
  * Module state. Log the error, and return true to tell caller to initiate
  * failure recovery steps.
  *
- * @return {boolean}
  */
-function piexModuleFailed() {
+function piexModuleFailed(): boolean {
   if (piexFailed || !PiexModule.calledRun) {
     console.error('[PiexLoader] piex wasm module failed');
     return true;
@@ -105,64 +97,40 @@
   return false;
 }
 
-/**
- * @typedef {{
- *  thumbnail: !ArrayBuffer,
- *  mimeType?: (string|undefined),
- *  orientation: number,
- *  colorSpace: string,
- *  ifd: ?string
- * }}
- */
-let PiexPreviewImageData;
+interface PiexPreviewImageData {
+  thumbnail: ArrayBuffer;
+  mimeType?: string;
+  orientation: number;
+  colorSpace: string;
+  ifd: string|null;
+}
 
-/**
- * @struct
- */
 class PiexLoaderResponse {
+  readonly thumbnail: ArrayBuffer;
+  readonly mimeType: string;
+
+  /** JEITA EXIF image orientation being an integer in [1..8].  */
+  readonly orientation: number;
+
+  /** JEITA EXIF image color space: 'sRgb' or 'adobeRgb'.  */
+  readonly colorSpace: string;
+
+  /** JSON encoded RAW image photographic details.  */
+  readonly ifd: string|null;
+
   /**
-   * @param {!PiexPreviewImageData} data The extracted preview image data.
+   * @param data The extracted preview image data.
    */
-  constructor(data) {
-    /**
-     * @type {!ArrayBuffer}
-     * @const
-     */
+  constructor(data: PiexPreviewImageData) {
     this.thumbnail = data.thumbnail;
-
-    /**
-     * @type {string}
-     * @const
-     */
     this.mimeType = data.mimeType || 'image/jpeg';
-
-    /**
-     * JEITA EXIF image orientation being an integer in [1..8].
-     * @type {number}
-     * @const
-     */
     this.orientation = data.orientation;
-
-    /**
-     * JEITA EXIF image color space: 'sRgb' or 'adobeRgb'.
-     * @type {string}
-     * @const
-     */
     this.colorSpace = data.colorSpace;
-
-    /**
-     * JSON encoded RAW image photographic details.
-     * @type {?string}
-     * @const
-     */
     this.ifd = data.ifd || null;
   }
 }
 
-/**
- * JFIF APP2 ICC_PROFILE segment containing an AdobeRGB1998 Color Profile.
- * @const {!Uint8Array}
- */
+/** JFIF APP2 ICC_PROFILE segment containing an AdobeRGB1998 Color Profile. */
 const adobeProfile = new Uint8Array([
   // clang-format off
   // APP2 ICC_PROFILE\0 segment header.
@@ -230,70 +198,81 @@
  * The |offset| to, and |length| of, the preview image relative to the source
  * data is indicated by those fields. They are positive > 0. Note: the values
  * are controlled by a third-party and are untrustworthy (Security).
- *
- * @typedef {{
- *  format:number,
- *  colorSpace:string,
- *  orientation:number,
- *  width:?number,
- *  height:?number,
- *  offset:number,
- *  length:number
- * }}
  */
-let PiexWasmPreviewImageMetadata;
+interface PiexWasmPreviewImageMetadata {
+  /** Ether 0 (JPEG), or 1 (RGB). */
+  format: number;
+
+  /** JEITA EXIF: sRGB or AdobeRGB1998. */
+  colorSpace: string;
+
+  /** JEITA EXIF image orientation.  */
+  orientation: number;
+
+  /** Only available for RGB format preview image. */
+  width?: number;
+
+  /** Only available for RGB format preview image. */
+  height?: number;
+
+  /**
+   * The offset of the preview image relative to the source data. They are
+   * positive > 0. Note: ths value is controlled by a third-party and are
+   * untrustworthy (Security).
+   */
+  offset: number;
+
+  /**
+   * The length of the preview image relative to the source data. They are
+   * positive > 0. Note: ths value is controlled by a third-party and are
+   * untrustworthy (Security).
+   */
+  length: number;
+}
 
 /**
- * The piex-wasm Module.image(<RAW image source>,...) API returns |error|, or
- * else the source |preview| and/or |thumbnail| image metadata along with the
- * photographic |details| derived from the RAW image EXIF.
- *
- * The |preview| images are JPEG. The |thumbnail| images are smaller, lower-
- * quality, JPEG or RGB format images.
- *
- * @typedef {{
- *  error:?string,
- *  preview:?PiexWasmPreviewImageMetadata,
- *  thumbnail:?PiexWasmPreviewImageMetadata,
- *  details:?Object
- * }}
+ * The piex-wasm Module.image(<RAW image source>,...) API returns `error`, or
+ * else the source `preview` and/or `thumbnail` image metadata along with the
+ * photographic `details` derived from the RAW image EXIF.
  */
-let PiexWasmImageResult;
+interface PiexWasmImageResult {
+  error: string|null;
+
+  /** The `preview` images are JPEG.*/
+  preview: PiexWasmPreviewImageMetadata|null;
+
+  /**
+   * The `thumbnail` images are smaller, lower  quality, JPEG or RGB format
+   * images.
+   */
+  thumbnail: PiexWasmPreviewImageMetadata|null;
+
+  /** The photographic `details` derived from the RAW image EXIF. */
+  details: Record<string, any>|null;
+}
 
 /**
  * Preview Image EXtractor (PIEX).
  */
 class ImageBuffer {
+  private readonly source: Uint8Array;
+  private readonly length: number;
+  private memory = 0;
+
   /**
-   * @param {!ArrayBuffer} buffer - RAW image source data.
+   * @param buffer - RAW image source data.
    */
-  constructor(buffer) {
-    /**
-     * @const {!Uint8Array}
-     * @private
-     */
+  constructor(buffer: ArrayBuffer) {
     this.source = new Uint8Array(buffer);
-
-    /**
-     * @const {number}
-     * @private
-     */
     this.length = buffer.byteLength;
-
-    /**
-     * @type {number}
-     * @private
-     */
-    this.memory = 0;
   }
 
   /**
    * Calls Module.image() to process |this.source| and return the result.
    *
    * @throws {!Error} Memory allocation error.
-   * @return {!PiexWasmImageResult}
    */
-  process() {
+  process(): PiexWasmImageResult {
     this.memory = PiexModule._malloc(this.length);
     if (!this.memory) {
       throw new Error('Image malloc failed: ' + this.length + ' bytes');
@@ -312,11 +291,9 @@
    * Returns the preview image data. If no preview image was found, returns
    * the thumbnail image.
    *
-   * @param {!PiexWasmImageResult} result
    * @throws {!Error} Data access security error.
-   * @return {!PiexPreviewImageData}
    */
-  preview(result) {
+  preview(result: PiexWasmImageResult): PiexPreviewImageData {
     const preview = result.preview;
     if (!preview) {
       return this.thumbnail_(result);
@@ -342,12 +319,9 @@
    * Returns the thumbnail image. If no thumbnail image was found, returns
    * an empty thumbnail image.
    *
-   * @private
-   * @param {!PiexWasmImageResult} result
    * @throws {!Error} Data access security error.
-   * @return {!PiexPreviewImageData}
    */
-  thumbnail_(result) {
+  private thumbnail_(result: PiexWasmImageResult): PiexPreviewImageData {
     const thumbnail = result.thumbnail;
     if (!thumbnail) {
       return {
@@ -382,12 +356,9 @@
    * Returns the RGB thumbnail. If no RGB thumbnail was found, returns
    * an empty thumbnail image.
    *
-   * @private
-   * @param {!PiexWasmImageResult} result
    * @throws {!Error} Data access security error.
-   * @return {!PiexPreviewImageData}
    */
-  rgb_(result) {
+  private rgb_(result: PiexWasmImageResult): PiexPreviewImageData {
     const thumbnail = result.thumbnail;
     if (!thumbnail || thumbnail.format !== 1) {
       return {
@@ -477,13 +448,13 @@
     }
 
     // RGB CIEXYZ.
-    bitmap.setUint32( 74, /* R CIEXYZ x */ rx, true);
-    bitmap.setUint32( 78, /* R CIEXYZ y */ ry, true);
-    bitmap.setUint32( 82, /* R CIEXYZ z */ zz, true);
-    bitmap.setUint32( 86, /* G CIEXYZ x */ gx, true);
-    bitmap.setUint32( 90, /* G CIEXYZ y */ gy, true);
-    bitmap.setUint32( 94, /* G CIEXYZ z */ zz, true);
-    bitmap.setUint32( 98, /* B CIEXYZ x */ bx, true);
+    bitmap.setUint32(74, /* R CIEXYZ x */ rx, true);
+    bitmap.setUint32(78, /* R CIEXYZ y */ ry, true);
+    bitmap.setUint32(82, /* R CIEXYZ z */ zz, true);
+    bitmap.setUint32(86, /* G CIEXYZ x */ gx, true);
+    bitmap.setUint32(90, /* G CIEXYZ y */ gy, true);
+    bitmap.setUint32(94, /* G CIEXYZ z */ zz, true);
+    bitmap.setUint32(98, /* B CIEXYZ x */ bx, true);
     bitmap.setUint32(102, /* B CIEXYZ y */ by, true);
     bitmap.setUint32(106, /* B CIEXYZ z */ zz, true);
 
@@ -544,15 +515,9 @@
       }
 
       for (let x = 0; x <= w; ++x, input += 3, output += dx) {
-        // @ts-ignore: error TS2345: Argument of type 'number | undefined' is
-        // not assignable to parameter of type 'number'.
-        bitmap.setUint8(output + 0, view[input + 2]);  // B
-        // @ts-ignore: error TS2345: Argument of type 'number | undefined' is
-        // not assignable to parameter of type 'number'.
-        bitmap.setUint8(output + 1, view[input + 1]);  // G
-        // @ts-ignore: error TS2345: Argument of type 'number | undefined' is
-        // not assignable to parameter of type 'number'.
-        bitmap.setUint8(output + 2, view[input + 0]);  // R
+        bitmap.setUint8(output + 0, view[input + 2]!);  // B
+        bitmap.setUint8(output + 1, view[input + 1]!);  // G
+        bitmap.setUint8(output + 2, view[input + 0]!);  // R
       }
     }
 
@@ -564,15 +529,18 @@
         let output = paddingOffset;
 
         switch (rowPad) {
-          case 3:
-            bitmap.setUint8(output++, 0);
-            // Fallthrough
-          case 2:
-            bitmap.setUint8(output++, 0);
-            // Fallthrough
           case 1:
             bitmap.setUint8(output++, 0);
-            // Fallthrough
+            break;
+          case 2:
+            bitmap.setUint8(output++, 0);
+            bitmap.setUint8(output++, 0);
+            break;
+          case 3:
+            bitmap.setUint8(output++, 0);
+            bitmap.setUint8(output++, 0);
+            bitmap.setUint8(output++, 0);
+            break;
         }
 
         paddingOffset += rowStride;
@@ -593,12 +561,9 @@
    * AdobeRGB1998 ICC Color Profile in that data if the preview is JPEG and
    * it has 'adodeRgb' color space.
    *
-   * @private
-   * @param {!PiexWasmPreviewImageMetadata} preview
-   * @param {!Uint8Array} view
-   * @return {!Uint8Array}
    */
-  createImageDataArray_(view, preview) {
+  private createImageDataArray_(
+      view: Uint8Array, preview: PiexWasmPreviewImageMetadata): Uint8Array {
     const jpeg = view.byteLength > 2 && view[0] === 0xff && view[1] === 0xd8;
 
     if (jpeg && preview.colorSpace === 'adobeRgb') {
@@ -617,34 +582,23 @@
    * Only number and string values are retained, and they are formatted for
    * presentation to the user.
    *
-   * @private
-   * @param {!PiexWasmImageResult} result
-   * @param {number} orientation - image EXIF orientation
-   * @return {?string}
+   * @param orientation - image EXIF orientation
    */
-  details_(result, orientation) {
+  private details_(result: PiexWasmImageResult, orientation: number): null
+      |string {
     const details = result.details;
     if (!details) {
       return null;
     }
 
-    /** @type {!Object<string|number, number|string>} */
-    const format = {};
-    /** @type {!Array<!Array<string|number>>} */
-    const entries = Object.entries(details);
-    for (const [key, value] of entries) {
+    const format: Record<string, string|number> = {};
+    for (const [key, value] of Object.entries(details)) {
       if (typeof value === 'string') {
-        // @ts-ignore: error TS2538: Type 'undefined' cannot be used as an index
-        // type.
         format[key] = value.replace(/\0+$/, '').trim();
       } else if (typeof value === 'number') {
         if (!Number.isInteger(value)) {
-          // @ts-ignore: error TS2538: Type 'undefined' cannot be used as an
-          // index type.
           format[key] = Number(value.toFixed(3).replace(/0+$/, ''));
         } else {
-          // @ts-ignore: error TS2538: Type 'undefined' cannot be used as an
-          // index type.
           format[key] = value;
         }
       }
@@ -652,8 +606,8 @@
 
     const usesWidthAsHeight = orientation >= 5;
     if (usesWidthAsHeight) {
-      const width = format['width'];
-      format['width'] = format['height'];
+      const width = format['width']!;
+      format['width'] = format['height']!;
       format['height'] = width;
     }
 
@@ -675,48 +629,45 @@
 /**
  * PiexLoader: is a namespace.
  */
-export const PiexLoader = {};
+export const PiexLoader = {
 
-/**
- * Loads a RAW image. Returns the image metadata and the image thumbnail in a
- * PiexLoaderResponse.
- *
- * piexModuleFailed() returns true if the Module is in an unrecoverable error
- * state. This is rare, but possible, and the only reliable way to recover is
- * to reload the page. Callback |onPiexModuleFailed| is used to indicate that
- * the caller should initiate failure recovery steps.
- *
- * @param {!ArrayBuffer} buffer
- * @param {VoidCallback} onPiexModuleFailed
- * @return {!Promise<!PiexLoaderResponse>}
- */
-PiexLoader.load = function(buffer, onPiexModuleFailed) {
-  /** @type {?ImageBuffer} */
-  let imageBuffer;
+  /**
+   * Loads a RAW image. Returns the image metadata and the image thumbnail in a
+   * PiexLoaderResponse.
+   *
+   * piexModuleFailed() returns true if the Module is in an unrecoverable error
+   * state. This is rare, but possible, and the only reliable way to recover is
+   * to reload the page. Callback |onPiexModuleFailed| is used to indicate that
+   * the caller should initiate failure recovery steps.
+   *
+   */
+  load(buffer: ArrayBuffer, onPiexModuleFailed: VoidCallback):
+      Promise<PiexLoaderResponse> {
+        let imageBuffer: ImageBuffer|null;
 
-  return piexModuleInitialized()
-      .then(() => {
-        if (piexModuleFailed()) {
-          throw new Error('piex wasm module failed');
-        }
-        imageBuffer = new ImageBuffer(buffer);
-        return imageBuffer.process();
-      })
-      .then((/** !PiexWasmImageResult */ result) => {
-        const buffer = /** @type {!ImageBuffer} */ (imageBuffer);
-        return new PiexLoaderResponse(buffer.preview(result));
-      })
-      .catch((error) => {
-        if (piexModuleFailed()) {
-          setTimeout(onPiexModuleFailed, 0);
-          return Promise.reject('piex wasm module failed');
-        }
-        console.warn('[PiexLoader] ' + error);
-        return Promise.reject(error);
-      })
-      .finally(() => {
-        imageBuffer && imageBuffer.close();
-      });
+        return piexModuleInitialized()
+            .then(() => {
+              if (piexModuleFailed()) {
+                throw new Error('piex wasm module failed');
+              }
+              imageBuffer = new ImageBuffer(buffer);
+              return imageBuffer.process();
+            })
+            .then((result: PiexWasmImageResult) => {
+              return new PiexLoaderResponse(imageBuffer!.preview(result));
+            })
+            .catch((error) => {
+              if (piexModuleFailed()) {
+                setTimeout(onPiexModuleFailed, 0);
+                return Promise.reject('piex wasm module failed');
+              }
+              console.warn('[PiexLoader] ' + error);
+              return Promise.reject(error);
+            })
+            .finally(() => {
+              imageBuffer && imageBuffer.close();
+            });
+      },
 };
 
 export const PIEX_LOADER_TEST_ONLY = {
diff --git a/ui/gfx/image/image_skia_util_ios.mm b/ui/gfx/image/image_skia_util_ios.mm
index 5ecc4f2..5799296 100644
--- a/ui/gfx/image/image_skia_util_ios.mm
+++ b/ui/gfx/image/image_skia_util_ios.mm
@@ -35,6 +35,14 @@
   SkBitmap bitmap(skia::CGImageToSkBitmap(image.CGImage,
                                           desired_size_for_scale,
                                           false));
+
+  // Resizing the image can fail.
+  // https://crbug.com/1184688, https://crbug.com/41495327
+  if (bitmap.empty()) {
+    return gfx::ImageSkiaRep();
+  }
+  CHECK_EQ(bitmap.colorType(), kN32_SkColorType);
+
   return gfx::ImageSkiaRep(bitmap, scale);
 }
 
diff --git a/ui/gl/gl_switches.cc b/ui/gl/gl_switches.cc
index 239ff372..28dd264c 100644
--- a/ui/gl/gl_switches.cc
+++ b/ui/gl/gl_switches.cc
@@ -250,7 +250,7 @@
 // Default to using ANGLE's Metal backend.
 BASE_FEATURE(kDefaultANGLEMetal,
              "DefaultANGLEMetal",
-#if BUILDFLAG(IS_IOS)
+#if BUILDFLAG(IS_IOS) || (BUILDFLAG(IS_MAC) && defined(ARCH_CPU_ARM64))
              base::FEATURE_ENABLED_BY_DEFAULT
 #else
              base::FEATURE_DISABLED_BY_DEFAULT
diff --git a/ui/native_theme/native_theme_win.cc b/ui/native_theme/native_theme_win.cc
index 6af4df9..9bc67e0 100644
--- a/ui/native_theme/native_theme_win.cc
+++ b/ui/native_theme/native_theme_win.cc
@@ -340,8 +340,7 @@
 
   memset(theme_handles_, 0, sizeof(theme_handles_));
 
-  if (theme_to_update && !IsForcedDarkMode() && !IsForcedHighContrast() &&
-      base::SequencedTaskRunner::HasCurrentDefault()) {
+  if (theme_to_update) {
     theme_to_update->set_use_dark_colors(ShouldUseDarkColors());
     theme_to_update->set_forced_colors(InForcedColorsMode());
     theme_to_update->set_preferred_color_scheme(GetPreferredColorScheme());
diff --git a/ui/ozone/platform/drm/gpu/drm_display.cc b/ui/ozone/platform/drm/gpu/drm_display.cc
index 38eaae5..a9d6cae9 100644
--- a/ui/ozone/platform/drm/gpu/drm_display.cc
+++ b/ui/ozone/platform/drm/gpu/drm_display.cc
@@ -308,29 +308,15 @@
   drm_->plane_manager()->SetColorTemperatureAdjustment(crtc_, cta);
 }
 
-void DrmDisplay::SetColorCalibration(
-    const display::ColorCalibration& calibration) {
-  drm_->plane_manager()->SetColorCalibration(crtc_, calibration);
-}
-
 void DrmDisplay::SetGammaAdjustment(
     const display::GammaAdjustment& adjustment) {
   drm_->plane_manager()->SetGammaAdjustment(crtc_, adjustment);
 }
 
-void DrmDisplay::SetColorMatrix(const std::vector<float>& color_matrix) {
-  // TODO(https://crbug.com/1505062): Remove callers of this function.
-}
-
 void DrmDisplay::SetBackgroundColor(const uint64_t background_color) {
   drm_->plane_manager()->SetBackgroundColor(crtc_, background_color);
 }
 
-void DrmDisplay::SetGammaCorrection(const display::GammaCurve& degamma,
-                                    const display::GammaCurve& gamma) {
-  // TODO(https://crbug.com/1505062): Remove callers of this function.
-}
-
 bool DrmDisplay::SetPrivacyScreen(bool enabled) {
   return privacy_screen_property_->SetPrivacyScreenProperty(enabled);
 }
diff --git a/ui/ozone/platform/drm/gpu/drm_display.h b/ui/ozone/platform/drm/gpu/drm_display.h
index 087dc606..89929f04 100644
--- a/ui/ozone/platform/drm/gpu/drm_display.h
+++ b/ui/ozone/platform/drm/gpu/drm_display.h
@@ -22,9 +22,7 @@
 
 namespace display {
 class DisplaySnapshot;
-class GammaCurve;
 struct ColorTemperatureAdjustment;
-struct ColorCalibration;
 struct GammaAdjustment;
 }  // namespace display
 
@@ -89,12 +87,8 @@
                     display::ContentProtectionMethod protection_method);
   void SetColorTemperatureAdjustment(
       const display::ColorTemperatureAdjustment& cta);
-  void SetColorCalibration(const display::ColorCalibration& calibration);
   void SetGammaAdjustment(const display::GammaAdjustment& adjustment);
-  void SetColorMatrix(const std::vector<float>& color_matrix);
   void SetBackgroundColor(const uint64_t background_color);
-  void SetGammaCorrection(const display::GammaCurve& degamma,
-                          const display::GammaCurve& gamma);
   bool SetPrivacyScreen(bool enabled);
   bool SetHdrOutputMetadata(const gfx::ColorSpace color_space);
   bool SetColorspaceProperty(const gfx::ColorSpace color_space);
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc
index 27466d7..838523e6 100644
--- a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc
+++ b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc
@@ -418,17 +418,6 @@
   display->SetColorTemperatureAdjustment(cta);
 }
 
-void DrmGpuDisplayManager::SetColorCalibration(
-    int64_t display_id,
-    const display::ColorCalibration& calibration) {
-  DrmDisplay* display = FindDisplay(display_id);
-  if (!display) {
-    LOG(WARNING) << __func__ << ": there is no display with ID " << display_id;
-    return;
-  }
-  display->SetColorCalibration(calibration);
-}
-
 void DrmGpuDisplayManager::SetGammaAdjustment(
     int64_t display_id,
     const display::GammaAdjustment& adjustment) {
@@ -440,18 +429,6 @@
   display->SetGammaAdjustment(adjustment);
 }
 
-void DrmGpuDisplayManager::SetColorMatrix(
-    int64_t display_id,
-    const std::vector<float>& color_matrix) {
-  DrmDisplay* display = FindDisplay(display_id);
-  if (!display) {
-    LOG(WARNING) << __func__ << ": there is no display with ID " << display_id;
-    return;
-  }
-
-  display->SetColorMatrix(color_matrix);
-}
-
 void DrmGpuDisplayManager::SetBackgroundColor(int64_t display_id,
                                               const uint64_t background_color) {
   DrmDisplay* display = FindDisplay(display_id);
@@ -463,18 +440,6 @@
   display->SetBackgroundColor(background_color);
 }
 
-void DrmGpuDisplayManager::SetGammaCorrection(
-    int64_t display_id,
-    const display::GammaCurve& degamma,
-    const display::GammaCurve& gamma) {
-  DrmDisplay* display = FindDisplay(display_id);
-  if (!display) {
-    LOG(WARNING) << __func__ << ": there is no display with ID " << display_id;
-    return;
-  }
-  display->SetGammaCorrection(degamma, gamma);
-}
-
 bool DrmGpuDisplayManager::SetPrivacyScreen(int64_t display_id, bool enabled) {
   DrmDisplay* display = FindDisplay(display_id);
   if (!display) {
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h
index 5188a51..3e5c5e1f 100644
--- a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h
+++ b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h
@@ -18,8 +18,6 @@
 using drmModeModeInfo = struct _drmModeModeInfo;
 
 namespace display {
-class GammaCurve;
-struct ColorCalibration;
 struct ColorTemperatureAdjustment;
 struct GammaAdjustment;
 }  // namespace display
@@ -70,16 +68,9 @@
   void SetColorTemperatureAdjustment(
       int64_t display_id,
       const display::ColorTemperatureAdjustment& cta);
-  void SetColorCalibration(int64_t display_id,
-                           const display::ColorCalibration& calibration);
   void SetGammaAdjustment(int64_t display_id,
                           const display::GammaAdjustment& adjustment);
-  void SetColorMatrix(int64_t display_id,
-                      const std::vector<float>& color_matrix);
   void SetBackgroundColor(int64_t display_id, const uint64_t background_color);
-  void SetGammaCorrection(int64_t display_id,
-                          const display::GammaCurve& degamma,
-                          const display::GammaCurve& gamma);
   bool SetPrivacyScreen(int64_t display_id, bool enabled);
 
  private:
diff --git a/ui/ozone/platform/drm/gpu/drm_thread.cc b/ui/ozone/platform/drm/gpu/drm_thread.cc
index 4707b12..720814f6 100644
--- a/ui/ozone/platform/drm/gpu/drm_thread.cc
+++ b/ui/ozone/platform/drm/gpu/drm_thread.cc
@@ -471,30 +471,11 @@
   display_manager_->SetColorTemperatureAdjustment(display_id, cta);
 }
 
-void DrmThread::SetColorCalibration(
-    int64_t display_id,
-    const display::ColorCalibration& calibration) {
-  display_manager_->SetColorCalibration(display_id, calibration);
-}
-
 void DrmThread::SetGammaAdjustment(int64_t display_id,
                                    const display::GammaAdjustment& adjustment) {
   display_manager_->SetGammaAdjustment(display_id, adjustment);
 }
 
-void DrmThread::SetColorMatrix(int64_t display_id,
-                               const std::vector<float>& color_matrix) {
-  TRACE_EVENT0("drm", "DrmThread::SetColorMatrix");
-  display_manager_->SetColorMatrix(display_id, color_matrix);
-}
-
-void DrmThread::SetGammaCorrection(int64_t display_id,
-                                   const display::GammaCurve& degamma,
-                                   const display::GammaCurve& gamma) {
-  TRACE_EVENT0("drm", "DrmThread::SetGammaCorrection");
-  display_manager_->SetGammaCorrection(display_id, degamma, gamma);
-}
-
 void DrmThread::SetPrivacyScreen(int64_t display_id,
                                  bool enabled,
                                  base::OnceCallback<void(bool)> callback) {
diff --git a/ui/ozone/platform/drm/gpu/drm_thread.h b/ui/ozone/platform/drm/gpu/drm_thread.h
index b16d028..4e341c4f 100644
--- a/ui/ozone/platform/drm/gpu/drm_thread.h
+++ b/ui/ozone/platform/drm/gpu/drm_thread.h
@@ -34,8 +34,6 @@
 }  // namespace base
 
 namespace display {
-class GammaCurve;
-struct ColorCalibration;
 struct ColorTemperatureAdjustment;
 struct GammaAdjustment;
 }  // namespace display
@@ -183,16 +181,8 @@
   void SetColorTemperatureAdjustment(
       int64_t display_id,
       const display::ColorTemperatureAdjustment& cta) override;
-  void SetColorCalibration(
-      int64_t display_id,
-      const display::ColorCalibration& calibration) override;
   void SetGammaAdjustment(int64_t display_id,
                           const display::GammaAdjustment& adjustment) override;
-  void SetColorMatrix(int64_t display_id,
-                      const std::vector<float>& color_matrix) override;
-  void SetGammaCorrection(int64_t display_id,
-                          const display::GammaCurve& degamma,
-                          const display::GammaCurve& gamma) override;
   void SetPrivacyScreen(int64_t display_id,
                         bool enabled,
                         base::OnceCallback<void(bool)> callback) override;
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc
index 200380c..574e2c3 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc
@@ -350,16 +350,6 @@
   UpdateAndCommitCrtcState(crtc_id, crtc_state);
 }
 
-void HardwareDisplayPlaneManager::SetColorCalibration(
-    uint32_t crtc_id,
-    const display::ColorCalibration& calibration) {
-  const auto crtc_index = LookupCrtcIndex(crtc_id);
-  DCHECK(crtc_index.has_value());
-  CrtcState* crtc_state = &crtc_state_[*crtc_index];
-  crtc_state->color_calibration = calibration;
-  UpdateAndCommitCrtcState(crtc_id, crtc_state);
-}
-
 void HardwareDisplayPlaneManager::SetGammaAdjustment(
     uint32_t crtc_id,
     const display::GammaAdjustment& adjustment) {
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h
index 30dad16..68434cb8 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h
@@ -162,10 +162,6 @@
       uint32_t crtc_id,
       const display::ColorTemperatureAdjustment& cta);
 
-  // Sets the color calibration information for a given CRTC.
-  void SetColorCalibration(uint32_t crtc_id,
-                           const display::ColorCalibration& calibration);
-
   // Sets the gamma adjustment for a given CRTC.
   void SetGammaAdjustment(uint32_t crtc_id,
                           const display::GammaAdjustment& adjustment);
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
index 539954da..b39ae3db 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
@@ -1053,44 +1053,6 @@
   }
 }
 
-TEST_P(HardwareDisplayPlaneManagerTest, ColorManagement_Profile) {
-  auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects(
-      /*crtc_count=*/1, /*planes_per_crtc=*/1);
-
-  // This test has full CTM, DEGAMMA, and GAMMA.
-  drm_state.crtc_properties[0].properties.push_back(
-      {.id = kCtmPropId, .value = 0});
-  drm_state.crtc_properties[0].properties.push_back(
-      {.id = kDegammaLutSizePropId, .value = 1});
-  drm_state.crtc_properties[0].properties.push_back(
-      {.id = kDegammaLutPropId, .value = 0});
-  drm_state.crtc_properties[0].properties.push_back(
-      {.id = kGammaLutSizePropId, .value = 1});
-  drm_state.crtc_properties[0].properties.push_back(
-      {.id = kGammaLutPropId, .value = 0});
-
-  // Color profile change will set all properties.
-  fake_drm_->InitializeState(drm_state, use_atomic_);
-  display::ColorCalibration calibration;
-  calibration.srgb_to_linear = display::GammaCurve::MakeGamma(2.2f);
-  calibration.linear_to_device = display::GammaCurve::MakeGamma(1.f / 2.2);
-  fake_drm_->plane_manager()->SetColorCalibration(
-      fake_drm_->crtc_property(0).id, calibration);
-
-  if (use_atomic_) {
-    HardwareDisplayPlaneList state;
-    PerformPageFlip(/*crtc_idx=*/0, &state);
-    EXPECT_EQ(2, fake_drm_->get_commit_count());
-    EXPECT_NE(0u, GetCrtcPropertyValue(fake_drm_->crtc_property(0).id, "CTM"));
-    EXPECT_NE(
-        0u, GetCrtcPropertyValue(fake_drm_->crtc_property(0).id, "GAMMA_LUT"));
-    EXPECT_NE(0u, GetCrtcPropertyValue(fake_drm_->crtc_property(0).id,
-                                       "DEGAMMA_LUT"));
-  } else {
-    EXPECT_EQ(3, fake_drm_->get_set_object_property_count());
-  }
-}
-
 TEST_P(HardwareDisplayPlaneManagerTest, ColorManagement_GammaAdjustment) {
   auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects(
       /*crtc_count=*/1, /*planes_per_crtc=*/1);
diff --git a/ui/ozone/platform/drm/host/drm_display_host.cc b/ui/ozone/platform/drm/host/drm_display_host.cc
index 510b4c1b..9b420552 100644
--- a/ui/ozone/platform/drm/host/drm_display_host.cc
+++ b/ui/ozone/platform/drm/host/drm_display_host.cc
@@ -106,25 +106,11 @@
   sender_->GpuSetColorTemperatureAdjustment(snapshot_->display_id(), cta);
 }
 
-void DrmDisplayHost::SetColorCalibration(
-    const display::ColorCalibration& calibration) {
-  sender_->GpuSetColorCalibration(snapshot_->display_id(), calibration);
-}
-
 void DrmDisplayHost::SetGammaAdjustment(
     const display::GammaAdjustment& adjustment) {
   sender_->GpuSetGammaAdjustment(snapshot_->display_id(), adjustment);
 }
 
-void DrmDisplayHost::SetColorMatrix(const std::vector<float>& color_matrix) {
-  sender_->GpuSetColorMatrix(snapshot_->display_id(), color_matrix);
-}
-
-void DrmDisplayHost::SetGammaCorrection(const display::GammaCurve& degamma,
-                                        const display::GammaCurve& gamma) {
-  sender_->GpuSetGammaCorrection(snapshot_->display_id(), degamma, gamma);
-}
-
 void DrmDisplayHost::SetPrivacyScreen(
     bool enabled,
     display::SetPrivacyScreenCallback callback) {
diff --git a/ui/ozone/platform/drm/host/drm_display_host.h b/ui/ozone/platform/drm/host/drm_display_host.h
index 5dc98e9..7e8820fb 100644
--- a/ui/ozone/platform/drm/host/drm_display_host.h
+++ b/ui/ozone/platform/drm/host/drm_display_host.h
@@ -44,11 +44,7 @@
                     display::SetHDCPStateCallback callback);
   void SetColorTemperatureAdjustment(
       const display::ColorTemperatureAdjustment& cta);
-  void SetColorCalibration(const display::ColorCalibration& calibration);
   void SetGammaAdjustment(const display::GammaAdjustment& adjustment);
-  void SetColorMatrix(const std::vector<float>& color_matrix);
-  void SetGammaCorrection(const display::GammaCurve& degamma,
-                          const display::GammaCurve& gamma);
   void SetPrivacyScreen(bool enabled,
                         display::SetPrivacyScreenCallback callback);
 
diff --git a/ui/ozone/platform/drm/host/drm_native_display_delegate.cc b/ui/ozone/platform/drm/host/drm_native_display_delegate.cc
index 966313a..ade9813f 100644
--- a/ui/ozone/platform/drm/host/drm_native_display_delegate.cc
+++ b/ui/ozone/platform/drm/host/drm_native_display_delegate.cc
@@ -89,13 +89,6 @@
   display->SetColorTemperatureAdjustment(cta);
 }
 
-void DrmNativeDisplayDelegate::SetColorCalibration(
-    int64_t display_id,
-    const display::ColorCalibration& calibration) {
-  DrmDisplayHost* display = display_manager_->GetDisplay(display_id);
-  display->SetColorCalibration(calibration);
-}
-
 void DrmNativeDisplayDelegate::SetGammaAdjustment(
     int64_t display_id,
     const display::GammaAdjustment& adjustment) {
@@ -103,23 +96,6 @@
   display->SetGammaAdjustment(adjustment);
 }
 
-bool DrmNativeDisplayDelegate::SetColorMatrix(
-    int64_t display_id,
-    const std::vector<float>& color_matrix) {
-  DrmDisplayHost* display = display_manager_->GetDisplay(display_id);
-  display->SetColorMatrix(color_matrix);
-  return true;
-}
-
-bool DrmNativeDisplayDelegate::SetGammaCorrection(
-    int64_t display_id,
-    const display::GammaCurve& degamma,
-    const display::GammaCurve& gamma) {
-  DrmDisplayHost* display = display_manager_->GetDisplay(display_id);
-  display->SetGammaCorrection(degamma, gamma);
-  return true;
-}
-
 void DrmNativeDisplayDelegate::SetPrivacyScreen(
     int64_t display_id,
     bool enabled,
diff --git a/ui/ozone/platform/drm/host/drm_native_display_delegate.h b/ui/ozone/platform/drm/host/drm_native_display_delegate.h
index 8a777c0..f9a1e499 100644
--- a/ui/ozone/platform/drm/host/drm_native_display_delegate.h
+++ b/ui/ozone/platform/drm/host/drm_native_display_delegate.h
@@ -49,16 +49,8 @@
   void SetColorTemperatureAdjustment(
       int64_t display_id,
       const display::ColorTemperatureAdjustment& cta) override;
-  void SetColorCalibration(
-      int64_t display_id,
-      const display::ColorCalibration& calibration) override;
   void SetGammaAdjustment(int64_t display_id,
                           const display::GammaAdjustment& gamma) override;
-  bool SetColorMatrix(int64_t display_id,
-                      const std::vector<float>& color_matrix) override;
-  bool SetGammaCorrection(int64_t display_id,
-                          const display::GammaCurve& degamma,
-                          const display::GammaCurve& gamma) override;
   void SetPrivacyScreen(int64_t display_id,
                         bool enabled,
                         display::SetPrivacyScreenCallback callback) override;
diff --git a/ui/ozone/platform/drm/host/gpu_thread_adapter.h b/ui/ozone/platform/drm/host/gpu_thread_adapter.h
index 78dd8f87..761e985c 100644
--- a/ui/ozone/platform/drm/host/gpu_thread_adapter.h
+++ b/ui/ozone/platform/drm/host/gpu_thread_adapter.h
@@ -63,17 +63,9 @@
   virtual void GpuSetColorTemperatureAdjustment(
       int64_t display_id,
       const display::ColorTemperatureAdjustment& cta) = 0;
-  virtual void GpuSetColorCalibration(
-      int64_t display_id,
-      const display::ColorCalibration& calibration) = 0;
   virtual void GpuSetGammaAdjustment(
       int64_t display_id,
       const display::GammaAdjustment& adjustment) = 0;
-  virtual bool GpuSetColorMatrix(int64_t display_id,
-                                 const std::vector<float>& color_matrix) = 0;
-  virtual bool GpuSetGammaCorrection(int64_t display_id,
-                                     const display::GammaCurve& degamma,
-                                     const display::GammaCurve& gamma) = 0;
   virtual void GpuSetPrivacyScreen(
       int64_t display_id,
       bool enabled,
diff --git a/ui/ozone/platform/drm/host/host_drm_device.cc b/ui/ozone/platform/drm/host/host_drm_device.cc
index f336da2..9e2bab1 100644
--- a/ui/ozone/platform/drm/host/host_drm_device.cc
+++ b/ui/ozone/platform/drm/host/host_drm_device.cc
@@ -252,15 +252,6 @@
   drm_device_->SetColorTemperatureAdjustment(display_id, cta);
 }
 
-void HostDrmDevice::GpuSetColorCalibration(
-    int64_t display_id,
-    const display::ColorCalibration& calibration) {
-  if (!IsConnected()) {
-    return;
-  }
-  drm_device_->SetColorCalibration(display_id, calibration);
-}
-
 void HostDrmDevice::GpuSetGammaAdjustment(
     int64_t display_id,
     const display::GammaAdjustment& adjustment) {
@@ -270,28 +261,6 @@
   drm_device_->SetGammaAdjustment(display_id, adjustment);
 }
 
-bool HostDrmDevice::GpuSetColorMatrix(int64_t display_id,
-                                      const std::vector<float>& color_matrix) {
-  DCHECK_CALLED_ON_VALID_THREAD(on_ui_thread_);
-  if (!IsConnected())
-    return false;
-
-  drm_device_->SetColorMatrix(display_id, color_matrix);
-  return true;
-}
-
-bool HostDrmDevice::GpuSetGammaCorrection(
-    int64_t display_id,
-    const display::GammaCurve& degamma_lut,
-    const display::GammaCurve& gamma_lut) {
-  DCHECK_CALLED_ON_VALID_THREAD(on_ui_thread_);
-  if (!IsConnected())
-    return false;
-
-  drm_device_->SetGammaCorrection(display_id, degamma_lut, gamma_lut);
-  return true;
-}
-
 void HostDrmDevice::GpuSetPrivacyScreen(
     int64_t display_id,
     bool enabled,
diff --git a/ui/ozone/platform/drm/host/host_drm_device.h b/ui/ozone/platform/drm/host/host_drm_device.h
index fa2f3f0..f769cb6 100644
--- a/ui/ozone/platform/drm/host/host_drm_device.h
+++ b/ui/ozone/platform/drm/host/host_drm_device.h
@@ -80,17 +80,9 @@
   void GpuSetColorTemperatureAdjustment(
       int64_t display_id,
       const display::ColorTemperatureAdjustment& cta) override;
-  void GpuSetColorCalibration(
-      int64_t display_id,
-      const display::ColorCalibration& calibration) override;
   void GpuSetGammaAdjustment(
       int64_t display_id,
       const display::GammaAdjustment& adjustment) override;
-  bool GpuSetColorMatrix(int64_t display_id,
-                         const std::vector<float>& color_matrix) override;
-  bool GpuSetGammaCorrection(int64_t display_id,
-                             const display::GammaCurve& degamma,
-                             const display::GammaCurve& gamma) override;
   void GpuSetPrivacyScreen(int64_t display_id,
                            bool enabled,
                            display::SetPrivacyScreenCallback callback) override;
diff --git a/ui/ozone/platform/drm/mojom/drm_device.mojom b/ui/ozone/platform/drm/mojom/drm_device.mojom
index 37a930cf..db88994 100644
--- a/ui/ozone/platform/drm/mojom/drm_device.mojom
+++ b/ui/ozone/platform/drm/mojom/drm_device.mojom
@@ -87,24 +87,10 @@
   SetColorTemperatureAdjustment(int64 display_id,
                                 display.mojom.ColorTemperatureAdjustment cta);
 
-  // Set the color calibration for the specified display.
-  SetColorCalibration(int64 display_id,
-                      display.mojom.ColorCalibration calibration);
-
   // Set the display profile space gamma adjustment for the specified display.
   SetGammaAdjustment(int64 display_id,
                      display.mojom.GammaAdjustment adjustment);
 
-  // Sets a 3x3 color transform matrix on the display hardware.
-  // TODO: Consider using a different type for the color matrix.
-  // https://crbug.com/846975.
-  SetColorMatrix(int64 display_id, array<float, 9> color_matrix);
-
-  // Sets a color correction gamma lookup table.
-  SetGammaCorrection(int64 display_id,
-                     display.mojom.GammaCurve degamma,
-                     display.mojom.GammaCurve gamma);
-
   // Sets the state of the privacy screen feature, returns whether or not the
   // configuration was successful.
   SetPrivacyScreen(int64 display_id, bool enabled) => (bool success);
diff --git a/ui/ozone/platform/wayland/OWNERS b/ui/ozone/platform/wayland/OWNERS
index c6267f99..85973bb 100644
--- a/ui/ozone/platform/wayland/OWNERS
+++ b/ui/ozone/platform/wayland/OWNERS
@@ -6,6 +6,9 @@
 max@igalia.com
 tluk@chromium.org
 
+# For graphics, synchronization
+edcourtney@chromium.org
+
 # For Keyboard / TextInput (IME)
 hidehiko@chromium.org
 
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
index 48dc3c0..72ca7cee 100644
--- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
+++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
@@ -883,6 +883,10 @@
 
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 
+void WaylandToplevelWindow::RoundTripQueue() {
+  connection()->RoundTripQueue();
+}
+
 void WaylandToplevelWindow::ShowSnapPreview(
     WaylandWindowSnapDirection snap_direction,
     bool allow_haptic_feedback) {
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.h b/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
index f2c9f67..85142285 100644
--- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
+++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
@@ -161,6 +161,7 @@
   gfx::RoundedCornersF GetWindowCornersRadii() override;
   void SetShadowCornersRadii(const gfx::RoundedCornersF& radii) override;
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+  void RoundTripQueue() override;
   void ShowSnapPreview(WaylandWindowSnapDirection snap,
                        bool allow_haptic_feedback) override;
   void CommitSnap(WaylandWindowSnapDirection snap, float snap_ratio) override;
diff --git a/ui/platform_window/extensions/wayland_extension.h b/ui/platform_window/extensions/wayland_extension.h
index 3a1ae75..7713b08 100644
--- a/ui/platform_window/extensions/wayland_extension.h
+++ b/ui/platform_window/extensions/wayland_extension.h
@@ -72,6 +72,10 @@
 
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 
+  // Wait for a Wayland roundtrip to ensure all side effects have been
+  // processed.
+  virtual void RoundTripQueue() = 0;
+
   // Signals the underneath platform to shows a preview for the given window
   // snap direction. `allow_haptic_feedback` indicates if it should send haptic
   // feedback.
diff --git a/ui/views/animation/ink_drop_ripple_unittest.cc b/ui/views/animation/ink_drop_ripple_unittest.cc
index 78e3989..800c449 100644
--- a/ui/views/animation/ink_drop_ripple_unittest.cc
+++ b/ui/views/animation/ink_drop_ripple_unittest.cc
@@ -49,6 +49,12 @@
 
   ~InkDropRippleTest() override;
 
+  void ResetInkDropRipple() {
+    observer_.set_ink_drop_ripple(nullptr);
+    test_api_.reset();
+    ink_drop_ripple_.reset();
+  }
+
  protected:
   TestInkDropRippleObserver observer_;
 
@@ -87,7 +93,9 @@
   test_api_->SetDisableAnimationTimers(true);
 }
 
-InkDropRippleTest::~InkDropRippleTest() = default;
+InkDropRippleTest::~InkDropRippleTest() {
+  ResetInkDropRipple();
+}
 
 // Note: First argument is optional and intentionally left blank.
 // (it's a prefix for the generated test cases)
@@ -187,7 +195,7 @@
     return;
 
   ink_drop_ripple_->AnimateToState(views::InkDropState::ACTION_PENDING);
-  ink_drop_ripple_.reset();
+  ResetInkDropRipple();
   EXPECT_EQ(1, observer_.last_animation_started_ordinal());
   EXPECT_EQ(2, observer_.last_animation_ended_ordinal());
   EXPECT_EQ(views::InkDropState::ACTION_PENDING,
diff --git a/ui/views/animation/test/test_ink_drop_ripple_observer.h b/ui/views/animation/test/test_ink_drop_ripple_observer.h
index bea5cd1e..7a13c72 100644
--- a/ui/views/animation/test/test_ink_drop_ripple_observer.h
+++ b/ui/views/animation/test/test_ink_drop_ripple_observer.h
@@ -62,7 +62,7 @@
   InkDropState target_state_at_last_animation_ended_ = InkDropState::HIDDEN;
 
   // An InkDropRipple to spy info from when notifications are handled.
-  raw_ptr<InkDropRipple, DanglingUntriaged> ink_drop_ripple_;
+  raw_ptr<InkDropRipple> ink_drop_ripple_ = nullptr;
 };
 
 }  // namespace test
diff --git a/ui/views/controls/button/label_button_image_container.cc b/ui/views/controls/button/label_button_image_container.cc
index 809cef7..831fabd 100644
--- a/ui/views/controls/button/label_button_image_container.cc
+++ b/ui/views/controls/button/label_button_image_container.cc
@@ -12,21 +12,23 @@
 std::unique_ptr<View> SingleImageContainer::CreateView() {
   std::unique_ptr<ImageView> view = std::make_unique<ImageView>();
   view->SetCanProcessEventsWithinSubtree(false);
-  image_ = view.get();
+  image_view_tracker_.SetView(view.get());
   return view;
 }
 
 View* SingleImageContainer::GetView() {
-  return image_;
+  return image_view_tracker_.view();
 }
 
 const View* SingleImageContainer::GetView() const {
-  return image_;
+  return image_view_tracker_.view();
 }
 
 void SingleImageContainer::UpdateImage(const LabelButton* button) {
-  image_->SetImage(ui::ImageModel::FromImageSkia(
-      button->GetImage(button->GetVisualState())));
+  if (auto* view = image_view_tracker_.view(); view) {
+    static_cast<ImageView*>(view)->SetImage(ui::ImageModel::FromImageSkia(
+        button->GetImage(button->GetVisualState())));
+  }
 }
 
 }  // namespace views
diff --git a/ui/views/controls/button/label_button_image_container.h b/ui/views/controls/button/label_button_image_container.h
index 244a417..489d330 100644
--- a/ui/views/controls/button/label_button_image_container.h
+++ b/ui/views/controls/button/label_button_image_container.h
@@ -8,14 +8,22 @@
 #include <memory>
 
 #include "base/memory/raw_ptr.h"
+#include "ui/views/view_tracker.h"
 #include "ui/views/views_export.h"
 
 namespace views {
 
 class View;
-class ImageView;
 class LabelButton;
 
+// Abstract interface used by LabelButton to handle updates to the button
+// image(s). This interface lets callers configure the button to have one image
+// or multiple without LabelButton itself needing to understand the details.
+// LabelButton can simply call CreateView() to get a view it can add as a child,
+// then call UpdateImage() any time state changes in such a way that the
+// image(s) might need to be updated. Concrete instances of this class are
+// responsible for laying out any image(s) and updating them in response to
+// calls to UpdateImage(), as well as any other relevant signals.
 class VIEWS_EXPORT LabelButtonImageContainer {
  public:
   LabelButtonImageContainer() = default;
@@ -24,6 +32,8 @@
       delete;
   virtual ~LabelButtonImageContainer() = default;
 
+  // Returns a view holding whatever image(s) are desired. Calls to
+  // UpdateImage() outside this view's lifetime will have no effect.
   virtual std::unique_ptr<View> CreateView() = 0;
 
   // Gets a pointer to a previously created view. Returns nullptr if no view was
@@ -31,10 +41,15 @@
   virtual View* GetView() = 0;
   virtual const View* GetView() const = 0;
 
-  // Updates image based on `ButtonState` of the LabelButton.
+  // Called when image(s) in the created view may need updating. `button` is the
+  // LabelButton which is displaying the images. Subclasses should respond to
+  // this by updating any image(s) appropriately based on the button's current
+  // state and any other state the container is tracking.
   virtual void UpdateImage(const LabelButton* button) = 0;
 };
 
+// The common-case implementation of LabelButtonImageContainer, which provides a
+// single image that tracks the LabelButton's ButtonState.
 class VIEWS_EXPORT SingleImageContainer final
     : public LabelButtonImageContainer {
  public:
@@ -50,7 +65,7 @@
   void UpdateImage(const LabelButton* button) override;
 
  private:
-  raw_ptr<ImageView> image_;
+  views::ViewTracker image_view_tracker_;
 };
 
 }  // namespace views
diff --git a/chrome/test/data/webui/print_preview/.eslintrc.js b/ui/webui/resources/.eslintrc.js
similarity index 65%
rename from chrome/test/data/webui/print_preview/.eslintrc.js
rename to ui/webui/resources/.eslintrc.js
index 43c2c942..1c35c8a5 100644
--- a/chrome/test/data/webui/print_preview/.eslintrc.js
+++ b/ui/webui/resources/.eslintrc.js
@@ -3,5 +3,5 @@
 // found in the LICENSE file.
 
 module.exports = {
-  'extends' : '../../../../../ui/webui/resources/tools/eslint_import_type.config.js',
+  'extends' : './tools/eslint_import_type.config.js',
 };
diff --git a/ui/webui/resources/cr_components/app_management/browser_proxy.ts b/ui/webui/resources/cr_components/app_management/browser_proxy.ts
index 725836a..bcf6823 100644
--- a/ui/webui/resources/cr_components/app_management/browser_proxy.ts
+++ b/ui/webui/resources/cr_components/app_management/browser_proxy.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {PageCallbackRouter, PageHandlerFactory, PageHandlerInterface, PageHandlerRemote} from './app_management.mojom-webui.js';
+import type {PageHandlerInterface} from './app_management.mojom-webui.js';
+import {PageCallbackRouter, PageHandlerFactory, PageHandlerRemote} from './app_management.mojom-webui.js';
 
 export class BrowserProxy {
   callbackRouter: PageCallbackRouter;
diff --git a/ui/webui/resources/cr_components/app_management/constants.ts b/ui/webui/resources/cr_components/app_management/constants.ts
index 9a838bd..6b48affb 100644
--- a/ui/webui/resources/cr_components/app_management/constants.ts
+++ b/ui/webui/resources/cr_components/app_management/constants.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {App} from './app_management.mojom-webui.js';
+import type {App} from './app_management.mojom-webui.js';
 
 export {AppType, InstallReason, InstallSource, RunOnOsLogin, RunOnOsLoginMode, WindowMode} from './app_management.mojom-webui.js';
 
diff --git a/ui/webui/resources/cr_components/app_management/permission_constants.ts b/ui/webui/resources/cr_components/app_management/permission_constants.ts
index eb20eee..a563583 100644
--- a/ui/webui/resources/cr_components/app_management/permission_constants.ts
+++ b/ui/webui/resources/cr_components/app_management/permission_constants.ts
@@ -2,6 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {PermissionType} from './app_management.mojom-webui.js';
+import type {PermissionType} from './app_management.mojom-webui.js';
 
 export type PermissionTypeIndex = keyof typeof PermissionType;
diff --git a/ui/webui/resources/cr_components/app_management/permission_util.ts b/ui/webui/resources/cr_components/app_management/permission_util.ts
index 908ec81..d1851d2 100644
--- a/ui/webui/resources/cr_components/app_management/permission_util.ts
+++ b/ui/webui/resources/cr_components/app_management/permission_util.ts
@@ -4,7 +4,8 @@
 
 import {assert, assertNotReached} from 'chrome://resources/js/assert.js';
 
-import {Permission, PermissionType, PermissionValue, TriState} from './app_management.mojom-webui.js';
+import type {Permission, PermissionType, PermissionValue} from './app_management.mojom-webui.js';
+import {TriState} from './app_management.mojom-webui.js';
 
 export function createPermission(
     permissionType: PermissionType, value: PermissionValue,
diff --git a/ui/webui/resources/cr_components/app_management/util.ts b/ui/webui/resources/cr_components/app_management/util.ts
index 352ed9a..8765caa7 100644
--- a/ui/webui/resources/cr_components/app_management/util.ts
+++ b/ui/webui/resources/cr_components/app_management/util.ts
@@ -4,10 +4,11 @@
 
 import {assert, assertNotReached} from 'chrome://resources/js/assert.js';
 
-import {App, Permission, PermissionType, TriState} from './app_management.mojom-webui.js';
+import type {App, Permission} from './app_management.mojom-webui.js';
+import {PermissionType, TriState} from './app_management.mojom-webui.js';
 import {BrowserProxy} from './browser_proxy.js';
 import {AppManagementUserAction, AppType} from './constants.js';
-import {PermissionTypeIndex} from './permission_constants.js';
+import type {PermissionTypeIndex} from './permission_constants.js';
 import {isBoolValue, isPermissionEnabled, isTriStateValue} from './permission_util.js';
 
 /**
diff --git a/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.ts b/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.ts
index 324eed24..4bb63e4 100644
--- a/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.ts
+++ b/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.ts
@@ -14,15 +14,16 @@
 import 'chrome://resources/polymer/v3_0/paper-spinner/paper-spinner-lite.js';
 import './certificate_shared.css.js';
 
-import {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
-import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import type {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
+import type {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {PaperSpinnerLiteElement} from 'chrome://resources/polymer/v3_0/paper-spinner/paper-spinner-lite.js';
+import type {PaperSpinnerLiteElement} from 'chrome://resources/polymer/v3_0/paper-spinner/paper-spinner-lite.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getTemplate} from './ca_trust_edit_dialog.html.js';
-import {CaTrustInfo, CertificatesBrowserProxy, CertificatesBrowserProxyImpl, CertificateSubnode, NewCertificateSubNode} from './certificates_browser_proxy.js';
+import type {CaTrustInfo, CertificatesBrowserProxy, CertificateSubnode, NewCertificateSubNode} from './certificates_browser_proxy.js';
+import {CertificatesBrowserProxyImpl} from './certificates_browser_proxy.js';
 
 export interface CaTrustEditDialogElement {
   $: {
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.ts b/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.ts
index 76466f8..cd6808d0 100644
--- a/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.ts
+++ b/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.ts
@@ -10,14 +10,15 @@
 import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
 import './certificate_shared.css.js';
 
-import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import type {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {assertNotReached} from 'chrome://resources/js/assert.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getTemplate} from './certificate_delete_confirmation_dialog.html.js';
-import {CertificatesBrowserProxyImpl, CertificateSubnode, CertificateType} from './certificates_browser_proxy.js';
+import type {CertificateSubnode} from './certificates_browser_proxy.js';
+import {CertificatesBrowserProxyImpl, CertificateType} from './certificates_browser_proxy.js';
 
 export interface CertificateDeleteConfirmationDialogElement {
   $: {
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificate_entry.ts b/ui/webui/resources/cr_components/certificate_manager/certificate_entry.ts
index 65d2844..7ac1190c 100644
--- a/ui/webui/resources/cr_components/certificate_manager/certificate_entry.ts
+++ b/ui/webui/resources/cr_components/certificate_manager/certificate_entry.ts
@@ -16,7 +16,7 @@
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getTemplate} from './certificate_entry.html.js';
-import {CertificatesOrgGroup, CertificateType} from './certificates_browser_proxy.js';
+import type {CertificatesOrgGroup, CertificateType} from './certificates_browser_proxy.js';
 
 const CertificateEntryElementBase = I18nMixin(PolymerElement);
 
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificate_list.ts b/ui/webui/resources/cr_components/certificate_manager/certificate_list.ts
index 2b5e665..187ad0d 100644
--- a/ui/webui/resources/cr_components/certificate_manager/certificate_list.ts
+++ b/ui/webui/resources/cr_components/certificate_manager/certificate_list.ts
@@ -18,7 +18,8 @@
 
 import {getTemplate} from './certificate_list.html.js';
 import {CertificateAction, CertificateActionEvent} from './certificate_manager_types.js';
-import {CertificatesBrowserProxyImpl, CertificatesError, CertificatesImportError, CertificatesOrgGroup, CertificateType, NewCertificateSubNode} from './certificates_browser_proxy.js';
+import type {CertificatesError, CertificatesImportError, CertificatesOrgGroup, NewCertificateSubNode} from './certificates_browser_proxy.js';
+import {CertificatesBrowserProxyImpl, CertificateType} from './certificates_browser_proxy.js';
 
 export interface CertificateListElement {
   $: {
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificate_manager.ts b/ui/webui/resources/cr_components/certificate_manager/certificate_manager.ts
index 5ea86f7..3c68496 100644
--- a/ui/webui/resources/cr_components/certificate_manager/certificate_manager.ts
+++ b/ui/webui/resources/cr_components/certificate_manager/certificate_manager.ts
@@ -27,7 +27,8 @@
 
 import {getTemplate} from './certificate_manager.html.js';
 import {CertificateAction, CertificateActionEvent} from './certificate_manager_types.js';
-import {CertificatesBrowserProxyImpl, CertificatesError, CertificatesImportError, CertificatesOrgGroup, CertificateSubnode, CertificateType, NewCertificateSubNode} from './certificates_browser_proxy.js';
+import type {CertificatesError, CertificatesImportError, CertificatesOrgGroup, CertificateSubnode, NewCertificateSubNode} from './certificates_browser_proxy.js';
+import {CertificatesBrowserProxyImpl, CertificateType} from './certificates_browser_proxy.js';
 
 const CertificateManagerElementBase =
     WebUiListenerMixin(I18nMixin(PolymerElement));
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificate_manager_types.ts b/ui/webui/resources/cr_components/certificate_manager/certificate_manager_types.ts
index 7b73e35..45ebfafc 100644
--- a/ui/webui/resources/cr_components/certificate_manager/certificate_manager_types.ts
+++ b/ui/webui/resources/cr_components/certificate_manager/certificate_manager_types.ts
@@ -8,9 +8,9 @@
 
 // clang-format off
 // <if expr="is_chromeos">
-import {CertificateProvisioningProcess} from './certificate_provisioning_browser_proxy.js';
+import type {CertificateProvisioningProcess} from './certificate_provisioning_browser_proxy.js';
 // </if>
-import {CertificatesError, CertificatesImportError,CertificateSubnode, CertificateType, NewCertificateSubNode} from './certificates_browser_proxy.js';
+import type {CertificatesError, CertificatesImportError,CertificateSubnode, CertificateType, NewCertificateSubNode} from './certificates_browser_proxy.js';
 // clang-format on
 
 /**
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificate_password_decryption_dialog.ts b/ui/webui/resources/cr_components/certificate_manager/certificate_password_decryption_dialog.ts
index 615b4bf..94fb9b1 100644
--- a/ui/webui/resources/cr_components/certificate_manager/certificate_password_decryption_dialog.ts
+++ b/ui/webui/resources/cr_components/certificate_manager/certificate_password_decryption_dialog.ts
@@ -11,8 +11,8 @@
 import 'chrome://resources/cr_elements/cr_input/cr_input.js';
 import './certificate_shared.css.js';
 
-import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
-import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import type {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import type {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificate_password_encryption_dialog.ts b/ui/webui/resources/cr_components/certificate_manager/certificate_password_encryption_dialog.ts
index abe4c98f..0291f0d1 100644
--- a/ui/webui/resources/cr_components/certificate_manager/certificate_password_encryption_dialog.ts
+++ b/ui/webui/resources/cr_components/certificate_manager/certificate_password_encryption_dialog.ts
@@ -12,8 +12,8 @@
 import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
 import './certificate_shared.css.js';
 
-import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
-import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import type {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import type {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificate_provisioning_details_dialog.ts b/ui/webui/resources/cr_components/certificate_manager/certificate_provisioning_details_dialog.ts
index 206e9b2..6ebd9a8 100644
--- a/ui/webui/resources/cr_components/certificate_manager/certificate_provisioning_details_dialog.ts
+++ b/ui/webui/resources/cr_components/certificate_manager/certificate_provisioning_details_dialog.ts
@@ -10,11 +10,12 @@
 import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
 import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
 
-import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import type {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {CertificateProvisioningBrowserProxyImpl, CertificateProvisioningProcess} from './certificate_provisioning_browser_proxy.js';
+import type {CertificateProvisioningProcess} from './certificate_provisioning_browser_proxy.js';
+import {CertificateProvisioningBrowserProxyImpl} from './certificate_provisioning_browser_proxy.js';
 import {getTemplate} from './certificate_provisioning_details_dialog.html.js';
 
 export interface CertificateProvisioningDetailsDialogElement {
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificate_provisioning_entry.ts b/ui/webui/resources/cr_components/certificate_manager/certificate_provisioning_entry.ts
index f3862f5..dd863fe7 100644
--- a/ui/webui/resources/cr_components/certificate_manager/certificate_provisioning_entry.ts
+++ b/ui/webui/resources/cr_components/certificate_manager/certificate_provisioning_entry.ts
@@ -12,13 +12,13 @@
 import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
 import './certificate_shared.css.js';
 
-import {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
-import {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
+import type {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
+import type {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {CertificateProvisioningViewDetailsActionEvent} from './certificate_manager_types.js';
-import {CertificateProvisioningProcess} from './certificate_provisioning_browser_proxy.js';
+import type {CertificateProvisioningProcess} from './certificate_provisioning_browser_proxy.js';
 import {getTemplate} from './certificate_provisioning_entry.html.js';
 
 export interface CertificateProvisioningEntryElement {
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificate_provisioning_list.ts b/ui/webui/resources/cr_components/certificate_manager/certificate_provisioning_list.ts
index ecd7708..1c2e6c95 100644
--- a/ui/webui/resources/cr_components/certificate_manager/certificate_provisioning_list.ts
+++ b/ui/webui/resources/cr_components/certificate_manager/certificate_provisioning_list.ts
@@ -17,7 +17,8 @@
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {CertificateProvisioningViewDetailsActionEvent} from './certificate_manager_types.js';
-import {CertificateProvisioningBrowserProxyImpl, CertificateProvisioningProcess} from './certificate_provisioning_browser_proxy.js';
+import type {CertificateProvisioningProcess} from './certificate_provisioning_browser_proxy.js';
+import {CertificateProvisioningBrowserProxyImpl} from './certificate_provisioning_browser_proxy.js';
 import {getTemplate} from './certificate_provisioning_list.html.js';
 
 const CertificateProvisioningListElementBase =
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.ts b/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.ts
index bb0a8206..885f86c9 100644
--- a/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.ts
+++ b/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.ts
@@ -13,15 +13,16 @@
 import 'chrome://resources/cr_elements/icons.html.js';
 import './certificate_shared.css.js';
 
-import {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
-import {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
+import type {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
+import type {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {CrPolicyIndicatorType} from 'chrome://resources/cr_elements/policy/cr_policy_indicator_mixin.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {CertificateAction, CertificateActionEvent} from './certificate_manager_types.js';
 import {getTemplate} from './certificate_subentry.html.js';
-import {CertificatesBrowserProxy, CertificatesBrowserProxyImpl, CertificatesError, CertificateSubnode, CertificateType} from './certificates_browser_proxy.js';
+import type {CertificatesBrowserProxy, CertificatesError, CertificateSubnode} from './certificates_browser_proxy.js';
+import {CertificatesBrowserProxyImpl, CertificateType} from './certificates_browser_proxy.js';
 
 export interface CertificateSubentryElement {
   $: {
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificates_error_dialog.ts b/ui/webui/resources/cr_components/certificate_manager/certificates_error_dialog.ts
index d3f1d99..d04b765 100644
--- a/ui/webui/resources/cr_components/certificate_manager/certificates_error_dialog.ts
+++ b/ui/webui/resources/cr_components/certificate_manager/certificates_error_dialog.ts
@@ -10,12 +10,12 @@
 import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
 import './certificate_shared.css.js';
 
-import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import type {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {CertificatesError, CertificatesImportError} from './certificates_browser_proxy.js';
+import type {CertificatesError, CertificatesImportError} from './certificates_browser_proxy.js';
 import {getTemplate} from './certificates_error_dialog.html.js';
 
 interface CertificatesErrorDialogElement {
diff --git a/ui/webui/resources/cr_components/customize_color_scheme_mode/customize_color_scheme_mode.ts b/ui/webui/resources/cr_components/customize_color_scheme_mode/customize_color_scheme_mode.ts
index 8b2fc7c..d0a7588 100644
--- a/ui/webui/resources/cr_components/customize_color_scheme_mode/customize_color_scheme_mode.ts
+++ b/ui/webui/resources/cr_components/customize_color_scheme_mode/customize_color_scheme_mode.ts
@@ -12,7 +12,8 @@
 
 import {CustomizeColorSchemeModeBrowserProxy} from './browser_proxy.js';
 import {getTemplate} from './customize_color_scheme_mode.html.js';
-import {ColorSchemeMode, CustomizeColorSchemeModeClientCallbackRouter, CustomizeColorSchemeModeHandlerInterface} from './customize_color_scheme_mode.mojom-webui.js';
+import type {CustomizeColorSchemeModeClientCallbackRouter, CustomizeColorSchemeModeHandlerInterface} from './customize_color_scheme_mode.mojom-webui.js';
+import {ColorSchemeMode} from './customize_color_scheme_mode.mojom-webui.js';
 
 export interface ColorSchemeModeOption {
   id: string;
diff --git a/ui/webui/resources/cr_components/customize_themes/browser_proxy.ts b/ui/webui/resources/cr_components/customize_themes/browser_proxy.ts
index d0bb8c2..b703d77 100644
--- a/ui/webui/resources/cr_components/customize_themes/browser_proxy.ts
+++ b/ui/webui/resources/cr_components/customize_themes/browser_proxy.ts
@@ -7,7 +7,8 @@
  * interact with the browser.
  */
 
-import {CustomizeThemesClientCallbackRouter, CustomizeThemesHandlerFactory, CustomizeThemesHandlerInterface, CustomizeThemesHandlerRemote} from './customize_themes.mojom-webui.js';
+import type {CustomizeThemesHandlerInterface} from './customize_themes.mojom-webui.js';
+import {CustomizeThemesClientCallbackRouter, CustomizeThemesHandlerFactory, CustomizeThemesHandlerRemote} from './customize_themes.mojom-webui.js';
 
 export interface CustomizeThemesBrowserProxy {
   handler(): CustomizeThemesHandlerInterface;
diff --git a/ui/webui/resources/cr_components/customize_themes/customize_themes.ts b/ui/webui/resources/cr_components/customize_themes/customize_themes.ts
index e9ede54..dfc06b0 100644
--- a/ui/webui/resources/cr_components/customize_themes/customize_themes.ts
+++ b/ui/webui/resources/cr_components/customize_themes/customize_themes.ts
@@ -15,14 +15,15 @@
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {hexColorToSkColor, skColorToRgba} from 'chrome://resources/js/color_utils.js';
-import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
-import {DomRepeat} from 'chrome://resources/polymer/v3_0/polymer/lib/elements/dom-repeat.js';
+import type {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
+import type {DomRepeat} from 'chrome://resources/polymer/v3_0/polymer/lib/elements/dom-repeat.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {CustomizeThemesBrowserProxyImpl} from './browser_proxy.js';
 import {getTemplate} from './customize_themes.html.js';
-import {ChromeTheme, CustomizeThemesClientCallbackRouter, CustomizeThemesHandlerInterface, Theme, ThemeType} from './customize_themes.mojom-webui.js';
-import {ThemeIconElement} from './theme_icon.js';
+import type {ChromeTheme, CustomizeThemesClientCallbackRouter, CustomizeThemesHandlerInterface, Theme} from './customize_themes.mojom-webui.js';
+import {ThemeType} from './customize_themes.mojom-webui.js';
+import type {ThemeIconElement} from './theme_icon.js';
 
 export interface CustomizeThemesElement {
   $: {
diff --git a/ui/webui/resources/cr_components/help_bubble/help_bubble.ts b/ui/webui/resources/cr_components/help_bubble/help_bubble.ts
index b2451919..4a3d9c8 100644
--- a/ui/webui/resources/cr_components/help_bubble/help_bubble.ts
+++ b/ui/webui/resources/cr_components/help_bubble/help_bubble.ts
@@ -16,15 +16,17 @@
 import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
 import './help_bubble_icons.html.js';
 
-import {CrButtonElement} from '//resources/cr_elements/cr_button/cr_button.js';
-import {CrIconButtonElement} from '//resources/cr_elements/cr_icon_button/cr_icon_button.js';
+import type {CrButtonElement} from '//resources/cr_elements/cr_button/cr_button.js';
+import type {CrIconButtonElement} from '//resources/cr_elements/cr_icon_button/cr_icon_button.js';
 import {assert, assertNotReached} from '//resources/js/assert.js';
 import {isWindows} from '//resources/js/platform.js';
-import {DomRepeat, DomRepeatEvent, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {InsetsF} from 'chrome://resources/mojo/ui/gfx/geometry/mojom/geometry.mojom-webui.js';
+import type {DomRepeat, DomRepeatEvent} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {InsetsF} from 'chrome://resources/mojo/ui/gfx/geometry/mojom/geometry.mojom-webui.js';
 
 import {getTemplate} from './help_bubble.html.js';
-import {HelpBubbleArrowPosition, HelpBubbleButtonParams, Progress} from './help_bubble.mojom-webui.js';
+import type {HelpBubbleButtonParams, Progress} from './help_bubble.mojom-webui.js';
+import {HelpBubbleArrowPosition} from './help_bubble.mojom-webui.js';
 
 const ACTION_BUTTON_ID_PREFIX = 'action-button-';
 
diff --git a/ui/webui/resources/cr_components/help_bubble/help_bubble_controller.ts b/ui/webui/resources/cr_components/help_bubble/help_bubble_controller.ts
index 0078e2d..9f2d38c 100644
--- a/ui/webui/resources/cr_components/help_bubble/help_bubble_controller.ts
+++ b/ui/webui/resources/cr_components/help_bubble/help_bubble_controller.ts
@@ -3,10 +3,12 @@
 // found in the LICENSE file.
 
 import {assert, assertNotReached} from 'chrome://resources/js/assert.js';
-import {InsetsF, RectF} from 'chrome://resources/mojo/ui/gfx/geometry/mojom/geometry.mojom-webui.js';
+import type {InsetsF, RectF} from 'chrome://resources/mojo/ui/gfx/geometry/mojom/geometry.mojom-webui.js';
 
-import {HELP_BUBBLE_SCROLL_ANCHOR_OPTIONS, HelpBubbleElement} from './help_bubble.js';
-import {HelpBubbleArrowPosition, HelpBubbleParams} from './help_bubble.mojom-webui.js';
+import type {HelpBubbleElement} from './help_bubble.js';
+import {HELP_BUBBLE_SCROLL_ANCHOR_OPTIONS} from './help_bubble.js';
+import type {HelpBubbleParams} from './help_bubble.mojom-webui.js';
+import {HelpBubbleArrowPosition} from './help_bubble.mojom-webui.js';
 
 type Root = HTMLElement|ShadowRoot&{shadowRoot?: ShadowRoot};
 
diff --git a/ui/webui/resources/cr_components/help_bubble/help_bubble_mixin.ts b/ui/webui/resources/cr_components/help_bubble/help_bubble_mixin.ts
index 3a2818b..ad0c717 100644
--- a/ui/webui/resources/cr_components/help_bubble/help_bubble_mixin.ts
+++ b/ui/webui/resources/cr_components/help_bubble/help_bubble_mixin.ts
@@ -20,12 +20,16 @@
 
 import {assert} from 'chrome://resources/js/assert.js';
 import {EventTracker} from 'chrome://resources/js/event_tracker.js';
-import {InsetsF, RectF} from 'chrome://resources/mojo/ui/gfx/geometry/mojom/geometry.mojom-webui.js';
-import {dedupingMixin, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {InsetsF, RectF} from 'chrome://resources/mojo/ui/gfx/geometry/mojom/geometry.mojom-webui.js';
+import type {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {dedupingMixin} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {debounceEnd, HELP_BUBBLE_DISMISSED_EVENT, HELP_BUBBLE_TIMED_OUT_EVENT, HelpBubbleDismissedEvent, HelpBubbleElement} from './help_bubble.js';
-import {HelpBubbleClientCallbackRouter, HelpBubbleClosedReason, HelpBubbleHandlerInterface, HelpBubbleParams} from './help_bubble.mojom-webui.js';
-import {HelpBubbleController, Trackable} from './help_bubble_controller.js';
+import type {HelpBubbleDismissedEvent, HelpBubbleElement} from './help_bubble.js';
+import {debounceEnd, HELP_BUBBLE_DISMISSED_EVENT, HELP_BUBBLE_TIMED_OUT_EVENT} from './help_bubble.js';
+import type {HelpBubbleClientCallbackRouter, HelpBubbleHandlerInterface, HelpBubbleParams} from './help_bubble.mojom-webui.js';
+import {HelpBubbleClosedReason} from './help_bubble.mojom-webui.js';
+import type {Trackable} from './help_bubble_controller.js';
+import {HelpBubbleController} from './help_bubble_controller.js';
 import {HelpBubbleProxyImpl} from './help_bubble_proxy.js';
 
 type Constructor<T> = new (...args: any[]) => T;
diff --git a/ui/webui/resources/cr_components/help_bubble/help_bubble_proxy.ts b/ui/webui/resources/cr_components/help_bubble/help_bubble_proxy.ts
index 705f65ad..6b1ca833 100644
--- a/ui/webui/resources/cr_components/help_bubble/help_bubble_proxy.ts
+++ b/ui/webui/resources/cr_components/help_bubble/help_bubble_proxy.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {HelpBubbleClientCallbackRouter, HelpBubbleHandlerFactory, HelpBubbleHandlerInterface, HelpBubbleHandlerRemote} from './help_bubble.mojom-webui.js';
+import type {HelpBubbleHandlerInterface} from './help_bubble.mojom-webui.js';
+import {HelpBubbleClientCallbackRouter, HelpBubbleHandlerFactory, HelpBubbleHandlerRemote} from './help_bubble.mojom-webui.js';
 
 export interface HelpBubbleProxy {
   getHandler(): HelpBubbleHandlerInterface;
diff --git a/ui/webui/resources/cr_components/history_clusters/browser_proxy.ts b/ui/webui/resources/cr_components/history_clusters/browser_proxy.ts
index 9e9d8b82..8193627 100644
--- a/ui/webui/resources/cr_components/history_clusters/browser_proxy.ts
+++ b/ui/webui/resources/cr_components/history_clusters/browser_proxy.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {PageCallbackRouter, PageHandler, PageHandlerRemote} from './history_clusters.mojom-webui.js';
+import type {PageHandlerRemote} from './history_clusters.mojom-webui.js';
+import {PageCallbackRouter, PageHandler} from './history_clusters.mojom-webui.js';
 
 /**
  * @fileoverview This file provides a singleton class that exposes the Mojo
diff --git a/ui/webui/resources/cr_components/history_clusters/cluster.ts b/ui/webui/resources/cr_components/history_clusters/cluster.ts
index 5918f06..ab7f8ba 100644
--- a/ui/webui/resources/cr_components/history_clusters/cluster.ts
+++ b/ui/webui/resources/cr_components/history_clusters/cluster.ts
@@ -19,8 +19,9 @@
 
 import {BrowserProxyImpl} from './browser_proxy.js';
 import {getTemplate} from './cluster.html.js';
-import {Cluster, SearchQuery, URLVisit} from './history_cluster_types.mojom-webui.js';
-import {ClusterAction, PageCallbackRouter, VisitAction} from './history_clusters.mojom-webui.js';
+import type {Cluster, SearchQuery, URLVisit} from './history_cluster_types.mojom-webui.js';
+import type {PageCallbackRouter} from './history_clusters.mojom-webui.js';
+import {ClusterAction, VisitAction} from './history_clusters.mojom-webui.js';
 import {MetricsProxyImpl} from './metrics_proxy.js';
 import {insertHighlightedTextWithMatchesIntoElement} from './utils.js';
 
diff --git a/ui/webui/resources/cr_components/history_clusters/cluster_menu.ts b/ui/webui/resources/cr_components/history_clusters/cluster_menu.ts
index 34cd0e3..a7f17f8 100644
--- a/ui/webui/resources/cr_components/history_clusters/cluster_menu.ts
+++ b/ui/webui/resources/cr_components/history_clusters/cluster_menu.ts
@@ -7,8 +7,8 @@
 import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
 import 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
 
-import {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
-import {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
+import type {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
+import type {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/ui/webui/resources/cr_components/history_clusters/clusters.ts b/ui/webui/resources/cr_components/history_clusters/clusters.ts
index 86d152a..34cd19f 100644
--- a/ui/webui/resources/cr_components/history_clusters/clusters.ts
+++ b/ui/webui/resources/cr_components/history_clusters/clusters.ts
@@ -11,23 +11,23 @@
 import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
 import 'chrome://resources/polymer/v3_0/iron-scroll-threshold/iron-scroll-threshold.js';
 
-import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
-import {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
-import {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
+import type {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import type {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
+import type {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {FocusOutlineManager} from 'chrome://resources/js/focus_outline_manager.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {Time} from 'chrome://resources/mojo/mojo/public/mojom/base/time.mojom-webui.js';
-import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
-import {IronListElement} from 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
-import {IronScrollThresholdElement} from 'chrome://resources/polymer/v3_0/iron-scroll-threshold/iron-scroll-threshold.js';
+import type {Time} from 'chrome://resources/mojo/mojo/public/mojom/base/time.mojom-webui.js';
+import type {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
+import type {IronListElement} from 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
+import type {IronScrollThresholdElement} from 'chrome://resources/polymer/v3_0/iron-scroll-threshold/iron-scroll-threshold.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {BrowserProxyImpl} from './browser_proxy.js';
 import {getTemplate} from './clusters.html.js';
-import {Cluster, URLVisit} from './history_cluster_types.mojom-webui.js';
-import {PageCallbackRouter, PageHandlerRemote, QueryResult} from './history_clusters.mojom-webui.js';
+import type {Cluster, URLVisit} from './history_cluster_types.mojom-webui.js';
+import type {PageCallbackRouter, PageHandlerRemote, QueryResult} from './history_clusters.mojom-webui.js';
 
 /**
  * @fileoverview This file provides a custom element that requests and shows
diff --git a/ui/webui/resources/cr_components/history_clusters/metrics_proxy.ts b/ui/webui/resources/cr_components/history_clusters/metrics_proxy.ts
index a2b1304..70caf0e 100644
--- a/ui/webui/resources/cr_components/history_clusters/metrics_proxy.ts
+++ b/ui/webui/resources/cr_components/history_clusters/metrics_proxy.ts
@@ -3,8 +3,10 @@
 // found in the LICENSE file.
 
 import {BrowserProxyImpl} from './browser_proxy.js';
-import {Annotation, URLVisit} from './history_cluster_types.mojom-webui.js';
-import {ClusterAction, RelatedSearchAction, VisitAction, VisitType} from './history_clusters.mojom-webui.js';
+import type {URLVisit} from './history_cluster_types.mojom-webui.js';
+import {Annotation} from './history_cluster_types.mojom-webui.js';
+import type {ClusterAction, RelatedSearchAction, VisitAction} from './history_clusters.mojom-webui.js';
+import {VisitType} from './history_clusters.mojom-webui.js';
 
 /**
  * @fileoverview This file provides an abstraction layer for logging metrics for
diff --git a/ui/webui/resources/cr_components/history_clusters/page_favicon.ts b/ui/webui/resources/cr_components/history_clusters/page_favicon.ts
index ef86d409..6ed0930b 100644
--- a/ui/webui/resources/cr_components/history_clusters/page_favicon.ts
+++ b/ui/webui/resources/cr_components/history_clusters/page_favicon.ts
@@ -9,7 +9,7 @@
 import {ClientId as PageImageServiceClientId} from 'chrome://resources/cr_components/page_image_service/page_image_service.mojom-webui.js';
 import {getFaviconForPageURL} from 'chrome://resources/js/icon.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
+import type {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getTemplate} from './page_favicon.html.js';
diff --git a/ui/webui/resources/cr_components/history_clusters/search_query.ts b/ui/webui/resources/cr_components/history_clusters/search_query.ts
index aa77a09..eac196b 100644
--- a/ui/webui/resources/cr_components/history_clusters/search_query.ts
+++ b/ui/webui/resources/cr_components/history_clusters/search_query.ts
@@ -8,7 +8,7 @@
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {BrowserProxyImpl} from './browser_proxy.js';
-import {SearchQuery} from './history_cluster_types.mojom-webui.js';
+import type {SearchQuery} from './history_cluster_types.mojom-webui.js';
 import {RelatedSearchAction} from './history_clusters.mojom-webui.js';
 import {MetricsProxyImpl} from './metrics_proxy.js';
 import {getTemplate} from './search_query.html.js';
diff --git a/ui/webui/resources/cr_components/history_clusters/url_visit.ts b/ui/webui/resources/cr_components/history_clusters/url_visit.ts
index 5a88a04..68a6d05 100644
--- a/ui/webui/resources/cr_components/history_clusters/url_visit.ts
+++ b/ui/webui/resources/cr_components/history_clusters/url_visit.ts
@@ -8,14 +8,15 @@
 import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
 import 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
 
-import {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
-import {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
+import type {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
+import type {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {BrowserProxyImpl} from './browser_proxy.js';
-import {Annotation, URLVisit} from './history_cluster_types.mojom-webui.js';
+import type {URLVisit} from './history_cluster_types.mojom-webui.js';
+import {Annotation} from './history_cluster_types.mojom-webui.js';
 import {getTemplate} from './url_visit.html.js';
 import {insertHighlightedTextWithMatchesIntoElement} from './utils.js';
 
diff --git a/ui/webui/resources/cr_components/history_clusters/utils.ts b/ui/webui/resources/cr_components/history_clusters/utils.ts
index c2a69fae..2811070c 100644
--- a/ui/webui/resources/cr_components/history_clusters/utils.ts
+++ b/ui/webui/resources/cr_components/history_clusters/utils.ts
@@ -4,7 +4,7 @@
 
 import {highlight} from 'chrome://resources/js/search_highlight_utils.js';
 
-import {MatchPosition} from './history_cluster_types.mojom-webui.js';
+import type {MatchPosition} from './history_cluster_types.mojom-webui.js';
 
 /**
  * Populates `container` with the highlighted `text` based on the mojom provided
diff --git a/ui/webui/resources/cr_components/managed_dialog/managed_dialog.ts b/ui/webui/resources/cr_components/managed_dialog/managed_dialog.ts
index 0e9ae58..1e1f324 100644
--- a/ui/webui/resources/cr_components/managed_dialog/managed_dialog.ts
+++ b/ui/webui/resources/cr_components/managed_dialog/managed_dialog.ts
@@ -12,7 +12,7 @@
 import 'chrome://resources/cr_elements/icons.html.js';
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 
-import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import type {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
diff --git a/ui/webui/resources/cr_components/most_visited/most_visited.ts b/ui/webui/resources/cr_components/most_visited/most_visited.ts
index 29705bb..d456c19a 100644
--- a/ui/webui/resources/cr_components/most_visited/most_visited.ts
+++ b/ui/webui/resources/cr_components/most_visited/most_visited.ts
@@ -11,9 +11,9 @@
 import 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
 import 'chrome://resources/cr_elements/cr_hidden_style.css.js';
 
-import {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
-import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
-import {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
+import type {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
+import type {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import type {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {skColorToRgba} from 'chrome://resources/js/color_utils.js';
@@ -23,13 +23,14 @@
 import {isMac} from 'chrome://resources/js/platform.js';
 import {hasKeyModifiers} from 'chrome://resources/js/util.js';
 import {TextDirection} from 'chrome://resources/mojo/mojo/public/mojom/base/text_direction.mojom-webui.js';
-import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
-import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
-import {afterNextRender, DomRepeat, DomRepeatEvent, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
+import type {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
+import type {DomRepeat, DomRepeatEvent} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {afterNextRender, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {MostVisitedBrowserProxy} from './browser_proxy.js';
 import {getTemplate} from './most_visited.html.js';
-import {MostVisitedInfo, MostVisitedPageCallbackRouter, MostVisitedPageHandlerRemote, MostVisitedTheme, MostVisitedTile} from './most_visited.mojom-webui.js';
+import type {MostVisitedInfo, MostVisitedPageCallbackRouter, MostVisitedPageHandlerRemote, MostVisitedTheme, MostVisitedTile} from './most_visited.mojom-webui.js';
 import {MostVisitedWindowProxy} from './window_proxy.js';
 
 function resetTilePosition(tile: HTMLElement) {
diff --git a/ui/webui/resources/cr_components/omnibox/realbox_action.ts b/ui/webui/resources/cr_components/omnibox/realbox_action.ts
index d807ce1..bd00c3d2 100644
--- a/ui/webui/resources/cr_components/omnibox/realbox_action.ts
+++ b/ui/webui/resources/cr_components/omnibox/realbox_action.ts
@@ -8,7 +8,7 @@
 import {sanitizeInnerHtml} from '//resources/js/parse_html_subset.js';
 import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {Action} from './omnibox.mojom-webui.js';
+import type {Action} from './omnibox.mojom-webui.js';
 import {getTemplate} from './realbox_action.html.js';
 import {decodeString16} from './utils.js';
 
diff --git a/ui/webui/resources/cr_components/omnibox/realbox_browser_proxy.ts b/ui/webui/resources/cr_components/omnibox/realbox_browser_proxy.ts
index cdab08ba..7a3dc34 100644
--- a/ui/webui/resources/cr_components/omnibox/realbox_browser_proxy.ts
+++ b/ui/webui/resources/cr_components/omnibox/realbox_browser_proxy.ts
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {PageCallbackRouter, PageHandler, PageHandlerInterface} from './omnibox.mojom-webui.js';
+import type {PageHandlerInterface} from './omnibox.mojom-webui.js';
+import {PageCallbackRouter, PageHandler} from './omnibox.mojom-webui.js';
 
 /**
  * @fileoverview This file provides a singleton class that exposes the Mojo
diff --git a/ui/webui/resources/cr_components/omnibox/realbox_dropdown.ts b/ui/webui/resources/cr_components/omnibox/realbox_dropdown.ts
index 76ecc6e..b312eed4 100644
--- a/ui/webui/resources/cr_components/omnibox/realbox_dropdown.ts
+++ b/ui/webui/resources/cr_components/omnibox/realbox_dropdown.ts
@@ -12,10 +12,11 @@
 import {MetricsReporterImpl} from '//resources/js/metrics_reporter/metrics_reporter.js';
 import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {AutocompleteMatch, AutocompleteResult, OmniboxPopupSelection, PageHandlerInterface, RenderType, SelectionLineState, SideType} from './omnibox.mojom-webui.js';
+import type {AutocompleteMatch, AutocompleteResult, OmniboxPopupSelection, PageHandlerInterface} from './omnibox.mojom-webui.js';
+import {RenderType, SelectionLineState, SideType} from './omnibox.mojom-webui.js';
 import {RealboxBrowserProxy} from './realbox_browser_proxy.js';
 import {getTemplate} from './realbox_dropdown.html.js';
-import {RealboxMatchElement} from './realbox_match.js';
+import type {RealboxMatchElement} from './realbox_match.js';
 import {decodeString16, renderTypeToClass, sideTypeToClass} from './utils.js';
 
 // The '%' operator in JS returns negative numbers. This workaround avoids that.
diff --git a/ui/webui/resources/cr_components/omnibox/realbox_icon.ts b/ui/webui/resources/cr_components/omnibox/realbox_icon.ts
index 3b9cd32..43d4c50 100644
--- a/ui/webui/resources/cr_components/omnibox/realbox_icon.ts
+++ b/ui/webui/resources/cr_components/omnibox/realbox_icon.ts
@@ -6,7 +6,7 @@
 import {loadTimeData} from '//resources/js/load_time_data.js';
 import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {AutocompleteMatch} from './omnibox.mojom-webui.js';
+import type {AutocompleteMatch} from './omnibox.mojom-webui.js';
 import {getTemplate} from './realbox_icon.html.js';
 
 const CALCULATOR: string = 'search-calculator-answer';
diff --git a/ui/webui/resources/cr_components/omnibox/realbox_match.ts b/ui/webui/resources/cr_components/omnibox/realbox_match.ts
index d645d5b..0348c1ba 100644
--- a/ui/webui/resources/cr_components/omnibox/realbox_match.ts
+++ b/ui/webui/resources/cr_components/omnibox/realbox_match.ts
@@ -13,9 +13,10 @@
 import {sanitizeInnerHtml} from '//resources/js/parse_html_subset.js';
 import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {ACMatchClassification, Action, AutocompleteMatch, NavigationPredictor, OmniboxPopupSelection, PageHandlerInterface, SelectionLineState, SideType} from './omnibox.mojom-webui.js';
+import type {ACMatchClassification, Action, AutocompleteMatch, OmniboxPopupSelection, PageHandlerInterface} from './omnibox.mojom-webui.js';
+import {NavigationPredictor, SelectionLineState, SideType} from './omnibox.mojom-webui.js';
 import {RealboxBrowserProxy} from './realbox_browser_proxy.js';
-import {RealboxIconElement} from './realbox_icon.js';
+import type {RealboxIconElement} from './realbox_icon.js';
 import {getTemplate} from './realbox_match.html.js';
 import {decodeString16, mojoTimeTicks, sideTypeToClass} from './utils.js';
 
diff --git a/ui/webui/resources/cr_components/omnibox/utils.ts b/ui/webui/resources/cr_components/omnibox/utils.ts
index 77f8aab2d..6f8d07d 100644
--- a/ui/webui/resources/cr_components/omnibox/utils.ts
+++ b/ui/webui/resources/cr_components/omnibox/utils.ts
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 import {assertNotReached} from '//resources/js/assert.js';
-import {String16} from '//resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js';
-import {TimeTicks} from '//resources/mojo/mojo/public/mojom/base/time.mojom-webui.js';
+import type {String16} from '//resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js';
+import type {TimeTicks} from '//resources/mojo/mojo/public/mojom/base/time.mojom-webui.js';
 
 import {RenderType, SideType} from './omnibox.mojom-webui.js';
 
diff --git a/ui/webui/resources/cr_components/page_image_service/browser_proxy.ts b/ui/webui/resources/cr_components/page_image_service/browser_proxy.ts
index 58302e1..cb99fd8 100644
--- a/ui/webui/resources/cr_components/page_image_service/browser_proxy.ts
+++ b/ui/webui/resources/cr_components/page_image_service/browser_proxy.ts
@@ -6,7 +6,8 @@
  * @fileoverview The browser proxy used to access `PageImageService` from WebUI.
  */
 
-import {PageImageServiceHandler, PageImageServiceHandlerRemote} from './page_image_service.mojom-webui.js';
+import type {PageImageServiceHandlerRemote} from './page_image_service.mojom-webui.js';
+import {PageImageServiceHandler} from './page_image_service.mojom-webui.js';
 
 export class PageImageServiceBrowserProxy {
   handler: PageImageServiceHandlerRemote;
diff --git a/ui/webui/resources/cr_components/settings_prefs/prefs_mixin.ts b/ui/webui/resources/cr_components/settings_prefs/prefs_mixin.ts
index 34a680a..a00ca97f 100644
--- a/ui/webui/resources/cr_components/settings_prefs/prefs_mixin.ts
+++ b/ui/webui/resources/cr_components/settings_prefs/prefs_mixin.ts
@@ -8,7 +8,8 @@
 
 // clang-format off
 import {assert} from 'chrome://resources/js/assert.js';
-import {dedupingMixin, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type { PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {dedupingMixin} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 // clang-format on
 
 type Constructor<T> = new (...args: any[]) => T;
diff --git a/ui/webui/resources/cr_components/theme_color_picker/color_utils.ts b/ui/webui/resources/cr_components/theme_color_picker/color_utils.ts
index 8ec2338..4602fdd 100644
--- a/ui/webui/resources/cr_components/theme_color_picker/color_utils.ts
+++ b/ui/webui/resources/cr_components/theme_color_picker/color_utils.ts
@@ -1,8 +1,8 @@
 // Copyright 2023 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
-import {BrowserColorVariant} from 'chrome://resources/mojo/ui/base/mojom/themes.mojom-webui.js';
+import type {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
+import type {BrowserColorVariant} from 'chrome://resources/mojo/ui/base/mojom/themes.mojom-webui.js';
 
 export interface Color {
   background: SkColor;
diff --git a/ui/webui/resources/cr_components/theme_color_picker/theme_color.ts b/ui/webui/resources/cr_components/theme_color_picker/theme_color.ts
index ef7e814..0707cb7 100644
--- a/ui/webui/resources/cr_components/theme_color_picker/theme_color.ts
+++ b/ui/webui/resources/cr_components/theme_color_picker/theme_color.ts
@@ -6,7 +6,7 @@
 
 import {skColorToRgba} from 'chrome://resources/js/color_utils.js';
 import {FocusOutlineManager} from 'chrome://resources/js/focus_outline_manager.js';
-import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
+import type {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getTemplate} from './theme_color.html.js';
diff --git a/ui/webui/resources/cr_components/theme_color_picker/theme_color_picker.ts b/ui/webui/resources/cr_components/theme_color_picker/theme_color_picker.ts
index 1232943..f32219f 100644
--- a/ui/webui/resources/cr_components/theme_color_picker/theme_color_picker.ts
+++ b/ui/webui/resources/cr_components/theme_color_picker/theme_color_picker.ts
@@ -9,16 +9,18 @@
 
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {hexColorToSkColor, skColorToRgba} from 'chrome://resources/js/color_utils.js';
-import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
+import type {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
 import {BrowserColorVariant} from 'chrome://resources/mojo/ui/base/mojom/themes.mojom-webui.js';
-import {DomRepeat, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {DomRepeat} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {ThemeColorPickerBrowserProxy} from './browser_proxy.js';
-import {Color, ColorType, DARK_BASELINE_BLUE_COLOR, DARK_BASELINE_GREY_COLOR, DARK_DEFAULT_COLOR, LIGHT_BASELINE_BLUE_COLOR, LIGHT_BASELINE_GREY_COLOR, LIGHT_DEFAULT_COLOR, SelectedColor} from './color_utils.js';
-import {ThemeColorElement} from './theme_color.js';
+import type {Color, SelectedColor} from './color_utils.js';
+import {ColorType, DARK_BASELINE_BLUE_COLOR, DARK_BASELINE_GREY_COLOR, DARK_DEFAULT_COLOR, LIGHT_BASELINE_BLUE_COLOR, LIGHT_BASELINE_GREY_COLOR, LIGHT_DEFAULT_COLOR} from './color_utils.js';
+import type {ThemeColorElement} from './theme_color.js';
 import {getTemplate} from './theme_color_picker.html.js';
-import {ChromeColor, Theme, ThemeColorPickerHandlerRemote} from './theme_color_picker.mojom-webui.js';
-import {ThemeHueSliderDialogElement} from './theme_hue_slider_dialog.js';
+import type {ChromeColor, Theme, ThemeColorPickerHandlerRemote} from './theme_color_picker.mojom-webui.js';
+import type {ThemeHueSliderDialogElement} from './theme_hue_slider_dialog.js';
 
 const ThemeColorPickerElementBase = I18nMixin(PolymerElement);
 
diff --git a/ui/webui/resources/cr_components/theme_color_picker/theme_hue_slider_dialog.ts b/ui/webui/resources/cr_components/theme_color_picker/theme_hue_slider_dialog.ts
index 80d2b8f..87ca26f 100644
--- a/ui/webui/resources/cr_components/theme_color_picker/theme_hue_slider_dialog.ts
+++ b/ui/webui/resources/cr_components/theme_color_picker/theme_hue_slider_dialog.ts
@@ -6,7 +6,7 @@
 import 'chrome://resources/cr_elements/cr_icons.css.js';
 import 'chrome://resources/cr_elements/cr_slider/cr_slider.js';
 
-import {CrSliderElement} from 'chrome://resources/cr_elements/cr_slider/cr_slider.js';
+import type {CrSliderElement} from 'chrome://resources/cr_elements/cr_slider/cr_slider.js';
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
diff --git a/ui/webui/resources/cr_elements/BUILD.gn b/ui/webui/resources/cr_elements/BUILD.gn
index 4a9d25c9..337a75af 100644
--- a/ui/webui/resources/cr_elements/BUILD.gn
+++ b/ui/webui/resources/cr_elements/BUILD.gn
@@ -117,10 +117,6 @@
       "cr_expand_button/cr_expand_button.css",
       "cr_input/cr_input_style.css",
     ]
-
-    if (is_chromeos_ash) {
-      css_files += [ "chromeos/cros_color_overrides.css" ]
-    }
   }
 
   html_to_wrapper_template = "detect"
diff --git a/ui/webui/resources/cr_elements/chromeos/cros_color_overrides.css b/ui/webui/resources/cr_elements/chromeos/cros_color_overrides.css
deleted file mode 100644
index 2aab432..0000000
--- a/ui/webui/resources/cr_elements/chromeos/cros_color_overrides.css
+++ /dev/null
@@ -1,472 +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. */
-
-/* #css_wrapper_metadata_start
- * #type=style
- * #scheme=relative
- * #css_wrapper_metadata_end */
-
-/*
- * Style Module that defines overrides for cr-elements on ChromeOS. This file
- * plumbs semantic colors and fonts into cr-elements.
- *
- * To get ChromeOS System colors, an element must:
- * 1) Have an ancestor element with the "cros" attribute in the <html> tag.
- * 2) Import the "cros_color_overrides.css.js" CSS wrapper file in JS/TS.
- * 3) Include the "cros-color-overrides" style module in HTML.
- *
- * To get ChromeOS Jelly colors, an element must:
- * 1) Have an ancestor element with the "jelly-enabled" class in the <body> tag.
- * 2) Import the "cros_color_overrides.css.js" CSS wrapper file in JS/TS.
- * 3) Include the "cros-color-overrides" style module in HTML.
- *
- * To apply ChromeOS overrides for Chrome Refresh 2023 styles, an element must:
- * 1) Have an ancestor element with both the "cros" and "chrome-refresh-2023"
- *    attributes in the <html> tag.
- * 2) Import the "cros_color_overrides.css.js" CSS wrapper file in JS/TS.
- * 3) Include the "cros-color-overrides" style module in HTML.
- */
-
-:host-context([cros]) a:not(.item)[href] {
-  color: var(--cros-link-color);
-}
-
-:host-context([cros]) cr-button[has-prefix-icon_],
-:host-context([cros]) cr-button[has-suffix-icon_] {
-  --iron-icon-fill-color: currentColor;
-}
-
-:host-context([cros]) cr-dialog::part(dialog) {
-  --cr-dialog-background-color: var(--cros-bg-color-elevation-3);
-  background-image: none;
-  box-shadow: var(--cros-elevation-3-shadow);
-}
-
-:host-context([cros]) cr-radio-button {
-  --cr-radio-button-checked-color: var(--cros-radio-button-color);
-  --cr-radio-button-checked-ripple-color:
-      var(--cros-radio-button-ripple-color);
-  --cr-radio-button-unchecked-color:
-      var(--cros-radio-button-color-unchecked);
-  --cr-radio-button-unchecked-ripple-color:
-      var(--cros-radio-button-ripple-color-unchecked);
-}
-
-:host-context([cros]) cr-toast {
-  --cr-toast-background-color: var(--cros-toast-background-color);
-  --cr-toast-background: var(--cros-toast-background-color);
-  --cr-toast-text-color: var(--cros-toast-text-color);
-  --iron-icon-fill-color: var(--cros-toast-icon-color);
-}
-
-:host-context([cros]) cr-toast .error-message {
-  color: var(--cros-toast-text-color);
-}
-
-:host-context([cros]) cr-toggle {
-  --cr-toggle-checked-bar-color: var(--cros-switch-track-color-active);
-  /* |--cros-switch-track-color-active| already includes opacity. */
-  --cr-toggle-checked-bar-opacity: 100%;
-  --cr-toggle-checked-button-color: var(--cros-switch-knob-color-active);
-  --cr-toggle-checked-ripple-color: var(--cros-focus-aura-color);
-  --cr-toggle-unchecked-bar-color: var(--cros-switch-track-color-inactive);
-  --cr-toggle-unchecked-button-color: var(--cros-switch-knob-color-inactive);
-  --cr-toggle-unchecked-ripple-color: var(--cros-ripple-color);
-  --cr-toggle-box-shadow: var(--cros-elevation-1-shadow);
-  --cr-toggle-ripple-diameter: 32px;
-}
-
-:host-context([cros]):host-context(.focus-outline-visible) cr-toggle:focus {
-  --cr-toggle-ripple-ring: 2px solid var(--cros-focus-ring-color);
-}
-
-:host-context([cros]) paper-spinner-lite {
-  --paper-spinner-color: var(--cros-icon-color-prominent);
-}
-
-:host-context([cros]) cr-tooltip-icon {
-  --cr-link-color: var(--cros-tooltip-link-color);
-}
-
-/** Jelly-specific styles below */
-
-/** General color overrides */
-:host-context(body.jelly-enabled) {
-  /* TODO(b/266837484) --cros-* values will be updated globally. Remove these
-    definitions after the swap. */
-  --cros-button-label-color-primary: var(--cros-sys-on_primary);
-  --cros-link-color: var(--cros-sys-primary);
-  --cros-separator-color: var(--cros-sys-separator);
-  --cros-tab-slider-track-color: var(--cros-sys-surface_variant, 80%);
-
-  --cr-form-field-label-color: var(--cros-sys-on_surface);
-  --cr-link-color: var(--cros-sys-primary);
-  --cr-primary-text-color: var(--cros-sys-on_surface);
-  --cr-secondary-text-color: var(--cros-sys-on_surface_variant);
-}
-
-:host-context([cros][chrome-refresh-2023]) {
-  --cr-focus-outline-color: var(--cros-sys-focus_ring);
-  --cr-disabled-opacity: var(--cros-disabled-opacity);
-}
-
-/* Button */
-:host-context(body.jelly-enabled) cr-button {
-  /* Default button colors */
-  --text-color: var(--cros-sys-on_primary_container);
-  --ink-color: var(--cros-sys-ripple_primary);
-  --iron-icon-fill-color: currentColor;
-  --hover-bg-color: var(--cros-sys-hover_on_subtle);
-  --ripple-opacity: .1;
-
-  /* Action button colors */
-  --bg-action: var(--cros-sys-primary);
-  --ink-color-action: var(--cros-sys-ripple_primary);
-  --text-color-action: var(--cros-sys-on_primary);
-  --hover-bg-action: var(--cros-sys-hover_on_prominent);
-  --ripple-opacity-action: 1;
-
-  /* Disabled button colors */
-  --disabled-bg: var(--cros-sys-disabled_container);
-  --disabled-bg-action: var(--cros-sys-disabled_container);
-  --disabled-text-color: var(--cros-sys-disabled);
-
-  background-color: var(--cros-sys-primary_container);
-  border: none;
-}
-
-:host-context(body.jelly-enabled) cr-button:hover::part(hoverBackground) {
-  background-color: var(--hover-bg-color);
-  display: block;
-}
-
-:host-context(body.jelly-enabled) cr-button:active,
-:host-context(body.jelly-enabled) cr-button.action-button:not(:active):hover  {
-  box-shadow: none;
-}
-
-:host-context(body.jelly-enabled) cr-button.action-button {
-  background-color: var(--bg-action);
-}
-
-:host-context(body.jelly-enabled)
-    cr-button.action-button:hover::part(hoverBackground) {
-  background-color: var(--hover-bg-action);
-}
-
-:host-context(body.jelly-enabled) cr-button[disabled] {
-  background-color: var(--cros-sys-disabled_container);
-}
-
-:host-context(body.jelly-enabled):host-context(.focus-outline-visible)
-    cr-button:focus {
-  box-shadow: none;
-  outline: 2px solid var(--cros-sys-focus_ring);
-}
-
-/* Checkbox */
-:host-context(body.jelly-enabled) cr-checkbox {
-  --cr-checkbox-checked-box-color: var(--cros-sys-primary);
-  --cr-checkbox-ripple-checked-color: var(--cros-sys-ripple_primary);
-  --cr-checkbox-checked-ripple-opacity: 1;
-  --cr-checkbox-mark-color: var(--cros-sys-inverse_on_surface);
-  --cr-checkbox-ripple-unchecked-color: var(--cros-sys-ripple_primary);
-  --cr-checkbox-unchecked-box-color: var(--cros-sys-on_surface);
-  --cr-checkbox-unchecked-ripple-opacity: 1;
-}
-
-:host-context([cros][chrome-refresh-2023]) cr-checkbox {
-  --cr-checkbox-focus-outline: none;
-}
-
-:host-context([cros][chrome-refresh-2023]) cr-checkbox[disabled] {
-  opacity: var(--cros-disabled-opacity);
-}
-
-:host-context([cros][chrome-refresh-2023]):host-context(.focus-outline-visible)
-    cr-checkbox:focus {
-  --cr-checkbox-ripple-ring: 2px solid var(--cros-sys-focus_ring);
-}
-
-/* Dialog */
-:host-context(body.jelly-enabled) cr-dialog::part(dialog) {
-  --cr-dialog-background-color: var(--cros-sys-base_elevated);
-  background-image: none;
-
-  /* TODO(b/266837484) Replace with cros.sys.app-elevation3 when available */
-  box-shadow: 0 0 12px 0 var(--cros-sys-shadow);
-}
-
-:host-context(body.jelly-enabled) cr-dialog > [slot='title'] {
-  font: var(--cros-display-7-font);
-}
-
-/* Drawer */
-:host-context(body.jelly-enabled) cr-drawer {
-  --cr-drawer-background-color: var(--cros-sys-app_base_shaded);
-}
-
-/* Icon button */
-:host-context(body.jelly-enabled) cr-icon-button,
-:host-context(body.jelly-enabled) cr-link-row::part(icon),
-:host-context(body.jelly-enabled) cr-expand-button::part(icon) {
-  --cr-icon-button-fill-color: var(--cros-sys-secondary);
-}
-
-/* Input and Textarea */
-:host-context(body.jelly-enabled) cr-input,
-:host-context(body.jelly-enabled) cr-search-field::part(searchInput),
-:host-context(body.jelly-enabled) cr-searchable-drop-down::part(input),
-:host-context(body.jelly-enabled) cr-textarea {
-  --cr-input-background-color: var(--cros-sys-input_field_on_base);
-  --cr-input-error-color: var(--cros-sys-error);
-  --cr-input-focus-color: var(--cros-sys-primary);
-  --cr-input-placeholder-color: var(--cros-sys-secondary);
-}
-
-:host-context([cros][chrome-refresh-2023]) cr-input,
-:host-context([cros][chrome-refresh-2023]) cr-search-field::part(searchInput),
-:host-context([cros][chrome-refresh-2023])
-    cr-searchable-drop-down::part(input) {
-  --cr-input-background-color: var(--cros-sys-input_field_on_base);
-  --cr-input-border: none;
-  --cr-input-border-bottom: none;
-  --cr-input-border-radius: 8px;
-  --cr-input-label-color: var(--cros-sys-on-surface);
-  --cr-input-padding-start: 16px;
-  --cr-input-padding-end: 16px;
-  --cr-input-placeholder-color: var(--cros-sys-secondary);
-  --cr-input-underline-display: none;
-
-  font: var(--cros-body-2-font);
-
-  /* Focused state */
-  --cr-input-focus-color: var(--cros-sys-primary);
-  --cr-input-focus-label-color: var(--cros-sys-primary);
-  --cr-input-focus-outline: 2px solid var(--cros-sys-focus_ring);
-
-  /* Hover state */
-  --cr-input-hover-background-color: transparent;
-
-  /* Invalid state */
-  --cr-input-error-color: var(--cros-sys-error);
-}
-
-:host-context([cros][chrome-refresh-2023]) cr-input[disabled] {
-  color: currentColor;
-  opacity: var(--cros-disabled-opacity);
-}
-
-:host-context([cros][chrome-refresh-2023]) cr-input[invalid] {
-  --cr-input-focus-outline: 2px solid var(--cros-sys-error);
-}
-
-/* Toolbar search field */
-:host-context([cros][chrome-refresh-2023]) cr-toolbar-search-field {
-  --cr-toolbar-search-field-hover-background: none;
-}
-
-/* md-select */
-:host-context(body.jelly-enabled) .md-select {
-  --md-select-bg-color: var(--cros-sys-input_field_on_base);
-  --md-select-focus-shadow-color: var(--cros-sys-primary);
-  --md-select-option-bg-color: var(--cros-sys-base_elevated);
-  --md-select-text-color: var(--cros-sys-on_surface);
-}
-
-:host-context([cros][chrome-refresh-2023]) .md-select {
-  --md-arrow-width: 7px;
-  --md-select-bg-color: var(--cros-sys-input_field_on_base);
-  --md-select-focus-shadow-color: transparent;
-  --md-select-option-bg-color: var(--cros-sys-base_elevated);
-  --md-select-side-padding: 16px;
-  --md-select-text-color: var(--cros-sys-on_surface);
-  border: none;
-  border-radius: 8px;
-  font: var(--cros-body-2-font);
-  height: 36px;
-  line-height: 36px;
-}
-
-:host-context([cros][chrome-refresh-2023]) .md-select:hover {
-  background-color: var(--md-select-bg-color);
-}
-
-:host-context([cros][chrome-refresh-2023]) .md-select[disabled] {
-  background-color: var(--md-select-bg-color);
-  border-color: transparent;
-  color: var(--md-select-text-color);
-  opacity: var(--cros-disabled-opacity);
-}
-
-/* Menu */
-:host-context(body.jelly-enabled) cr-action-menu {
-  --cr-menu-background-color: var(--cros-sys-base_elevated);
-  --cr-menu-background-focus-color: var(--cros-sys-hover_on_subtle);
-}
-
-/* Radio button */
-:host-context(body.jelly-enabled),
-:host-context(body.jelly-enabled) cr-radio-button {
-  --cr-radio-button-checked-color: var(--cros-sys-primary);
-  --cr-radio-button-checked-ripple-color: var(--cros-sys-ripple_primary);
-  --cr-radio-button-unchecked-color: var(--cros-sys-on_surface);
-  --cr-radio-button-unchecked-ripple-color:
-      var(--cros-sys-ripple_neutral_on_subtle);
-}
-
-:host-context([cros][chrome-refresh-2023]),
-:host-context([cros][chrome-refresh-2023]) cr-radio-button {
-  --cr-radio-button-checked-color: var(--cros-sys-primary);
-  --cr-radio-button-checked-ripple-color: var(--cros-sys-ripple_primary);
-  --cr-radio-button-unchecked-color: var(--cros-sys-on_surface);
-  --cr-radio-button-unchecked-ripple-color:
-    var(--cros-sys-ripple_neutral_on_subtle);
-  --cr-radio-button-ink-size: 40px;
-}
-
-:host-context([cros][chrome-refresh-2023]) cr-radio-button[disabled] {
-  --cr-radio-button-checked-color: var(--cros-sys-disabled);
-  --cr-radio-button-unchecked-color: var(--cros-sys-disabled);
-}
-
-:host-context(body.jelly-enabled) cr-card-radio-button {
-  --cr-card-background-color: var(--cros-sys-app_base);
-  --cr-checked-color: var(--cros-sys-primary);
-  --cr-radio-button-checked-ripple-color: var(--cros-sys-ripple_primary);
-  --hover-bg-color: var(--cros-sys-hover_on_subtle);
-}
-
-/* Search field */
-:host-context(body.jelly-enabled) cr-search-field {
-  --cr-search-field-clear-icon-fill: var(--cros-sys-primary);
-  --cr-search-field-clear-icon-margin-end: 6px;
-  --cr-search-field-input-border-bottom: none;
-  --cr-search-field-input-padding-start: 8px;
-  --cr-search-field-input-underline-border-radius: 4px;
-  --cr-search-field-search-icon-display: none;
-  --cr-search-field-search-icon-fill: var(--cros-sys-primary);
-  --cr-search-field-search-icon-inline-display: block;
-  --cr-search-field-search-icon-inline-margin-start: 6px;
-  border-radius: 4px;
-}
-
-:host-context([cros][chrome-refresh-2023]) cr-search-field {
-  /* Search icon */
-  --cr-search-field-search-icon-fill: var(--cros-sys-secondary);
-  --cr-search-field-search-icon-inline-margin-start: 0;
-
-  /* Clear icon */
-  --cr-search-field-clear-icon-fill: var(--cros-sys-secondary);
-  --cr-search-field-clear-icon-margin-end: 6px;
-  --cr-search-field-clear-icon-size: 16px;
-}
-
-:host-context([cros][chrome-refresh-2023]) cr-search-field::part(searchInput) {
-  /* Modify the styles defined in "Input and Textarea" above. */
-  --cr-input-padding-bottom: 10px;
-  --cr-input-padding-end: 28px;
-  --cr-input-padding-start: 8px;
-  --cr-input-padding-top: 10px;
-}
-
-/* Searchable Dropdown */
-:host-context(body.jelly-enabled) cr-searchable-drop-down,
-:host-context([cros][chrome-refresh-2023]) cr-searchable-drop-down {
-  --cr-searchable-drop-down-bg-color: var(--cros-sys-base_elevated);
-  --cr-searchable-drop-down-icon-color-focus: var(--cros-sys-primary);
-  --cr-searchable-drop-down-list-bg-color-selected:
-      var(--cros-sys-base_highlight);
-  --cr-searchable-drop-down-list-item-color: var(--cros-sys-on_surface);
-  --cr-searchable-drop-down-shadow: var(--cros-elevation-3-shadow);
-}
-
-/* Slider */
-:host-context(body.jelly-enabled) cr-slider {
-  --cr-slider-active-color: var(--cros-sys-primary);
-  --cr-slider-container-color: var(--cros-sys-primary_container);
-  --cr-slider-container-disabled-color: var(--cros-sys-disabled_container);
-  --cr-slider-disabled-color: var(--cros-sys-disabled);
-  --cr-slider-knob-active-color: var(--cros-sys-primary);
-  --cr-slider-knob-disabled-color: var(--cros-sys-disabled);
-  --cr-slider-marker-active-color: var(--cros-sys-primary_container);
-  --cr-slider-marker-color: var(--cros-sys-primary);
-  --cr-slider-marker-disabled-color: var(--cros-sys-disabled);
-  --cr-slider-ripple-color: var(--cros-sys-hover_on_prominent);
-}
-
-:host-context(body.jelly-enabled) cr-slider:not([disabled])::part(knob) {
-  background-color: var(--cros-sys-primary);
-}
-
-:host-context(body.jelly-enabled) cr-slider[disabled]::part(knob) {
-  border: none;
-}
-
-:host-context(body.jelly-enabled) cr-slider::part(label) {
-  background: var(--cros-sys-primary);
-  color: var(--cros-sys-on_primary);
-}
-
-/* Tabs */
-:host-context(body.jelly-enabled) cr-tabs {
-  --cr-tabs-selected-color: var(--cros-sys-primary);
-}
-
-/* Toggle */
-:host-context(body.jelly-enabled) cr-toggle {
-  --cr-toggle-checked-bar-color: var(--cros-sys-primary_container);
-  --cr-toggle-checked-bar-opacity: 100%;
-  --cr-toggle-checked-button-color: var(--cros-sys-primary);
-  --cr-toggle-checked-ripple-color: var(--cros-sys-hover_on_prominent);
-  --cr-toggle-unchecked-bar-color: var(--cros-sys-secondary);
-  --cr-toggle-unchecked-button-color: var(--cros-sys-surface_variant);
-  --cr-toggle-unchecked-ripple-color: var(--cros-sys-hover_on_prominent);
-  /* TODO(b/266837484) Replace with cros.sys.app-elevation1 when available */
-  --cr-toggle-box-shadow: var(--cros-elevation-1-shadow);
-  --cr-toggle-ripple-diameter: 32px;
-}
-
-:host-context(body.jelly-enabled):host-context(.focus-outline-visible)
-    cr-toggle:focus {
-  --cr-toggle-ripple-ring: 2px solid var(--cros-sys-focus_ring);
-}
-
-:host-context([cros][chrome-refresh-2023]) cr-toggle {
-  --cr-toggle-bar-width: 32px;
-  --cr-toggle-knob-diameter: 12px;
-  --cr-toggle-bar-border: none;
-
-  /* "On" state */
-  --cr-toggle-checked-bar-color: var(--cros-sys-primary);
-  --cr-toggle-checked-button-color: var(--cros-sys-on_primary);
-
-  /* "Off" state */
-  --cr-toggle-unchecked-bar-color: var(--cros-sys-secondary);
-  --cr-toggle-unchecked-button-color: var(--cros-sys-on_secondary);
-
-  /* Hover state */
-  --color-toggle-button-thumb-on-hover: var(--cros-sys-on_primary);
-
-  /* Disabled state */
-  --cr-toggle-disabled-opacity: var(--cros-disabled-opacity);
-}
-
-:host-context([cros][chrome-refresh-2023]):host-context(.focus-outline-visible)
-    cr-toggle:focus {
-  --cr-toggle-ripple-ring: none;
-}
-
-/* Tooltip */
-:host-context(body.jelly-enabled) cr-policy-indicator,
-:host-context(body.jelly-enabled) cr-policy-pref-indicator,
-:host-context(body.jelly-enabled) cr-tooltip-icon::part(tooltip),
-:host-context([cros][chrome-refresh-2023]) cr-policy-indicator,
-:host-context([cros][chrome-refresh-2023]) cr-policy-pref-indicator,
-:host-context([cros][chrome-refresh-2023]) cr-tooltip-icon::part(tooltip) {
-  --paper-tooltip-background: var(--cros-sys-on_surface);
-  --paper-tooltip-padding: 5px 8px;
-  --paper-tooltip-text-color: var(--cros-sys-inverse_on_surface);
-  font: var(--cros-annotation-1-font);
-}
diff --git a/ui/webui/resources/cr_elements/cr_actionable_row_style_lit.css b/ui/webui/resources/cr_elements/cr_actionable_row_style_lit.css
index 5a405bce..6de9982 100644
--- a/ui/webui/resources/cr_elements/cr_actionable_row_style_lit.css
+++ b/ui/webui/resources/cr_elements/cr_actionable_row_style_lit.css
@@ -7,35 +7,5 @@
  * #import=./cr_shared_vars.css.js
  * #css_wrapper_metadata_end */
 
-:host {
-  align-items: center;
-  align-self: stretch;
-  display: flex;
-  margin: 0;
-  outline: none;
-}
-
-/* [effectively-disabled_] is a private attribute to allow custom elements
- * to toggle the attribute based on state, such as whether or not the
- * internal control element is disabled, without affecting any public
- * attributes or properties. */
-:host(:not([effectively-disabled_])) {
-  cursor: pointer;
-}
-
-:host(:not([no-hover], [effectively-disabled_]):hover) {
-  background-color: var(--cr-hover-background-color);
-}
-
-:host(:not([no-hover], [effectively-disabled_]):active) {
-  background-color: var(--cr-active-background-color);
-}
-
-/* Do not show hover or active states for cr-icon-buttons that are
- * embedded within the row to avoid showing multiple layers of
- * backgrounds. */
-:host(:not([no-hover], [effectively-disabled_])) cr-icon-button {
-  --cr-icon-button-hover-background-color: transparent;
-  --cr-icon-button-active-background-color: transparent;
-}
-
+/* Purposefully empty since this style is generated at build time from the
+ * equivalent Polymer version. */
diff --git a/ui/webui/resources/cr_elements/cr_container_shadow_mixin.ts b/ui/webui/resources/cr_elements/cr_container_shadow_mixin.ts
index 8067e117..9596735 100644
--- a/ui/webui/resources/cr_elements/cr_container_shadow_mixin.ts
+++ b/ui/webui/resources/cr_elements/cr_container_shadow_mixin.ts
@@ -32,7 +32,8 @@
  */
 
 import {assert} from '//resources/js/assert.js';
-import {dedupingMixin, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {dedupingMixin} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 export enum CrContainerShadowSide {
   TOP = 'top',
diff --git a/ui/webui/resources/cr_elements/cr_container_shadow_mixin_lit.ts b/ui/webui/resources/cr_elements/cr_container_shadow_mixin_lit.ts
index ee42090..e376ded 100644
--- a/ui/webui/resources/cr_elements/cr_container_shadow_mixin_lit.ts
+++ b/ui/webui/resources/cr_elements/cr_container_shadow_mixin_lit.ts
@@ -34,7 +34,7 @@
  */
 
 import {assert} from '//resources/js/assert.js';
-import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
+import type {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
 
 export enum CrContainerShadowSide {
   TOP = 'top',
diff --git a/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.ts b/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.ts
index 864c50c..1b71a16 100644
--- a/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.ts
+++ b/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.ts
@@ -27,9 +27,9 @@
 
 import {CrContainerShadowMixinLit} from '../cr_container_shadow_mixin_lit.js';
 import {getCss as getHiddenCss} from '../cr_hidden_style_lit.css.js';
-import {CrIconButtonElement} from '../cr_icon_button/cr_icon_button.js';
+import type {CrIconButtonElement} from '../cr_icon_button/cr_icon_button.js';
 import {getCss as getIconsCss} from '../cr_icons_lit.css.js';
-import {CrInputElement} from '../cr_input/cr_input.js';
+import type {CrInputElement} from '../cr_input/cr_input.js';
 
 import {getCss} from './cr_dialog.css.js';
 import {getHtml} from './cr_dialog.html.js';
diff --git a/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.ts b/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.ts
index 866673d8..172de39 100644
--- a/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.ts
+++ b/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.ts
@@ -13,10 +13,11 @@
 import '../icons.html.js';
 
 import {focusWithoutInk} from '//resources/js/focus_without_ink.js';
-import {CrLitElement, PropertyValues} from '//resources/lit/v3_0/lit.rollup.js';
+import type {PropertyValues} from '//resources/lit/v3_0/lit.rollup.js';
+import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
 
 import {getCss as getActionableRowCss} from '../cr_actionable_row_style_lit.css.js';
-import {CrIconButtonElement} from '../cr_icon_button/cr_icon_button.js';
+import type {CrIconButtonElement} from '../cr_icon_button/cr_icon_button.js';
 
 import {getCss} from './cr_expand_button.css.js';
 import {getHtml} from './cr_expand_button.html.js';
diff --git a/ui/webui/resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.ts b/ui/webui/resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.ts
index 8812b0c..75315ae 100644
--- a/ui/webui/resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.ts
+++ b/ui/webui/resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.ts
@@ -9,7 +9,7 @@
 import {loadTimeData} from '//resources/js/load_time_data.js';
 import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {CrIconButtonElement} from '../cr_icon_button/cr_icon_button.js';
+import type {CrIconButtonElement} from '../cr_icon_button/cr_icon_button.js';
 
 import {getTemplate} from './cr_feedback_buttons.html.js';
 
diff --git a/ui/webui/resources/cr_elements/cr_fingerprint/cr_fingerprint_progress_arc.ts b/ui/webui/resources/cr_elements/cr_fingerprint/cr_fingerprint_progress_arc.ts
index b0542630..2ec9f96 100644
--- a/ui/webui/resources/cr_elements/cr_fingerprint/cr_fingerprint_progress_arc.ts
+++ b/ui/webui/resources/cr_elements/cr_fingerprint/cr_fingerprint_progress_arc.ts
@@ -8,10 +8,10 @@
 import '../cr_lottie/cr_lottie.js';
 
 import {assert} from '//resources/js/assert.js';
-import {IronIconElement} from '//resources/polymer/v3_0/iron-icon/iron-icon.js';
+import type {IronIconElement} from '//resources/polymer/v3_0/iron-icon/iron-icon.js';
 import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {CrLottieElement} from '../cr_lottie/cr_lottie.js';
+import type {CrLottieElement} from '../cr_lottie/cr_lottie.js';
 
 import {getTemplate} from './cr_fingerprint_progress_arc.html.js';
 
diff --git a/ui/webui/resources/cr_elements/cr_hidden_style_lit.css b/ui/webui/resources/cr_elements/cr_hidden_style_lit.css
index d11a634a6..be05341 100644
--- a/ui/webui/resources/cr_elements/cr_hidden_style_lit.css
+++ b/ui/webui/resources/cr_elements/cr_hidden_style_lit.css
@@ -7,7 +7,5 @@
  * #scheme=relative
  * #css_wrapper_metadata_end */
 
-[hidden],
-:host([hidden]) {
-  display: none !important;
-}
+/* Purposefully empty since this style is generated at build time from the
+ * equivalent Polymer version. */
diff --git a/ui/webui/resources/cr_elements/cr_icons_lit.css b/ui/webui/resources/cr_elements/cr_icons_lit.css
index e928628..be05341 100644
--- a/ui/webui/resources/cr_elements/cr_icons_lit.css
+++ b/ui/webui/resources/cr_elements/cr_icons_lit.css
@@ -7,119 +7,5 @@
  * #scheme=relative
  * #css_wrapper_metadata_end */
 
-.icon-arrow-back {
-  --cr-icon-image: url(chrome://resources/images/icon_arrow_back.svg);
-}
-
-.icon-arrow-dropdown {
-  --cr-icon-image: url(chrome://resources/images/icon_arrow_dropdown.svg);
-}
-
-.icon-arrow-drop-down-cr23 {
-  --cr-icon-image: url(chrome://resources/images/icon_arrow_drop_down_cr23.svg);
-}
-
-.icon-arrow-drop-up-cr23 {
-  --cr-icon-image: url(chrome://resources/images/icon_arrow_drop_up_cr23.svg);
-}
-
-.icon-cancel {
-  --cr-icon-image: url(chrome://resources/images/icon_cancel.svg);
-}
-
-.icon-clear {
-  --cr-icon-image: url(chrome://resources/images/icon_clear.svg);
-}
-
-.icon-copy-content {
-  --cr-icon-image: url(chrome://resources/images/icon_copy_content.svg);
-}
-
-.icon-delete-gray {
-  --cr-icon-image: url(chrome://resources/images/icon_delete_gray.svg);
-}
-
-.icon-edit {
-  --cr-icon-image: url(chrome://resources/images/icon_edit.svg);
-}
-
-.icon-file {
-  --cr-icon-image: url(chrome://resources/images/icon_filetype_generic.svg);
-}
-
-.icon-folder-open {
-  --cr-icon-image: url(chrome://resources/images/icon_folder_open.svg);
-}
-
-.icon-picture-delete {
-  --cr-icon-image: url(chrome://resources/images/icon_picture_delete.svg);
-}
-
-.icon-expand-less {
-  --cr-icon-image: url(chrome://resources/images/icon_expand_less.svg);
-}
-
-.icon-expand-more {
-  --cr-icon-image: url(chrome://resources/images/icon_expand_more.svg);
-}
-
-.icon-external {
-  --cr-icon-image: url(chrome://resources/images/open_in_new.svg);
-}
-
-.icon-more-vert {
-  --cr-icon-image: url(chrome://resources/images/icon_more_vert.svg);
-}
-
-.icon-refresh {
-  --cr-icon-image: url(chrome://resources/images/icon_refresh.svg);
-}
-
-.icon-search {
-  --cr-icon-image: url(chrome://resources/images/icon_search.svg);
-}
-
-.icon-settings {
-  --cr-icon-image: url(chrome://resources/images/icon_settings.svg);
-}
-
-.icon-visibility {
-  --cr-icon-image: url(chrome://resources/images/icon_visibility.svg);
-}
-
-.icon-visibility-off {
-  --cr-icon-image: url(chrome://resources/images/icon_visibility_off.svg);
-}
-
-.subpage-arrow {
-  --cr-icon-image: url(chrome://resources/images/arrow_right.svg);
-}
-
-.cr-icon {
-  -webkit-mask-image: var(--cr-icon-image);
-  -webkit-mask-position: center;
-  -webkit-mask-repeat: no-repeat;
-  -webkit-mask-size: var(--cr-icon-size);
-  background-color: var(--cr-icon-color, var(--google-grey-700));
-  flex-shrink: 0;
-  height: var(--cr-icon-ripple-size);
-  margin-inline-end: var(--cr-icon-ripple-margin);
-  margin-inline-start: var(--cr-icon-button-margin-start);
-  user-select: none;
-  width: var(--cr-icon-ripple-size);
-}
-
-:host-context([dir=rtl]) .cr-icon {
-  transform: scaleX(-1);  /* Invert X: flip on the Y axis (aka mirror). */
-}
-
-.cr-icon.no-overlap {
-  margin-inline-end: 0;
-  margin-inline-start: 0;
-}
-
-@media (prefers-color-scheme: dark) {
-  .cr-icon {
-    background-color: var(--cr-icon-color, var(--google-grey-500));
-  }
-}
+/* Purposefully empty since this style is generated at build time from the
+ * equivalent Polymer version. */
diff --git a/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.ts b/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.ts
index 4015f0b..fee20d3 100644
--- a/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.ts
+++ b/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.ts
@@ -17,7 +17,8 @@
  */
 
 import {assert} from '//resources/js/assert.js';
-import {html, PolymerElement, TemplateInstanceBase, templatize} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {TemplateInstanceBase} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {html, PolymerElement, templatize} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 export class CrLazyRenderElement<T extends HTMLElement> extends PolymerElement {
   static get is() {
diff --git a/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.ts b/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.ts
index 0d2bfed..d91cdfa 100644
--- a/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.ts
+++ b/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.ts
@@ -20,7 +20,7 @@
 import {loadTimeData} from '//resources/js/load_time_data.js';
 import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {CrIconButtonElement} from '../cr_icon_button/cr_icon_button.js';
+import type {CrIconButtonElement} from '../cr_icon_button/cr_icon_button.js';
 
 import {getTemplate} from './cr_link_row.html.js';
 
diff --git a/ui/webui/resources/cr_elements/cr_loading_gradient/cr_loading_gradient.ts b/ui/webui/resources/cr_elements/cr_loading_gradient/cr_loading_gradient.ts
index cf211919..363b284 100644
--- a/ui/webui/resources/cr_elements/cr_loading_gradient/cr_loading_gradient.ts
+++ b/ui/webui/resources/cr_elements/cr_loading_gradient/cr_loading_gradient.ts
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {assert} from 'chrome://resources/js/assert.js';
-import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {assert} from '//resources/js/assert.js';
+import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getTemplate} from './cr_loading_gradient.html.js';
 
diff --git a/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.ts b/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.ts
index 5ba7e84..4e0836c 100644
--- a/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.ts
+++ b/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.ts
@@ -16,7 +16,8 @@
 
 import {assert} from '//resources/js/assert.js';
 import {getImage} from '//resources/js/icon.js';
-import {DomRepeatEvent, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {DomRepeatEvent} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getTemplate} from './cr_profile_avatar_selector.html.js';
 
diff --git a/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button_mixin.ts b/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button_mixin.ts
index daa2d38..2b818a5 100644
--- a/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button_mixin.ts
+++ b/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button_mixin.ts
@@ -7,7 +7,8 @@
  */
 
 // clang-format off
-import {dedupingMixin, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type { PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {dedupingMixin} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assert, assertNotReached} from '//resources/js/assert.js';
 
 interface PaperRippleElement {
diff --git a/ui/webui/resources/cr_elements/cr_radio_group/cr_radio_group.d.ts b/ui/webui/resources/cr_elements/cr_radio_group/cr_radio_group.d.ts
index 1216b53..45632648 100644
--- a/ui/webui/resources/cr_elements/cr_radio_group/cr_radio_group.d.ts
+++ b/ui/webui/resources/cr_elements/cr_radio_group/cr_radio_group.d.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {LegacyElementMixin} from '//resources/polymer/v3_0/polymer/lib/legacy/legacy-element-mixin.js';
+import type {LegacyElementMixin} from '//resources/polymer/v3_0/polymer/lib/legacy/legacy-element-mixin.js';
 
 interface CrRadioGroupElement extends LegacyElementMixin, HTMLElement {
   disabled: boolean;
diff --git a/ui/webui/resources/cr_elements/cr_radio_group/cr_radio_group.ts b/ui/webui/resources/cr_elements/cr_radio_group/cr_radio_group.ts
index e9f6b44..4a8499e 100644
--- a/ui/webui/resources/cr_elements/cr_radio_group/cr_radio_group.ts
+++ b/ui/webui/resources/cr_elements/cr_radio_group/cr_radio_group.ts
@@ -9,7 +9,7 @@
 import {EventTracker} from '//resources/js/event_tracker.js';
 import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {CrRadioButtonElement} from '../cr_radio_button/cr_radio_button.js';
+import type {CrRadioButtonElement} from '../cr_radio_button/cr_radio_button.js';
 
 import {getTemplate} from './cr_radio_group.html.js';
 
diff --git a/ui/webui/resources/cr_elements/cr_scrollable_mixin.ts b/ui/webui/resources/cr_elements/cr_scrollable_mixin.ts
index 8080938..9e04343b 100644
--- a/ui/webui/resources/cr_elements/cr_scrollable_mixin.ts
+++ b/ui/webui/resources/cr_elements/cr_scrollable_mixin.ts
@@ -35,8 +35,9 @@
  */
 
 // clang-format off
-import {beforeNextRender, dedupingMixin, microTask, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {IronListElement} from '//resources/polymer/v3_0/iron-list/iron-list.js';
+import type { PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {beforeNextRender, dedupingMixin, microTask} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {IronListElement} from '//resources/polymer/v3_0/iron-list/iron-list.js';
 // clang-format on
 
 type IronListElementWithExtras = IronListElement&{
diff --git a/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.ts b/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.ts
index 1c5da2636..eeb09dd5 100644
--- a/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.ts
+++ b/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.ts
@@ -18,7 +18,7 @@
 
 import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {CrInputElement} from '../cr_input/cr_input.js';
+import type {CrInputElement} from '../cr_input/cr_input.js';
 
 import {getTemplate} from './cr_search_field.html.js';
 import {CrSearchFieldMixin} from './cr_search_field_mixin.js';
diff --git a/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_mixin.ts b/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_mixin.ts
index 203f1a5..d2ce33e 100644
--- a/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_mixin.ts
+++ b/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_mixin.ts
@@ -7,9 +7,10 @@
  * <settings-subpage-search> for a simple implementation.
  */
 import {assertNotReached} from '//resources/js/assert.js';
-import {dedupingMixin, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {dedupingMixin} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {CrInputElement} from '../cr_input/cr_input.js';
+import type {CrInputElement} from '../cr_input/cr_input.js';
 
 
 type Constructor<T> = new (...args: any[]) => T;
diff --git a/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.ts b/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.ts
index 02f22c41..848c41c1 100644
--- a/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.ts
+++ b/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.ts
@@ -23,10 +23,11 @@
 import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
 import '//resources/polymer/v3_0/paper-spinner/paper-spinner-lite.js';
 
-import {IronDropdownElement} from '//resources/polymer/v3_0/iron-dropdown/iron-dropdown.js';
-import {DomRepeatEvent, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {IronDropdownElement} from '//resources/polymer/v3_0/iron-dropdown/iron-dropdown.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';
 
-import {CrInputElement} from '../cr_input/cr_input.js';
+import type {CrInputElement} from '../cr_input/cr_input.js';
 
 import {getTemplate} from './cr_searchable_drop_down.html.js';
 
diff --git a/ui/webui/resources/cr_elements/cr_tabs/cr_tabs.ts b/ui/webui/resources/cr_elements/cr_tabs/cr_tabs.ts
index f42904b..98819da 100644
--- a/ui/webui/resources/cr_elements/cr_tabs/cr_tabs.ts
+++ b/ui/webui/resources/cr_elements/cr_tabs/cr_tabs.ts
@@ -22,7 +22,8 @@
 import '../cr_hidden_style.css.js';
 import '../cr_shared_vars.css.js';
 
-import {DomRepeatEvent, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {DomRepeatEvent} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getTemplate} from './cr_tabs.html.js';
 
diff --git a/ui/webui/resources/cr_elements/cr_toast/cr_toast_manager.ts b/ui/webui/resources/cr_elements/cr_toast/cr_toast_manager.ts
index 805fca59..786f102 100644
--- a/ui/webui/resources/cr_elements/cr_toast/cr_toast_manager.ts
+++ b/ui/webui/resources/cr_elements/cr_toast/cr_toast_manager.ts
@@ -10,7 +10,7 @@
 import {assert} from '//resources/js/assert.js';
 import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {CrToastElement} from './cr_toast.js';
+import type {CrToastElement} from './cr_toast.js';
 import {getTemplate} from './cr_toast_manager.html.js';
 
 let toastManagerInstance: CrToastManagerElement|null = null;
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.ts b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.ts
index 667ed11f..b16424ef 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.ts
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.ts
@@ -13,7 +13,7 @@
 import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getTemplate} from './cr_toolbar.html.js';
-import {CrToolbarSearchFieldElement} from './cr_toolbar_search_field.js';
+import type {CrToolbarSearchFieldElement} from './cr_toolbar_search_field.js';
 
 export interface CrToolbarElement {
   $: {
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.ts b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.ts
index 9d8e48f..16c2a59 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.ts
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.ts
@@ -9,7 +9,8 @@
 import '../cr_shared_vars.css.js';
 import '//resources/polymer/v3_0/paper-spinner/paper-spinner-lite.js';
 
-import {DomIf, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {DomIf} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {CrSearchFieldMixin} from '../cr_search_field/cr_search_field_mixin.js';
 
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.ts b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.ts
index ff32bc0..8a1f321 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.ts
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.ts
@@ -21,7 +21,7 @@
 import {IronA11yAnnouncer} from '//resources/polymer/v3_0/iron-a11y-announcer/iron-a11y-announcer.js';
 import {Debouncer, microTask, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {CrButtonElement} from '../cr_button/cr_button.js';
+import type {CrButtonElement} from '../cr_button/cr_button.js';
 
 import {getTemplate} from './cr_toolbar_selection_overlay.html.js';
 
diff --git a/ui/webui/resources/cr_elements/cr_tree/cr_tree.ts b/ui/webui/resources/cr_elements/cr_tree/cr_tree.ts
index 1102e37..83bf627 100644
--- a/ui/webui/resources/cr_elements/cr_tree/cr_tree.ts
+++ b/ui/webui/resources/cr_elements/cr_tree/cr_tree.ts
@@ -7,7 +7,8 @@
 
 import {getTemplate} from './cr_tree.html.js';
 import {CrTreeBaseElement} from './cr_tree_base.js';
-import {CrTreeItemElement, SELECTED_ATTR} from './cr_tree_item.js';
+import type {CrTreeItemElement} from './cr_tree_item.js';
+import {SELECTED_ATTR} from './cr_tree_item.js';
 
 /**
  * @fileoverview cr-tree is a container for a tree structure. Items can be added
diff --git a/ui/webui/resources/cr_elements/cr_view_manager/cr_view_manager.ts b/ui/webui/resources/cr_elements/cr_view_manager/cr_view_manager.ts
index a3b8567..3ee51ecd 100644
--- a/ui/webui/resources/cr_elements/cr_view_manager/cr_view_manager.ts
+++ b/ui/webui/resources/cr_elements/cr_view_manager/cr_view_manager.ts
@@ -5,7 +5,7 @@
 import {assert} from '//resources/js/assert.js';
 import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {CrLazyRenderElement} from '../cr_lazy_render/cr_lazy_render.js';
+import type {CrLazyRenderElement} from '../cr_lazy_render/cr_lazy_render.js';
 
 import {getTemplate} from './cr_view_manager.html.js';
 
diff --git a/ui/webui/resources/cr_elements/find_shortcut_mixin.ts b/ui/webui/resources/cr_elements/find_shortcut_mixin.ts
index af2667e..5323e6b 100644
--- a/ui/webui/resources/cr_elements/find_shortcut_mixin.ts
+++ b/ui/webui/resources/cr_elements/find_shortcut_mixin.ts
@@ -5,7 +5,8 @@
 import {assert, assertNotReached} from '//resources/js/assert.js';
 import {KeyboardShortcutList} from '//resources/js/keyboard_shortcut_list.js';
 import {isMac} from '//resources/js/platform.js';
-import {dedupingMixin, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {dedupingMixin} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 /**
  * @fileoverview Listens for a find keyboard shortcut (i.e. Ctrl/Cmd+f or /)
diff --git a/ui/webui/resources/cr_elements/focus_row_mixin.ts b/ui/webui/resources/cr_elements/focus_row_mixin.ts
index 1c7baaa..30b0856 100644
--- a/ui/webui/resources/cr_elements/focus_row_mixin.ts
+++ b/ui/webui/resources/cr_elements/focus_row_mixin.ts
@@ -3,10 +3,12 @@
 // found in the LICENSE file.
 
 // clang-format off
-import {afterNextRender, dedupingMixin, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type { PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {afterNextRender, dedupingMixin} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {focusWithoutInk} from 'chrome://resources/js/focus_without_ink.js';
-import {FocusRow, FocusRowDelegate} from 'chrome://resources/js/focus_row.js';
+import type { FocusRowDelegate} from 'chrome://resources/js/focus_row.js';
+import {FocusRow} from 'chrome://resources/js/focus_row.js';
 // clang-format on
 
 interface ListItem {
diff --git a/ui/webui/resources/cr_elements/i18n_mixin.ts b/ui/webui/resources/cr_elements/i18n_mixin.ts
index bac58ee..f09a0f3 100644
--- a/ui/webui/resources/cr_elements/i18n_mixin.ts
+++ b/ui/webui/resources/cr_elements/i18n_mixin.ts
@@ -11,8 +11,10 @@
  */
 
 import {loadTimeData} from '//resources/js/load_time_data.js';
-import {parseHtmlSubset, sanitizeInnerHtml, SanitizeInnerHtmlOpts} from '//resources/js/parse_html_subset.js';
-import {dedupingMixin, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {SanitizeInnerHtmlOpts} from '//resources/js/parse_html_subset.js';
+import {parseHtmlSubset, sanitizeInnerHtml} from '//resources/js/parse_html_subset.js';
+import type {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {dedupingMixin} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 type Constructor<T> = new (...args: any[]) => T;
 
diff --git a/ui/webui/resources/cr_elements/list_property_update_mixin.ts b/ui/webui/resources/cr_elements/list_property_update_mixin.ts
index b6e8297..e519ae6 100644
--- a/ui/webui/resources/cr_elements/list_property_update_mixin.ts
+++ b/ui/webui/resources/cr_elements/list_property_update_mixin.ts
@@ -15,7 +15,8 @@
  * containing information about all the edits is sent to the polyer object.
  */
 
-import {calculateSplices, dedupingMixin, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {calculateSplices, dedupingMixin} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 type Constructor<T> = new (...args: any[]) => T;
 
diff --git a/ui/webui/resources/cr_elements/mouse_hoverable_mixin.ts b/ui/webui/resources/cr_elements/mouse_hoverable_mixin.ts
index 8365491..c7560c3 100644
--- a/ui/webui/resources/cr_elements/mouse_hoverable_mixin.ts
+++ b/ui/webui/resources/cr_elements/mouse_hoverable_mixin.ts
@@ -8,7 +8,8 @@
  * mouse events too.
  */
 
-import {dedupingMixin, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {dedupingMixin} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 const HOVERED_STYLE: string = 'hovered';
 
diff --git a/ui/webui/resources/cr_elements/policy/cr_policy_indicator.ts b/ui/webui/resources/cr_elements/policy/cr_policy_indicator.ts
index 1aba0264..cf60483 100644
--- a/ui/webui/resources/cr_elements/policy/cr_policy_indicator.ts
+++ b/ui/webui/resources/cr_elements/policy/cr_policy_indicator.ts
@@ -9,7 +9,8 @@
 import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getTemplate} from './cr_policy_indicator.html.js';
-import {CrPolicyIndicatorMixin, CrPolicyIndicatorType} from './cr_policy_indicator_mixin.js';
+import type {CrPolicyIndicatorType} from './cr_policy_indicator_mixin.js';
+import {CrPolicyIndicatorMixin} from './cr_policy_indicator_mixin.js';
 
 
 const CrPolicyIndicatorElementBase = CrPolicyIndicatorMixin(PolymerElement);
diff --git a/ui/webui/resources/cr_elements/policy/cr_policy_indicator_mixin.ts b/ui/webui/resources/cr_elements/policy/cr_policy_indicator_mixin.ts
index fbaf1964..51cfd1f 100644
--- a/ui/webui/resources/cr_elements/policy/cr_policy_indicator_mixin.ts
+++ b/ui/webui/resources/cr_elements/policy/cr_policy_indicator_mixin.ts
@@ -9,7 +9,8 @@
  */
 
 import {assertNotReached} from '//resources/js/assert.js';
-import {dedupingMixin, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {dedupingMixin} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 /**
  * Strings required for policy indicators. These must be set at runtime.
diff --git a/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.ts b/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.ts
index ac6503a4..015b789 100644
--- a/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.ts
+++ b/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.ts
@@ -14,7 +14,7 @@
 
 import {CrPolicyIndicatorMixin, CrPolicyIndicatorType} from './cr_policy_indicator_mixin.js';
 import {getTemplate} from './cr_policy_pref_indicator.html.js';
-import {CrTooltipIconElement} from './cr_tooltip_icon.js';
+import type {CrTooltipIconElement} from './cr_tooltip_icon.js';
 
 const CrPolicyPrefIndicatorElementBase = CrPolicyIndicatorMixin(PolymerElement);
 
diff --git a/ui/webui/resources/cr_elements/store_client/store_client.ts b/ui/webui/resources/cr_elements/store_client/store_client.ts
index 655a13a..74db3e4 100644
--- a/ui/webui/resources/cr_elements/store_client/store_client.ts
+++ b/ui/webui/resources/cr_elements/store_client/store_client.ts
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Action, DeferredAction, Store} from '//resources/js/store.js';
-import {dedupingMixin, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {Action, DeferredAction, Store} from '//resources/js/store.js';
+import type {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {dedupingMixin} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 /**
  * @fileoverview defines a helper function `makeStoreClientMixin` to create a
diff --git a/ui/webui/resources/cr_elements/web_ui_listener_mixin.ts b/ui/webui/resources/cr_elements/web_ui_listener_mixin.ts
index 192d5b2e..68a11f03 100644
--- a/ui/webui/resources/cr_elements/web_ui_listener_mixin.ts
+++ b/ui/webui/resources/cr_elements/web_ui_listener_mixin.ts
@@ -7,8 +7,10 @@
  * automatically remove WebUI listeners when detached.
  */
 
-import {addWebUiListener, removeWebUiListener, WebUiListener} from '//resources/js/cr.js';
-import {dedupingMixin, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {WebUiListener} from '//resources/js/cr.js';
+import {addWebUiListener, removeWebUiListener} from '//resources/js/cr.js';
+import type {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {dedupingMixin} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 type Constructor<T> = new (...args: any[]) => T;
 
diff --git a/ui/webui/resources/js/color_utils.ts b/ui/webui/resources/js/color_utils.ts
index 5158ba2..e42848c6 100644
--- a/ui/webui/resources/js/color_utils.ts
+++ b/ui/webui/resources/js/color_utils.ts
@@ -6,7 +6,7 @@
  * @fileoverview Helper functions for color manipulations.
  */
 
-import {SkColor} from '//resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
+import type {SkColor} from '//resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
 
 /**
  * Converts an SkColor object to a string in the form
diff --git a/ui/webui/resources/js/focus_grid.ts b/ui/webui/resources/js/focus_grid.ts
index 6104029..07671a39 100644
--- a/ui/webui/resources/js/focus_grid.ts
+++ b/ui/webui/resources/js/focus_grid.ts
@@ -4,7 +4,7 @@
 
 // clang-format off
 import {assert} from './assert.js';
-import {FocusRow, FocusRowDelegate} from './focus_row.js';
+import type {FocusRow, FocusRowDelegate} from './focus_row.js';
 // clang-format on
 
 /**
diff --git a/ui/webui/resources/js/metrics_reporter/browser_proxy.ts b/ui/webui/resources/js/metrics_reporter/browser_proxy.ts
index d5243027..77a16d7 100644
--- a/ui/webui/resources/js/metrics_reporter/browser_proxy.ts
+++ b/ui/webui/resources/js/metrics_reporter/browser_proxy.ts
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {TimeDelta} from '//resources/mojo/mojo/public/mojom/base/time.mojom-webui.js';
+import type {TimeDelta} from '//resources/mojo/mojo/public/mojom/base/time.mojom-webui.js';
 
-import {PageMetricsCallbackRouter, PageMetricsHost, PageMetricsHostRemote} from '../metrics_reporter.mojom-webui.js';
+import type {PageMetricsHostRemote} from '../metrics_reporter.mojom-webui.js';
+import {PageMetricsCallbackRouter, PageMetricsHost} from '../metrics_reporter.mojom-webui.js';
 
 export interface BrowserProxy {
   getMark(name: string): Promise<{markedTime: TimeDelta | null}>;
diff --git a/ui/webui/resources/js/metrics_reporter/metrics_reporter.ts b/ui/webui/resources/js/metrics_reporter/metrics_reporter.ts
index 926274e..7ac7e14 100644
--- a/ui/webui/resources/js/metrics_reporter/metrics_reporter.ts
+++ b/ui/webui/resources/js/metrics_reporter/metrics_reporter.ts
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {TimeDelta} from '//resources/mojo/mojo/public/mojom/base/time.mojom-webui.js';
+import type {TimeDelta} from '//resources/mojo/mojo/public/mojom/base/time.mojom-webui.js';
 
 import {assert} from '../assert.js';
 
-import {BrowserProxy, BrowserProxyImpl} from './browser_proxy.js';
+import type {BrowserProxy} from './browser_proxy.js';
+import {BrowserProxyImpl} from './browser_proxy.js';
 
 function timeFromMojo(delta: TimeDelta): bigint {
   return delta.microseconds;
diff --git a/ui/webui/resources/js/mojo_type_util.ts b/ui/webui/resources/js/mojo_type_util.ts
index a08ebc0..5906e06 100644
--- a/ui/webui/resources/js/mojo_type_util.ts
+++ b/ui/webui/resources/js/mojo_type_util.ts
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {String16} from 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js';
-import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
+import type {String16} from 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js';
+import type {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 
 // Convert a javascript string into a Mojo String16.
 export function stringToMojoString16(str: string): String16 {
diff --git a/v8 b/v8
index e101644..93c5bbc 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit e1016444345b1bd45aade04a243fdf094b23f488
+Subproject commit 93c5bbc582e38f3a543ae0e5a28e593f2e7cc6dd