diff --git a/DEPS b/DEPS
index 08b694a..597176fc 100644
--- a/DEPS
+++ b/DEPS
@@ -295,7 +295,7 @@
   # 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': '56087f1ee64af85f055c3dffe1d540ea90626924',
+  'src_internal_revision': '54ba5bc847bfa9724ef0b67b1c88da2704467a09',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
@@ -303,15 +303,15 @@
   # 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': '3e4d2e9bda03056d1e4f1434f95777aeb03fbe1f',
+  'v8_revision': 'e3624ed658727258f0fe4b3d0ff11a9d27881ac4',
   # 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': '25fc50465713d5c524fc49dda81e80624fd676a5',
+  'angle_revision': 'b65791809eeeff4e6e243e552e21d174080cdc54',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': 'b62ac8aa106b86158bb5925004d1b3658d664756',
+  'swiftshader_revision': '974741b1cbc8c648e6beb74ff93182665af6286a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -319,7 +319,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
-  'boringssl_revision': '9a895ea00a34ce7ce2d9f087c9d8bbfc639e233d',
+  'boringssl_revision': '60d933e9d62d33a8d58bb350dea7c27b6b18b619',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Fuchsia sdk
   # and whatever else without interference from each other.
@@ -391,7 +391,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'ef9aad93988e3ff4149c5ea6e337abf234dc74de',
+  'devtools_frontend_revision': '71c4f8a7f072f0f31369d45241ffb6c24769a320',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -1521,12 +1521,12 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    'd7c11e788469c544db99807d9d272a651b39437f',
+    '2828bd1ff4886faee721d4943c8d68b9e2315640',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
   'src/docs/website': {
-    'url': Var('chromium_git') + '/website.git' + '@' + 'ecd9c325be30d2d9a9fe22964c5728f16de183b2',
+    'url': Var('chromium_git') + '/website.git' + '@' + 'c53221ec59afc47168647f603ba0ed197707d015',
   },
 
   'src/ios/third_party/earl_grey2/src': {
@@ -1983,7 +1983,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' + '@' + '1b3f6f4b3bf374f7fe826a19bd009d346f024ddf',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'a61d51627b18bfe921b8698a092f28523d96bd44',
       'condition': 'checkout_chromeos',
   },
 
@@ -1997,7 +1997,7 @@
     Var('chromium_git') + '/external/github.com/google/cpu_features.git' + '@' + '936b9ab5515dead115606559502e3864958f7f6e',
 
   'src/third_party/cpuinfo/src':
-    Var('chromium_git') + '/external/github.com/pytorch/cpuinfo.git' + '@' + 'de0ce7c7251372892e53ce9bc891750d2c9a4fd8',
+    Var('chromium_git') + '/external/github.com/pytorch/cpuinfo.git' + '@' + '6c9eb84ba310f237cea13c478be50102e1128e9b',
 
   'src/third_party/crc32c/src':
     Var('chromium_git') + '/external/github.com/google/crc32c.git' + '@' + 'd3d60ac6e0f16780bcfcc825385e1d338801a558',
@@ -2013,7 +2013,7 @@
 
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '6d52c22ee313eb5701f326a9eca98acb41b669b4',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '4e7e235dc89c740ddb1cca39ef2ad1fca7745be1',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -2025,7 +2025,7 @@
     Var('chromium_git') + '/external/github.com/jk-jeon/dragonbox.git' + '@' + '6c7c925b571d54486b9ffae8d9d18a822801cbda',
 
   'src/third_party/eigen3/src':
-    Var('chromium_git') + '/external/gitlab.com/libeigen/eigen.git' + '@' + '171bd08ca987987c3c50f0fa5dd8914bdd42dd3b',
+    Var('chromium_git') + '/external/gitlab.com/libeigen/eigen.git' + '@' + '21e89b930c6af56dbdaeea2a91d8b9d6fd2c208a',
 
   'src/third_party/emoji-metadata/src': {
     'url': Var('chromium_git') + '/external/github.com/googlefonts/emoji-metadata' + '@' + '045f146fca682a836e01cd265171312bfb300e06',
@@ -2561,7 +2561,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '557fafc38445fdde33d19b1cf2d784686e51d120',
+    Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '576110306936083fafbdab5594b96d25d4b4106d',
 
   'src/base/tracing/test/data': {
     'bucket': 'perfetto',
@@ -2867,7 +2867,7 @@
     Var('chromium_git') + '/external/github.com/GoogleChromeLabs/text-fragments-polyfill.git' + '@' + 'c036420683f672d685e27415de0a5f5e85bdc23f',
 
   'src/third_party/tflite/src':
-    Var('chromium_git') + '/external/github.com/tensorflow/tensorflow.git' + '@' + '2acd05be6d3a637cb2ed2b040879c55b02d6bc19',
+    Var('chromium_git') + '/external/github.com/tensorflow/tensorflow.git' + '@' + '60fb9c76b41f0e4d6cfdd6b1bd2fabe443455306',
 
   'src/third_party/turbine/cipd': {
       'packages': [
@@ -2880,7 +2880,7 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@0dbc0583398406ec3c74f88cc1fb4dc21f478040',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@d96f5ecf24060ed2e820d8f1f74a2b1bfb3b0c64',
   'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@fc060af4813d2d9b973f982c4987e1d53b1ec1c6',
   'src/third_party/spirv-cross/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Cross@b8fcf307f1f347089e3c46eb4451d27f32ebc8d3',
   'src/third_party/spirv-headers/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Headers@fd96661925488574fe247a779babe5d380b63635',
@@ -2889,7 +2889,7 @@
   'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@bf4aec7ddb0f46d729ebd3255296d9fe8eed73ec',
   'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@e4fb76dc08f139df0436e9c3031f75be5e1f6264',
   'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@03e1445cc7cce22baeeef8eff7bb934362d040eb',
-  'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@596534df7e93137255f803653de4614e9d9e7ba2',
+  'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@287caf1be206dca1b8a391b10eab474a7e238fd6',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '56300b29fbfcc693ee6609ddad3fdd5b7a449a21',
@@ -2934,7 +2934,7 @@
     Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'),
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'fd4a59c2a43b208d965899a6cef10404d5cdc43d',
+    Var('webrtc_git') + '/src.git' + '@' + '382cb45e818024f4168918f6b04f99dd659f50ca',
 
   # 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.
@@ -2958,7 +2958,7 @@
   },
 
   'src/third_party/xnnpack/src':
-    Var('chromium_git') + '/external/github.com/google/XNNPACK.git' + '@' + '84f98c030577fc1d8c3a5fcc703f765fd8a976be',
+    Var('chromium_git') + '/external/github.com/google/XNNPACK.git' + '@' + '888719e9a4b11251d03ad2d6526da7f2e2b57308',
 
   'src/third_party/libei/cipd': {
 
@@ -3067,7 +3067,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'LYWJSEzqpCgtBIqT2BKq11-IJkgpnpEoWxPh5nRwzC4C',
+        'version': 'Xx8Np3hP8yJ_5R6UnwVwSIK4-PI1kWosVQwfDm4i5pYC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3078,7 +3078,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'aftrelzAo2s2__6RphIyEjIKm9ZsJ6do4OSitkVEwXsC',
+        'version': 'aW6pyiiHG37yEwcRiYLCQXxtpoeY2yvs1aMc_KoVwigC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4654,7 +4654,7 @@
 
   'src/components/optimization_guide/internal': {
       'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' +
-        '628b2acf00d0de13413f41d29f044cae2ee865be',
+        'f1e5abb6f8c93c4eb6ba9a48bdc40cd4b9579779',
       'condition': 'checkout_src_internal',
   },
 
@@ -4720,7 +4720,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        'c98c23282a810917184ef42c8f5c8724474266e9',
+        'badd9e01b2f87ed45b48ca5b26ede8cbafb95ed4',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_field_trials.cc b/android_webview/browser/aw_field_trials.cc
index 859e274c..4b8ce8ff 100644
--- a/android_webview/browser/aw_field_trials.cc
+++ b/android_webview/browser/aw_field_trials.cc
@@ -121,8 +121,7 @@
 
   // WebView does not support multiple processes, so don't try to call some
   // MediaDrm APIs in a separate process.
-  aw_feature_overrides.DisableFeature(
-      media::kAllowMediaCodecCallsInSeparateProcess);
+  aw_feature_overrides.DisableFeature(media::kMediaDrmQueryInSeparateProcess);
 
   aw_feature_overrides.DisableFeature(::features::kBackgroundFetch);
 
diff --git a/android_webview/browser/aw_quota_manager_bridge.cc b/android_webview/browser/aw_quota_manager_bridge.cc
index 2956a8d2..5106707 100644
--- a/android_webview/browser/aw_quota_manager_bridge.cc
+++ b/android_webview/browser/aw_quota_manager_bridge.cc
@@ -291,8 +291,7 @@
   // (Legacy) Clear all web storage data except cookies.
   uint32_t remove_mask = StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS |
                          StoragePartition::REMOVE_DATA_MASK_INDEXEDDB |
-                         StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE |
-                         StoragePartition::REMOVE_DATA_MASK_WEBSQL;
+                         StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE;
   GetStoragePartition()->ClearData(
       remove_mask, StoragePartition::QUOTA_MANAGED_STORAGE_MASK_TEMPORARY,
       blink::StorageKey(), base::Time(), base::Time::Max(), base::DoNothing());
@@ -307,8 +306,7 @@
   StoragePartition* storage_partition = GetStoragePartition();
   // All (temporary) QuotaClient types.
   uint32_t remove_mask = StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS |
-                         StoragePartition::REMOVE_DATA_MASK_INDEXEDDB |
-                         StoragePartition::REMOVE_DATA_MASK_WEBSQL;
+                         StoragePartition::REMOVE_DATA_MASK_INDEXEDDB;
   storage_partition->ClearDataForOrigin(
       remove_mask, StoragePartition::QUOTA_MANAGED_STORAGE_MASK_TEMPORARY,
       GURL(origin_string), base::DoNothing());
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 25fd675..2b53e48 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -99,6 +99,7 @@
 import org.chromium.components.embedder_support.util.TouchEventFilter;
 import org.chromium.components.embedder_support.util.WebResourceResponseInfo;
 import org.chromium.components.navigation_interception.InterceptNavigationDelegate;
+import org.chromium.components.origin_matcher.OriginMatcher;
 import org.chromium.components.sensitive_content.SensitiveContentFeatures;
 import org.chromium.components.stylus_handwriting.StylusHandwritingFeatureMap;
 import org.chromium.components.stylus_handwriting.StylusWritingController;
@@ -1766,12 +1767,24 @@
                 previousState.javascriptInterfaces.entrySet()) {
             @SuppressWarnings("unchecked")
             JavascriptInjector.InjectedInterface injected = entry.getValue();
-            getJavascriptInjector()
-                    .addPossiblyUnsafeInterface(
-                            injected.getInjectedObject(),
-                            entry.getKey(),
-                            injected.getRequiredAnnotation(),
-                            injected.getOriginAllowlist());
+
+            OriginMatcher matcher = new OriginMatcher();
+            try {
+                List<String> badRules = matcher.setRuleList(injected.getMatcherRules());
+                // We should only be storing well formed rules at this point.
+                assert badRules.size() == 0;
+
+                getJavascriptInjector()
+                        .addPossiblyUnsafeInterfaceToOrigins(
+                                injected.getInjectedObject(),
+                                entry.getKey(),
+                                injected.getRequiredAnnotation(),
+                                matcher);
+            } finally {
+                // The matcher has a native counter part so we need to clean it
+                // after we are done with it. The injector will copy whatever it uses.
+                matcher.destroy();
+            }
         }
 
         // Restore injected WebMessageListeners.
@@ -3562,8 +3575,6 @@
 
     public List<String> addJavascriptInterface(
             Object object, String name, @NonNull List<String> originAllowlist) {
-        // TODO(crbug.com/383099115): Get rid of this allowlist version of addJavascriptInterface
-        // and instead rely on some allowlist state.
         if (TRACE) Log.i(TAG, "%s addJavascriptInterface=%s", this, name);
         if (isDestroyed(WARN)) return Collections.emptyList();
 
@@ -3581,8 +3592,24 @@
             requiredAnnotation = JavascriptInterface.class;
         }
 
-        return getJavascriptInjector()
-                .addPossiblyUnsafeInterface(object, name, requiredAnnotation, originAllowlist);
+        // If any rules were ill-formed, we will skip injection and return the
+        // bad rules.
+        OriginMatcher matcher = new OriginMatcher();
+        try {
+            List<String> badRules = matcher.setRuleList(originAllowlist);
+            if (badRules.size() != 0) {
+                return badRules;
+            }
+
+            getJavascriptInjector()
+                    .addPossiblyUnsafeInterfaceToOrigins(object, name, requiredAnnotation, matcher);
+        } finally {
+            // The matcher has a native counter part so we need to clean it
+            // after we are done with it. The injector will copy whatever it uses.
+            matcher.destroy();
+        }
+
+        return Collections.emptyList();
     }
 
     /**
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/VisualStateTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/VisualStateTest.java
index 786f76b..5c82d7403 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/VisualStateTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/VisualStateTest.java
@@ -44,7 +44,6 @@
 import java.io.FilterInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
@@ -507,10 +506,7 @@
                         () -> {
                             JavascriptInjector.fromWebContents(awContents.getWebContents())
                                     .addPossiblyUnsafeInterface(
-                                            pageChangeNotifier,
-                                            "pageChangeNotifier",
-                                            null,
-                                            List.of("*"));
+                                            pageChangeNotifier, "pageChangeNotifier", null);
                             awContents.loadUrl(WAIT_FOR_JS_DETACHED_TEST_URL);
                         });
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/util/JavascriptEventObserver.java b/android_webview/javatests/src/org/chromium/android_webview/test/util/JavascriptEventObserver.java
index 63a456c2..f8a2c46e 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/util/JavascriptEventObserver.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/util/JavascriptEventObserver.java
@@ -8,7 +8,6 @@
 import org.chromium.content_public.browser.JavascriptInjector;
 import org.chromium.content_public.browser.WebContents;
 
-import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
@@ -32,7 +31,7 @@
      */
     public void register(WebContents webContents, String name) {
         JavascriptInjector.fromWebContents(webContents)
-                .addPossiblyUnsafeInterface(this, name, null, List.of("*"));
+                .addPossiblyUnsafeInterface(this, name, null);
     }
 
     /**
diff --git a/ash/display/display_manager_unittest.cc b/ash/display/display_manager_unittest.cc
index 931361c..7d974e6 100644
--- a/ash/display/display_manager_unittest.cc
+++ b/ash/display/display_manager_unittest.cc
@@ -5218,7 +5218,10 @@
           display::Screen::GetScreen()->GetPrimaryDisplay().bounds()));
   transformed_rect2 =
       host_list[0]->window()->transform().MapRect(transformed_rect2);
-  EXPECT_EQ(gfx::RectF(50.0f, 0.0f, 600.0f, 800.0f), transformed_rect2);
+  // Use gfx::ToEnclosingRect because `transformed_rect2` has rounding errors.
+  // External display shouldn't be rotated.
+  EXPECT_EQ(gfx::Rect(137.0f, 0.0f, 525.0f, 700.0f),
+            gfx::ToEnclosingRect(transformed_rect2));
 
   // Change the bounds of the source display and rotate the source display by 90
   // degrees.
@@ -5238,7 +5241,8 @@
   transformed_rect3 =
       host_list[0]->window()->transform().MapRect(transformed_rect3);
   // Use gfx::ToEnclosingRect because `transformed_rect3` has rounding errors.
-  EXPECT_EQ(gfx::Rect(0.0f, 137.0f, 700.0f, 525.0f),
+  // External display shouldn't be rotated.
+  EXPECT_EQ(gfx::Rect(0.0f, 50.0f, 800.0f, 600.0f),
             gfx::ToEnclosingRect(transformed_rect3));
 }
 
diff --git a/ash/display/mirror_window_controller.cc b/ash/display/mirror_window_controller.cc
index 1a34080..31cd1d5 100644
--- a/ash/display/mirror_window_controller.cc
+++ b/ash/display/mirror_window_controller.cc
@@ -269,18 +269,6 @@
     auto* mirroring_host_info =
         mirroring_host_info_map_[display_info.id()].get();
 
-    const bool should_undo_rotation = ShouldUndoRotationForMirror();
-
-    if (!should_undo_rotation && !display_manager->IsInUnifiedMode()) {
-      // Use the rotation from source display without panel orientation
-      // applied instead of the display transform hint in |source_compositor|
-      // so that panel orientation is not applied to the mirror host.
-      mirroring_host_info->ash_host->AsWindowTreeHost()
-          ->SetDisplayTransformHint(display::DisplayRotationToOverlayTransform(
-              display_manager->GetDisplayInfo(reflecting_source_id_)
-                  .GetActiveRotation()));
-    }
-
     aura::Window* mirror_window = mirroring_host_info->mirror_window;
     mirror_window->SetBounds(gfx::Rect(mirror_size));
     mirror_window->Show();
diff --git a/ash/display/mirror_window_controller_unittest.cc b/ash/display/mirror_window_controller_unittest.cc
index 853277f7..474c8c7 100644
--- a/ash/display/mirror_window_controller_unittest.cc
+++ b/ash/display/mirror_window_controller_unittest.cc
@@ -124,7 +124,9 @@
 class MirrorWindowControllerRotationAndPanelOrientationTest
     : public MirrorWindowControllerTest,
       public testing::WithParamInterface<
-          std::tuple<display::Display::Rotation, display::PanelOrientation>> {};
+          std::tuple<display::Display::Rotation,
+                     display::Display::Rotation,
+                     display::PanelOrientation>> {};
 
 // Test that the mirror display matches the size and rotation of the source.
 TEST_P(MirrorWindowControllerRotationAndPanelOrientationTest, MirrorSize) {
@@ -132,7 +134,8 @@
   const int64_t mirror_id = 2;
 
   const display::Display::Rotation active_rotation = std::get<0>(GetParam());
-  const display::PanelOrientation panel_orientation = std::get<1>(GetParam());
+  const display::Display::Rotation mirror_rotation = std::get<1>(GetParam());
+  const display::PanelOrientation panel_orientation = std::get<2>(GetParam());
 
   // Run the test with and without display scaling.
   int scale_factors[] = {1, 2};
@@ -143,8 +146,10 @@
     primary_display_info.SetRotation(active_rotation,
                                      display::Display::RotationSource::ACTIVE);
 
-    const display::ManagedDisplayInfo mirror_display_info =
+    display::ManagedDisplayInfo mirror_display_info =
         CreateDisplayInfo(mirror_id, gfx::Rect(400, 0, 600, 500), scale);
+    mirror_display_info.SetRotation(mirror_rotation,
+                                    display::Display::RotationSource::ACTIVE);
     std::vector<display::ManagedDisplayInfo> display_info_list = {
         primary_display_info, mirror_display_info};
 
@@ -164,10 +169,10 @@
     EXPECT_EQ(primary_display.GetSizeInPixel(), root_window->bounds().size());
     EXPECT_EQ(primary_display.GetSizeInPixel(), mirror_window->bounds().size());
 
-    // Mirror should have a display transform hint that matches the active
-    // rotation (excluding the panel orientation) of the source.
-    EXPECT_EQ(display::DisplayRotationToOverlayTransform(active_rotation),
-              root_window->GetHost()->compositor()->display_transform_hint());
+    // Mirror Display should not have its rotation changed no matter what the
+    // primary display's rotation or orientation.
+    mirror_display_info = display_manager()->GetDisplayInfo(mirror_id);
+    EXPECT_EQ(mirror_rotation, mirror_display_info.GetLogicalActiveRotation());
   }
 }
 
@@ -178,6 +183,10 @@
                                      display::Display::ROTATE_90,
                                      display::Display::ROTATE_180,
                                      display::Display::ROTATE_270),
+                     testing::Values(display::Display::ROTATE_0,
+                                     display::Display::ROTATE_90,
+                                     display::Display::ROTATE_180,
+                                     display::Display::ROTATE_270),
                      testing::Values(display::PanelOrientation::kNormal,
                                      display::PanelOrientation::kBottomUp,
                                      display::PanelOrientation::kLeftUp,
diff --git a/ash/display/root_window_transformers.cc b/ash/display/root_window_transformers.cc
index 5edee42..5eceb09 100644
--- a/ash/display/root_window_transformers.cc
+++ b/ash/display/root_window_transformers.cc
@@ -205,6 +205,8 @@
         gfx::Rect(source_display_info.GetSizeInPixelWithPanelOrientation());
     display::Display::Rotation active_root_rotation =
         source_display_info.GetActiveRotation();
+    display::Display::Rotation active_mirror_rotation =
+        mirror_display_info.GetActiveRotation();
 
     const bool should_undo_rotation = ShouldUndoRotationForMirror();
     gfx::Transform rotation_transform;
@@ -229,11 +231,12 @@
     // `transform_` needs to be calculated without the rotation.
     // E.g. host native size 1600x1200. Rotation 90 degree. `transform_` needs
     // to fit 1200x1600 rather than 1600x1200.
-    const bool need_transpose =
-        active_root_rotation == display::Display::ROTATE_90 ||
-        active_root_rotation == display::Display::ROTATE_270;
-    if (need_transpose) {
+    if (active_root_rotation == display::Display::ROTATE_90 ||
+        active_root_rotation == display::Display::ROTATE_270) {
       root_bounds_.Transpose();
+    }
+    if (active_mirror_rotation == display::Display::ROTATE_90 ||
+        active_mirror_rotation == display::Display::ROTATE_270) {
       mirror_display_rect.Transpose();
     }
 
diff --git a/ash/display/root_window_transformers_unittest.cc b/ash/display/root_window_transformers_unittest.cc
index a6191fa..bb557f1 100644
--- a/ash/display/root_window_transformers_unittest.cc
+++ b/ash/display/root_window_transformers_unittest.cc
@@ -434,12 +434,12 @@
   display_manager()->SetMirrorMode(display::MirrorMode::kNormal, std::nullopt);
   std::unique_ptr<RootWindowTransformer> transformer(
       CreateCurrentRootWindowTransformerForMirroring());
-  // Y margin must be margin is (400 - 500/400 * 200) / 2 = 75
+  // Y margin is (400 - 500/400 * 200) / 2 = 75
   EXPECT_EQ(gfx::Insets::TLBR(0, 75, 0, 75), transformer->GetHostInsets());
 
   // Pillar boxed
   UpdateDisplay("200x400,500x400");
-  // X margin must be margin is (500 - 200) / 2 = 150
+  // X margin is (500 - 200) / 2 = 150
   transformer = CreateCurrentRootWindowTransformerForMirroring();
   EXPECT_EQ(gfx::Insets::TLBR(150, 0, 150, 0), transformer->GetHostInsets());
 }
@@ -461,17 +461,16 @@
 
     const bool need_transpose = rotation == display::Display::ROTATE_90 ||
                                 rotation == display::Display::ROTATE_270;
-    // Y margin is (400 - 500/400 * 200) / 2 = 75 for no rotation. Transposed
-    // on 90/270 degree.
+    // X margin is (500 - 200) / 2 = 150 for with rotation.
+    // Y margin is (400 - 500/400 * 200) / 2 = 75 for without rotation.
     gfx::Insets expected_insets =
-        need_transpose ? gfx::Insets::VH(75, 0) : gfx::Insets::VH(0, 75);
+        need_transpose ? gfx::Insets::VH(150, 0) : gfx::Insets::VH(0, 75);
     EXPECT_EQ(expected_insets, transformer->GetHostInsets());
 
-    // Expected rect in mirror of the source root, with y margin applied for no
-    // rotation. Transposed on 90/270 degree.
-    gfx::RectF expected_rect(0, 75, 500, 250);
-    if (need_transpose)
-      expected_rect.Transpose();
+    // Expected rect in mirror of the source root, x margin applied for with
+    // rotation and y margin applied for without rotation.
+    gfx::RectF expected_rect = need_transpose ? gfx::RectF(150, 0, 200, 400)
+                                              : gfx::RectF(0, 75, 500, 250);
 
     gfx::RectF rect = transformer->GetTransform().MapRect(
         gfx::RectF(transformer->GetRootWindowBounds(gfx::Size())));
diff --git a/ash/quick_pair/common/fast_pair/fast_pair_decoder.cc b/ash/quick_pair/common/fast_pair/fast_pair_decoder.cc
index f74f85b..45f15a8 100644
--- a/ash/quick_pair/common/fast_pair/fast_pair_decoder.cc
+++ b/ash/quick_pair/common/fast_pair/fast_pair_decoder.cc
@@ -85,10 +85,10 @@
          kExtraFieldTypeOffset;
 }
 
-// TODO(399163998): Remove deprecated code after feature launch.
+// TODO(399163998): Deprecate this code after feature launch. This function is
+// also used when parsing not discoverable advertisements for subsequent pair,
+// so we'll have to audit that code as well.
 int GetIdLength(const std::vector<uint8_t>* service_data) {
-  CHECK(!features::IsFastPairAdvertisingFormat2025Enabled());
-
   return service_data->size() == kMinModelIdLength
              ? kMinModelIdLength
              : ((*service_data)[kHeaderIndex] & kHeaderLengthBitmask2018) >>
diff --git a/ash/shelf/shelf_app_button.cc b/ash/shelf/shelf_app_button.cc
index 166f6b4..96a4d1d 100644
--- a/ash/shelf/shelf_app_button.cc
+++ b/ash/shelf/shelf_app_button.cc
@@ -25,7 +25,6 @@
 #include "ash/system/progress_indicator/progress_indicator.h"
 #include "ash/wm/desks/desks_controller.h"
 #include "ash/wm/window_util.h"
-#include "base/debug/stack_trace.h"
 #include "base/functional/bind.h"
 #include "base/i18n/rtl.h"
 #include "base/metrics/histogram_macros.h"
diff --git a/ash/webui/common/resources/BUILD.gn b/ash/webui/common/resources/BUILD.gn
index 8ddabe12..0ba9ed1 100644
--- a/ash/webui/common/resources/BUILD.gn
+++ b/ash/webui/common/resources/BUILD.gn
@@ -23,6 +23,7 @@
   "multidevice_setup/button_bar.js",
   "multidevice_setup/multidevice_setup.js",
   "multidevice_setup/password_page.js",
+  "multidevice_setup/pausable_lottie.js",
   "multidevice_setup/setup_succeeded_page.js",
   "multidevice_setup/start_setup_page.js",
   "multidevice_setup/ui_page.js",
@@ -444,6 +445,8 @@
   "multidevice_setup/multidevice_setup_browser_proxy.js",
   "multidevice_setup/multidevice_setup_delegate.js",
   "multidevice_setup/multidevice_setup_shared.css.js",
+  "multidevice_setup/pausable_lottie.html.js",
+  "multidevice_setup/pausable_lottie.js",
   "multidevice_setup/setup_succeeded_page.html.js",
   "multidevice_setup/setup_succeeded_page.js",
   "multidevice_setup/start_setup_page.html.js",
diff --git a/ash/webui/common/resources/multidevice_setup/icons.html b/ash/webui/common/resources/multidevice_setup/icons.html
index 263debc5..af9ce37 100644
--- a/ash/webui/common/resources/multidevice_setup/icons.html
+++ b/ash/webui/common/resources/multidevice_setup/icons.html
@@ -54,3 +54,24 @@
     </defs>
   </svg>
 </iron-iconset-svg>
+<iron-iconset-svg name="multidevice-setup-icons-48" size="48">
+  <svg>
+    <defs>
+      <g id="pause" opacity="0.6">
+        <g style="mix-blend-mode:multiply">
+          <circle cx="24" cy="24" r="22" fill="#202124"></circle>
+        </g>
+        <path d="M22.4 16H17.6V32H22.4V16Z" fill="white"></path>
+        <path d="M30.4 16H25.6V32H30.4V16Z" fill="white"></path>
+      </g>
+      <g id="play" opacity="0.6">
+        <g style="mix-blend-mode:multiply">
+          <circle cx="24" cy="24" r="22" fill="#202124"></circle>
+        </g>
+        <path fill-rule="evenodd" clip-rule="evenodd"
+            d="M17.6 33.5999L33.6 23.9999L17.6 14.3999V33.5999Z" fill="white">
+        </path>
+      </g>
+    </defs>
+  </svg>
+</iron-iconset-svg>
diff --git a/ash/webui/common/resources/multidevice_setup/multidevice_setup_shared.css b/ash/webui/common/resources/multidevice_setup/multidevice_setup_shared.css
index b102d13..261ef64 100644
--- a/ash/webui/common/resources/multidevice_setup/multidevice_setup_shared.css
+++ b/ash/webui/common/resources/multidevice_setup/multidevice_setup_shared.css
@@ -7,13 +7,12 @@
  * #scheme=relative
  * #import=//resources/ash/common/cr_elements/cr_shared_style.css.js
  * #import=//resources/ash/common/cr_elements/cr_shared_vars.css.js
+ * #import=//resources/ash/common/cr_elements/cros_color_overrides.css.js
  * #import=//resources/ash/common/cr_elements/md_select.css.js
  * #import=//resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js
  * #include=iron-flex cr-shared-style md-select
  * #css_wrapper_metadata_end */
 
-@import 'ash/webui/common/resources/cr_elements/cros_color_overrides.css';
-
 a {
   color: var(--cros-sys-primary);
 }
diff --git a/ash/webui/common/resources/multidevice_setup/pausable_lottie.html b/ash/webui/common/resources/multidevice_setup/pausable_lottie.html
new file mode 100644
index 0000000..0f6ab8ea
--- /dev/null
+++ b/ash/webui/common/resources/multidevice_setup/pausable_lottie.html
@@ -0,0 +1,48 @@
+<!--
+Copyright 2025 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<style>
+  :host {
+    height: 100%;
+    width: 100%;
+  }
+  #container {
+    height: 100%;
+    position: relative;
+    width: 100%;
+  }
+  #animation {
+    height: 100%;
+    position: absolute;
+    width: 100%;
+  }
+  #playPauseIcon {
+    bottom: 0;
+    left: 0;
+    margin: auto;
+    opacity: 0; /* We use opacity to hide/show the element so it stays in
+                    the tab index */
+    position: absolute;
+    right: 0;
+    top: 0;
+  }
+  #playPauseIcon:focus,
+  :host(:hover) #playPauseIcon {
+    opacity: 1;
+  }
+  cr-icon-button {
+    --cr-icon-button-icon-size: 40px;
+    --cr-icon-button-size: 48px;
+  }
+</style>
+<div id="container">
+  <!-- <cros-lottie-renderer> created dynamically -->
+  <div id="playPauseIconContainer" hidden="[[hidePlayPauseIcon]]" >
+    <cr-icon-button id="playPauseIcon" iron-icon="[[getIcon(playing)]]"
+        aria-label$="[[getAria(locale, playing)]]"></cr-icon-button>
+  </div>
+
+</div>
\ No newline at end of file
diff --git a/ash/webui/common/resources/multidevice_setup/pausable_lottie.js b/ash/webui/common/resources/multidevice_setup/pausable_lottie.js
new file mode 100644
index 0000000..9228853
--- /dev/null
+++ b/ash/webui/common/resources/multidevice_setup/pausable_lottie.js
@@ -0,0 +1,105 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+import '//resources/ash/common/cr_elements/cr_icon_button/cr_icon_button.js';
+import './icons.html.js';
+
+import {I18nBehavior} from '//resources/ash/common/i18n_behavior.js';
+import {LottieRenderer} from '//resources/cros_components/lottie_renderer/lottie-renderer.js';
+import {assert} from '//resources/js/assert.js';
+import {Polymer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './pausable_lottie.html.js';
+
+/**
+ * Lottie wrapper to allow users to click to pause.  Inspired by
+ * <oobe-cr-lottie>
+ */
+Polymer({
+  _template: getTemplate(),
+  is: 'pausable-lottie',
+  behaviors: [
+    I18nBehavior,
+  ],
+  properties: {
+    playing: {
+      type: Boolean,
+      observer: 'onPlayingChanged',
+      value: false,
+    },
+    animationUrl: {
+      type: String,
+      observer: 'onUrlChanged',
+      value: '',
+    },
+    hidePlayPauseIcon: {
+      type: Boolean,
+      value: false,
+    },
+    preload: {
+      type: Boolean,
+      value: false,
+    },
+    /**
+     * Whether or not the illustration should render using a dynamic palette.
+     * nuke this property when all animation migrated.
+     */
+    dynamic: {
+      type: Boolean,
+      value: true,
+    },
+    animationPlayer: {type: LottieRenderer | null, value: null},
+  },
+  ready() {
+    this.addEventListener('click', this.onClick);
+    this.addEventListener(
+        'cros-lottie-initialized', this.onInitialized, {once: true});
+    // Preload the player so that the first frame is shown.
+    if (this.preload) {
+      this.createPlayer(/*autoplay=*/ true);
+    }
+  },
+  onClick() {
+    if (this.hidePlayPauseIcon) {
+      return;
+    }
+    this.playing = !this.playing;
+  },
+  createPlayer(autoplay = true) {
+    this.animationPlayer = document.createElement('cros-lottie-renderer');
+    this.animationPlayer.id = 'animation';
+    this.animationPlayer.setAttribute('asset-url', this.animationUrl);
+    this.animationPlayer.setAttribute('dynamic', String(this.dynamic));
+    this.animationPlayer.autoplay = autoplay;
+    const container = this.shadowRoot?.querySelector('#container');
+    assert(container instanceof HTMLElement);
+    const playPauseIconContainer =
+        this.shadowRoot?.querySelector('#playPauseIconContainer');
+    assert(playPauseIconContainer instanceof HTMLElement);
+    container.insertBefore(this.animationPlayer, playPauseIconContainer);
+  },
+  onUrlChanged() {
+    if (this.animationUrl && this.animationPlayer) {
+      this.animationPlayer.setAttribute('asset-url', this.animationUrl);
+    }
+  },
+  onPlayingChanged() {
+    if (this.animationPlayer) {
+      if (this.playing) {
+        this.animationPlayer.play();
+      } else {
+        this.animationPlayer.pause();
+      }
+    } else if (this.playing) {
+      this.createPlayer(/*autoplay=*/ true);
+    }
+  },
+  getIcon(playing) {
+    return playing ? 'multidevice-setup-icons-48:pause' :
+                     'multidevice-setup-icons-48:play';
+  },
+  getAria(_locale, playing) {
+    return this.i18n(
+        playing ? 'pauseAnimationAriaLabel' : 'playAnimationAriaLabel');
+  },
+});
diff --git a/ash/wm/overview/scoped_overview_transform_window.cc b/ash/wm/overview/scoped_overview_transform_window.cc
index 3482e3d..4ad25c7 100644
--- a/ash/wm/overview/scoped_overview_transform_window.cc
+++ b/ash/wm/overview/scoped_overview_transform_window.cc
@@ -32,7 +32,6 @@
 #include "ash/wm/window_util.h"
 #include "ash/wm/wm_constants.h"
 #include "base/auto_reset.h"
-#include "base/debug/stack_trace.h"
 #include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
 #include "base/task/single_thread_task_runner.h"
diff --git a/base/android/jni_android.cc b/base/android/jni_android.cc
index f852478..a885fda 100644
--- a/base/android/jni_android.cc
+++ b/base/android/jni_android.cc
@@ -12,6 +12,7 @@
 #include "base/android/jni_utils.h"
 #include "base/android_runtime_jni_headers/Throwable_jni.h"
 #include "base/debug/debugging_buildflags.h"
+#include "base/debug/stack_trace.h"
 #include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/strings/string_util.h"
diff --git a/base/android/jni_android.h b/base/android/jni_android.h
index 31f7924..ed3dd98 100644
--- a/base/android/jni_android.h
+++ b/base/android/jni_android.h
@@ -16,7 +16,6 @@
 #include "base/base_export.h"
 #include "base/compiler_specific.h"
 #include "base/debug/debugging_buildflags.h"
-#include "base/debug/stack_trace.h"
 #include "third_party/jni_zero/jni_zero.h"
 
 namespace base {
diff --git a/base/android/jni_string.cc b/base/android/jni_string.cc
index 06c04b2..cea0be7 100644
--- a/base/android/jni_string.cc
+++ b/base/android/jni_string.cc
@@ -4,6 +4,7 @@
 
 #include "base/android/jni_string.h"
 
+#include <array>
 #include <string_view>
 
 #include "base/android/jni_android.h"
diff --git a/base/android/pre_freeze_background_memory_trimmer.cc b/base/android/pre_freeze_background_memory_trimmer.cc
index da07a41..a7b8c6f 100644
--- a/base/android/pre_freeze_background_memory_trimmer.cc
+++ b/base/android/pre_freeze_background_memory_trimmer.cc
@@ -567,15 +567,15 @@
 
 // static
 bool PreFreezeBackgroundMemoryTrimmer::ShouldContinueCompaction(
-    const PreFreezeBackgroundMemoryTrimmer::CompactionState& state) {
-  return ShouldContinueCompaction(state.triggered_at_);
+    base::TimeTicks compaction_triggered_at) {
+  return SelfCompactionManager::ShouldContinueCompaction(
+      compaction_triggered_at);
 }
 
 // static
 bool SelfCompactionManager::ShouldContinueCompaction(
     const SelfCompactionManager::CompactionState& state) {
-  return PreFreezeBackgroundMemoryTrimmer::ShouldContinueCompaction(
-      state.triggered_at_);
+  return ShouldContinueCompaction(state.triggered_at_);
 }
 
 // static
@@ -586,7 +586,7 @@
 }
 
 // static
-bool PreFreezeBackgroundMemoryTrimmer::ShouldContinueCompaction(
+bool SelfCompactionManager::ShouldContinueCompaction(
     base::TimeTicks compaction_triggered_at) {
   base::AutoLock locker(lock());
   return Instance().compaction_last_cancelled_ < compaction_triggered_at;
@@ -614,8 +614,7 @@
                        std::move(metric)),
         GetDelayBetweenCompaction());
   } else {
-    PreFreezeBackgroundMemoryTrimmer::Instance().FinishCompaction(
-        std::move(state), std::move(metric));
+    FinishCompaction(std::move(state), std::move(metric));
   }
 }
 
@@ -657,7 +656,7 @@
   MaybePostCompactionTask(std::move(state), std::move(metric));
 }
 
-void PreFreezeBackgroundMemoryTrimmer::FinishCompaction(
+void SelfCompactionManager::FinishCompaction(
     std::unique_ptr<CompactionState> state,
     scoped_refptr<CompactionMetric> metric) {
   TRACE_EVENT0("base", "FinishCompaction");
@@ -680,7 +679,7 @@
   return base::Milliseconds(base::RandInt(100, 300));
 }
 
-void PreFreezeBackgroundMemoryTrimmer::MaybeCancelCompactionInternal(
+void SelfCompactionManager::MaybeCancelCompactionInternal(
     CompactCancellationReason cancellation_reason) {
   // Check for the last time cancelled here in order to avoid recording this
   // metric multiple times. Also, only record this metric if a compaction is
@@ -819,18 +818,17 @@
 
   auto task_runner = base::ThreadPool::CreateSequencedTaskRunner(
       {base::TaskPriority::BEST_EFFORT, MayBlock()});
-  PreFreezeBackgroundMemoryTrimmer::Instance()
-      .OnTriggerCompact<SelfCompactionState>(std::move(task_runner));
+  Instance().OnTriggerCompact<SelfCompactionState>(std::move(task_runner));
 }
 
 template <class State>
-void PreFreezeBackgroundMemoryTrimmer::OnTriggerCompact(
+void SelfCompactionManager::OnTriggerCompact(
     scoped_refptr<SequencedTaskRunner> task_runner) {
   const auto triggered_at = base::TimeTicks::Now();
   base::AutoLock locker(lock());
   compaction_last_triggered_ = triggered_at;
   auto state = std::make_unique<State>(task_runner, triggered_at);
-  SelfCompactionManager::Instance().OnTriggerCompact(std::move(state));
+  OnTriggerCompact(std::move(state));
 }
 
 void SelfCompactionManager::OnTriggerCompact(
@@ -853,8 +851,7 @@
 
   auto task_runner = base::ThreadPool::CreateSequencedTaskRunner(
       {base::TaskPriority::BEST_EFFORT, MayBlock()});
-  PreFreezeBackgroundMemoryTrimmer::Instance()
-      .OnTriggerCompact<RunningCompactionState>(std::move(task_runner));
+  Instance().OnTriggerCompact<RunningCompactionState>(std::move(task_runner));
 }
 
 // static
@@ -979,7 +976,7 @@
 }
 
 // static
-void PreFreezeBackgroundMemoryTrimmer::ResetCompactionForTesting() {
+void SelfCompactionManager::ResetCompactionForTesting() {
   base::AutoLock locker(lock());
   Instance().compaction_last_cancelled_ = base::TimeTicks::Min();
   Instance().compaction_last_finished_ = base::TimeTicks::Min();
@@ -1129,8 +1126,7 @@
     base::android::CompactCancellationReason cancellation_reason) {
   base::AutoLock locker(lock());
   Instance().process_compacted_metadata_.reset();
-  PreFreezeBackgroundMemoryTrimmer::Instance().MaybeCancelCompactionInternal(
-      cancellation_reason);
+  Instance().MaybeCancelCompactionInternal(cancellation_reason);
 }
 
 std::unique_ptr<SelfCompactionManager::CompactionState>
diff --git a/base/android/pre_freeze_background_memory_trimmer.h b/base/android/pre_freeze_background_memory_trimmer.h
index dd9e454..d95a687 100644
--- a/base/android/pre_freeze_background_memory_trimmer.h
+++ b/base/android/pre_freeze_background_memory_trimmer.h
@@ -126,7 +126,6 @@
   bool DidRegisterTasksForTesting() const;
 
   static void OnPreFreezeForTesting() LOCKS_EXCLUDED(lock()) { OnPreFreeze(); }
-  static void ResetCompactionForTesting();
 
   // Called when Chrome is about to be frozen. Runs as many delayed tasks as
   // possible immediately, before we are frozen.
@@ -273,16 +272,6 @@
 
   static base::Lock& lock() { return Instance().lock_; }
 
-  template <class State>
-  void OnTriggerCompact(scoped_refptr<SequencedTaskRunner> task_runner);
-
-  void FinishCompaction(std::unique_ptr<CompactionState> state,
-                        scoped_refptr<CompactionMetric> metric)
-      LOCKS_EXCLUDED(lock());
-
-  static bool ShouldContinueCompaction(
-      const PreFreezeBackgroundMemoryTrimmer::CompactionState& state)
-      LOCKS_EXCLUDED(lock());
   static bool ShouldContinueCompaction(base::TimeTicks compaction_triggered_at)
       LOCKS_EXCLUDED(lock());
 
@@ -317,10 +306,6 @@
   void OnPreFreezeInternal() LOCKS_EXCLUDED(lock());
   void RunPreFreezeTasks() EXCLUSIVE_LOCKS_REQUIRED(lock());
 
-  void MaybeCancelCompactionInternal(
-      CompactCancellationReason cancellation_reason)
-      EXCLUSIVE_LOCKS_REQUIRED(lock());
-
   void PostMetricsTasksIfModern() EXCLUSIVE_LOCKS_REQUIRED(lock());
   void PostMetricsTask() EXCLUSIVE_LOCKS_REQUIRED(lock());
   void RecordMetrics() LOCKS_EXCLUDED(lock());
@@ -334,24 +319,6 @@
   // to the "i"th entry in |metrics_|. When there is no pending metrics task,
   // |values_before_| should be empty.
   std::vector<std::optional<uint64_t>> values_before_ GUARDED_BY(lock());
-  // Whether or not we should continue self compaction. There are two reasons
-  // why we would cancel:
-  // (1) We have resumed, meaning we are likely to touch much of the process
-  //     memory soon, and we do not want to waste CPU time with compaction,
-  //     since it can block other work that needs to be done.
-  // (2) We are going to be frozen by App Freezer, which will do the compaction
-  //     work for us. This situation should be relatively rare, because we
-  //     attempt to not do self compaction if we know that we are going to
-  //     frozen by App Freezer.
-  base::TimeTicks compaction_last_cancelled_ GUARDED_BY(lock()) =
-      base::TimeTicks::Min();
-  // When we last triggered self compaction. Used to record metrics.
-  base::TimeTicks compaction_last_triggered_ GUARDED_BY(lock()) =
-      base::TimeTicks::Min();
-  // When we last finished self compaction (either successfully, or from
-  // being cancelled). Used to record metrics.
-  base::TimeTicks compaction_last_finished_ GUARDED_BY(lock()) =
-      base::TimeTicks::Min();
   bool supports_modern_trim_;
 };
 
@@ -394,14 +361,23 @@
   }
 
   static bool CompactionIsSupported();
-  static bool ShouldContinueCompaction(
-      const PreFreezeBackgroundMemoryTrimmer::CompactionState& state)
-      LOCKS_EXCLUDED(lock());
   static bool TimeoutExceeded();
   static base::TimeDelta GetDelayBetweenCompaction();
 
+  static bool ShouldContinueCompaction(
+      const PreFreezeBackgroundMemoryTrimmer::CompactionState& state)
+      LOCKS_EXCLUDED(lock());
+  static bool ShouldContinueCompaction(base::TimeTicks compaction_triggered_at)
+      LOCKS_EXCLUDED(lock());
+
+  void MaybeCancelCompactionInternal(
+      CompactCancellationReason cancellation_reason)
+      EXCLUSIVE_LOCKS_REQUIRED(lock());
+
   // Compacts the memory for the process.
   static void CompactSelf(std::unique_ptr<CompactionState> state);
+  template <class State>
+  void OnTriggerCompact(scoped_refptr<SequencedTaskRunner> task_runner);
   void OnTriggerCompact(std::unique_ptr<CompactionState> state)
       EXCLUSIVE_LOCKS_REQUIRED(lock());
   void StartCompaction(std::unique_ptr<CompactionState> state)
@@ -412,6 +388,9 @@
   void CompactionTask(std::unique_ptr<CompactionState> state,
                       scoped_refptr<CompactionMetric> metric)
       LOCKS_EXCLUDED(lock());
+  void FinishCompaction(std::unique_ptr<CompactionState> state,
+                        scoped_refptr<CompactionMetric> metric)
+      LOCKS_EXCLUDED(lock());
   static std::optional<uint64_t> CompactMemory(
       std::vector<debug::MappedMemoryRegion>* regions,
       const uint64_t max_bytes);
@@ -420,6 +399,7 @@
 
   void MaybeRunOnSelfCompactCallback() EXCLUSIVE_LOCKS_REQUIRED(lock());
 
+  static void ResetCompactionForTesting();
   static std::unique_ptr<CompactionState> GetSelfCompactionStateForTesting(
       scoped_refptr<SequencedTaskRunner> task_runner,
       const TimeTicks& triggered_at);
@@ -427,10 +407,28 @@
       scoped_refptr<SequencedTaskRunner> task_runner,
       const TimeTicks& triggered_at);
 
+  // Whether or not we should continue self compaction. There are two reasons
+  // why we would cancel:
+  // (1) We have resumed, meaning we are likely to touch much of the process
+  //     memory soon, and we do not want to waste CPU time with compaction,
+  //     since it can block other work that needs to be done.
+  // (2) We are going to be frozen by App Freezer, which will do the compaction
+  //     work for us. This situation should be relatively rare, because we
+  //     attempt to not do self compaction if we know that we are going to
+  //     frozen by App Freezer.
+  base::TimeTicks compaction_last_cancelled_ GUARDED_BY(lock()) =
+      base::TimeTicks::Min();
+  // When we last triggered self compaction. Used to record metrics.
+  base::TimeTicks compaction_last_triggered_ GUARDED_BY(lock()) =
+      base::TimeTicks::Min();
   // When we last started self compaction. Used to know if we should cancel
   // compaction due to it taking too long.
   base::TimeTicks compaction_last_started_ GUARDED_BY(lock()) =
       base::TimeTicks::Min();
+  // When we last finished self compaction (either successfully, or from
+  // being cancelled). Used to record metrics.
+  base::TimeTicks compaction_last_finished_ GUARDED_BY(lock()) =
+      base::TimeTicks::Min();
   base::RepeatingClosure on_self_compact_callback_ GUARDED_BY(lock());
   std::optional<base::ScopedSampleMetadata> process_compacted_metadata_
       GUARDED_BY(lock());
diff --git a/base/android/pre_freeze_background_memory_trimmer_unittest.cc b/base/android/pre_freeze_background_memory_trimmer_unittest.cc
index 37bdf7b..f2e08cb 100644
--- a/base/android/pre_freeze_background_memory_trimmer_unittest.cc
+++ b/base/android/pre_freeze_background_memory_trimmer_unittest.cc
@@ -133,9 +133,7 @@
 
 class PreFreezeSelfCompactionTest : public testing::Test {
  public:
-  void SetUp() override {
-    PreFreezeBackgroundMemoryTrimmer::ResetCompactionForTesting();
-  }
+  void SetUp() override { SelfCompactionManager::ResetCompactionForTesting(); }
 
   bool ShouldContinueCompaction(base::TimeTicks compaction_started_at) {
     return PreFreezeBackgroundMemoryTrimmer::Instance()
@@ -926,8 +924,7 @@
 
   {
     base::AutoLock locker(PreFreezeBackgroundMemoryTrimmer::lock());
-    PreFreezeBackgroundMemoryTrimmer::Instance().compaction_last_triggered_ =
-        triggered_at;
+    SelfCompactionManager::Instance().compaction_last_triggered_ = triggered_at;
   }
   SelfCompactionManager::Instance().StartCompaction(std::move(state));
 
@@ -997,8 +994,7 @@
 
   {
     base::AutoLock locker(PreFreezeBackgroundMemoryTrimmer::lock());
-    PreFreezeBackgroundMemoryTrimmer::Instance().compaction_last_triggered_ =
-        triggered_at;
+    SelfCompactionManager::Instance().compaction_last_triggered_ = triggered_at;
   }
   SelfCompactionManager::Instance().StartCompaction(std::move(state));
 
diff --git a/base/android/shared_preferences/shared_preferences_manager.h b/base/android/shared_preferences/shared_preferences_manager.h
index d4aad52..eb6dc7b 100644
--- a/base/android/shared_preferences/shared_preferences_manager.h
+++ b/base/android/shared_preferences/shared_preferences_manager.h
@@ -7,6 +7,7 @@
 
 #include "base/android/jni_android.h"
 #include "base/base_export.h"
+#include "base/memory/raw_ptr.h"
 
 namespace base::android {
 
diff --git a/base/process/launch_posix.cc b/base/process/launch_posix.cc
index 06307dd99..6433a7e 100644
--- a/base/process/launch_posix.cc
+++ b/base/process/launch_posix.cc
@@ -29,7 +29,6 @@
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/debug/debugger.h"
-#include "base/debug/stack_trace.h"
 #include "base/files/dir_reader_posix.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_file.h"
diff --git a/base/process/launch_win.cc b/base/process/launch_win.cc
index 9b03f31..3399c0e8 100644
--- a/base/process/launch_win.cc
+++ b/base/process/launch_win.cc
@@ -23,7 +23,6 @@
 #include "base/containers/heap_array.h"
 #include "base/containers/span.h"
 #include "base/debug/alias.h"
-#include "base/debug/stack_trace.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
 #include "base/functional/function_ref.h"
diff --git a/base/profiler/module_cache.cc b/base/profiler/module_cache.cc
index 46f0f19..31d4753 100644
--- a/base/profiler/module_cache.cc
+++ b/base/profiler/module_cache.cc
@@ -90,6 +90,7 @@
   for (const std::unique_ptr<const Module>& module : native_modules_) {
     result.push_back(module.get());
   }
+  base::AutoLock locker(lock_);
   for (const std::unique_ptr<const Module>& module : non_native_modules_) {
     result.push_back(module.get());
   }
@@ -103,6 +104,7 @@
   flat_set<const Module*> defunct_modules_set(defunct_modules.begin(),
                                               defunct_modules.end());
 
+  base::AutoLock locker(lock_);
   // Reorder the modules to be removed to the last slots in the set, then move
   // them to the inactive modules, then erase the moved-from modules from the
   // set. This is a variation on the standard erase-remove idiom, which is
@@ -153,9 +155,12 @@
 
 const ModuleCache::Module* ModuleCache::GetExistingModuleForAddress(
     uintptr_t address) const {
-  const auto non_native_module_loc = non_native_modules_.find(address);
-  if (non_native_module_loc != non_native_modules_.end()) {
-    return non_native_module_loc->get();
+  {
+    base::AutoLock locker(lock_);
+    const auto non_native_module_loc = non_native_modules_.find(address);
+    if (non_native_module_loc != non_native_modules_.end()) {
+      return non_native_module_loc->get();
+    }
   }
 
   const auto native_module_loc = native_modules_.find(address);
diff --git a/base/profiler/module_cache.h b/base/profiler/module_cache.h
index 0a3ca02..14de5781 100644
--- a/base/profiler/module_cache.h
+++ b/base/profiler/module_cache.h
@@ -15,6 +15,8 @@
 #include "base/containers/flat_set.h"
 #include "base/files/file_path.h"
 #include "base/memory/raw_ptr.h"
+#include "base/synchronization/lock.h"
+#include "base/thread_annotations.h"
 #include "build/build_config.h"
 
 #if BUILDFLAG(IS_WIN)
@@ -94,8 +96,8 @@
   // Gets the module containing |address| or nullptr if |address| is not within
   // a module. The returned module remains owned by and has the same lifetime as
   // the ModuleCache object.
-  const Module* GetModuleForAddress(uintptr_t address);
-  std::vector<const Module*> GetModules() const;
+  const Module* GetModuleForAddress(uintptr_t address) LOCKS_EXCLUDED(lock_);
+  std::vector<const Module*> GetModules() const LOCKS_EXCLUDED(lock_);
 
   // Updates the set of non-native modules maintained by the
   // ModuleCache. Non-native modules represent regions of non-native executable
@@ -116,7 +118,8 @@
   // the same call.
   void UpdateNonNativeModules(
       const std::vector<const Module*>& defunct_modules,
-      std::vector<std::unique_ptr<const Module>> new_modules);
+      std::vector<std::unique_ptr<const Module>> new_modules)
+      LOCKS_EXCLUDED(lock_);
 
   // Adds a custom native module to the cache. This is intended to support
   // native modules that require custom handling. In general, native modules
@@ -144,7 +147,8 @@
   // NOTE: Only users that create their own modules and need control over native
   // module creation should use this function. Everyone else should use
   // GetModuleForAddress().
-  const Module* GetExistingModuleForAddress(uintptr_t address) const;
+  const Module* GetExistingModuleForAddress(uintptr_t address) const
+      LOCKS_EXCLUDED(lock_);
 
  private:
   // Heterogenously compares modules by base address, and modules and
@@ -173,6 +177,9 @@
   std::set<std::unique_ptr<const Module>, ModuleAndAddressCompare>
       native_modules_;
 
+  // Lock to guard |non_native_modules_|.
+  mutable base::Lock lock_;
+
   // Set of non-native modules currently mapped into the address space, sorted
   // by base address. Represented as flat_set because std::set does not support
   // extracting move-only element types prior to C++17's
@@ -182,7 +189,7 @@
   // native_modules_ to support preferential lookup of non-native modules
   // embedded in native modules; see comment on UpdateNonNativeModules().
   base::flat_set<std::unique_ptr<const Module>, ModuleAndAddressCompare>
-      non_native_modules_;
+      non_native_modules_ GUARDED_BY(lock_);
 
   // Unsorted vector of inactive non-native modules. Inactive modules are no
   // longer mapped in the address space and don't participate in address lookup,
diff --git a/base/system/sys_info_mac.mm b/base/system/sys_info_mac.mm
index f783fc6a..a00b8d7c 100644
--- a/base/system/sys_info_mac.mm
+++ b/base/system/sys_info_mac.mm
@@ -17,7 +17,6 @@
 
 #include "base/apple/scoped_mach_port.h"
 #include "base/check_op.h"
-#include "base/debug/stack_trace.h"
 #include "base/feature_list.h"
 #include "base/mac/mac_util.h"
 #include "base/no_destructor.h"
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
index 5d032fb..6f9561cc 100644
--- a/base/trace_event/memory_dump_manager.cc
+++ b/base/trace_event/memory_dump_manager.cc
@@ -16,7 +16,6 @@
 #include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/debug/alias.h"
-#include "base/debug/stack_trace.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/span_printf.h"
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
index 5f22f8d..c9f89a80 100644
--- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py
+++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -133,7 +133,7 @@
 # A map of Android product models to SDK ints.
 RENDER_TEST_MODEL_SDK_CONFIGS = {
     # Android x86 emulator.
-    'Android SDK built for x86': [26],
+    'Android SDK built for x86': [26, 29],
     # We would like this to be supported, but it is currently too prone to
     # introducing flakiness due to a combination of Gold and Chromium issues.
     # See crbug.com/1233700 and skbug.com/12149 for more information.
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc
index edebb63..76179044 100644
--- a/cc/layers/heads_up_display_layer_impl.cc
+++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -25,7 +25,7 @@
 #include "base/trace_event/traced_value.h"
 #include "build/build_config.h"
 #include "cc/debug/debug_colors.h"
-#include "cc/metrics/dropped_frame_counter.h"
+#include "cc/metrics/frame_sorter.h"
 #include "cc/paint/display_item_list.h"
 #include "cc/paint/image_provider.h"
 #include "cc/paint/paint_canvas.h"
@@ -470,7 +470,7 @@
 
     if (debug_state.show_fps_counter) {
       throughput_value_ =
-          layer_tree_impl()->dropped_frame_counter()->GetAverageThroughput();
+          layer_tree_impl()->frame_sorter()->GetAverageThroughput();
       const auto& args = layer_tree_impl()->CurrentBeginFrameArgs();
       if (args.IsValid())
         frame_interval_ = args.interval;
@@ -520,8 +520,7 @@
   SkRect area = SkRect::MakeXYWH(0, 0, 0, 0);
 
   if (debug_state.show_fps_counter) {
-    area = DrawFrameThroughputDisplay(
-        canvas, layer_tree_impl()->dropped_frame_counter(), 0, 0);
+    area = DrawFrameThroughputDisplay(canvas, 0, 0);
     area = DrawGpuRasterizationStatus(canvas, 0, area.bottom(),
                                       std::max<SkScalar>(area.width(), 150));
   }
@@ -635,9 +634,9 @@
 
 SkRect HeadsUpDisplayLayerImpl::DrawFrameThroughputDisplay(
     PaintCanvas* canvas,
-    const DroppedFrameCounter* dropped_frame_counter,
     int right,
     int top) const {
+  FrameSorter* frame_sorter = layer_tree_impl()->frame_sorter();
   const int kPadding = 4;
   const int kGap = 6;
 
@@ -645,7 +644,7 @@
   const int kFontHeight = 12;
 
   const int kGraphWidth =
-      base::saturated_cast<int>(dropped_frame_counter->frame_history_size());
+      base::saturated_cast<int>(frame_sorter->frame_history_size());
   const int kGraphHeight = 40;
 
   int width = kGraphWidth + 4 * kPadding;
@@ -692,14 +691,13 @@
   SkPath good_path;
   SkPath dropped_path;
   SkPath partial_path;
-  for (auto it = dropped_frame_counter->End(); it; --it) {
+  for (auto it = frame_sorter->End(); it; --it) {
     const auto state = **it;
     int x = graph_bounds.left() + it.index();
-    SkPath& path = state == DroppedFrameCounter::kFrameStateDropped
-                       ? dropped_path
-                       : state == DroppedFrameCounter::kFrameStateComplete
-                             ? good_path
-                             : partial_path;
+    SkPath& path = state == FrameInfo::FrameFinalState::kDropped ? dropped_path
+                   : state == FrameInfo::FrameFinalState::kPresentedAll
+                       ? good_path
+                       : partial_path;
     path.moveTo(x, graph_bounds.top());
     path.lineTo(x, graph_bounds.bottom());
   }
diff --git a/cc/layers/heads_up_display_layer_impl.h b/cc/layers/heads_up_display_layer_impl.h
index 198473e..3429d0a 100644
--- a/cc/layers/heads_up_display_layer_impl.h
+++ b/cc/layers/heads_up_display_layer_impl.h
@@ -29,7 +29,6 @@
 }
 
 namespace cc {
-class DroppedFrameCounter;
 class LayerTreeFrameSink;
 class PaintCanvas;
 class PaintFlags;
@@ -119,11 +118,9 @@
                          PaintFlags* flags,
                          const SkRect& bounds) const;
 
-  SkRect DrawFrameThroughputDisplay(
-      PaintCanvas* canvas,
-      const DroppedFrameCounter* dropped_frame_counter,
-      int right,
-      int top) const;
+  SkRect DrawFrameThroughputDisplay(PaintCanvas* canvas,
+                                    int right,
+                                    int top) const;
   SkRect DrawMemoryDisplay(PaintCanvas* canvas,
                            int top,
                            int right,
diff --git a/cc/metrics/compositor_frame_reporter.cc b/cc/metrics/compositor_frame_reporter.cc
index b532870..fa3aec4e 100644
--- a/cc/metrics/compositor_frame_reporter.cc
+++ b/cc/metrics/compositor_frame_reporter.cc
@@ -994,11 +994,13 @@
   if (TestReportType(FrameReportType::kDroppedFrame)) {
     global_trackers_.dropped_frame_counter->AddDroppedFrame();
   } else {
-    if (has_partial_update_)
+    if (has_partial_update_) {
       global_trackers_.dropped_frame_counter->AddPartialFrame();
-    else
+    } else {
       global_trackers_.dropped_frame_counter->AddGoodFrame();
+    }
   }
+  global_trackers_.frame_sorter->AddFrameInfoToBuffer(frame_info);
   if (global_trackers_.dropped_frame_counter
           ->first_contentful_paint_received()) {
     // Delegates call to DFC->OnEndFrame.
diff --git a/cc/metrics/compositor_frame_reporting_controller_unittest.cc b/cc/metrics/compositor_frame_reporting_controller_unittest.cc
index ff1e906..c80aa3f 100644
--- a/cc/metrics/compositor_frame_reporting_controller_unittest.cc
+++ b/cc/metrics/compositor_frame_reporting_controller_unittest.cc
@@ -1191,8 +1191,8 @@
   reporting_controller_.WillBeginImplFrame(args_);
   reporting_controller_.OnFinishImplFrame(current_id_);
 
-  EXPECT_EQ(3u, dropped_counter_.total_frames());
-  EXPECT_EQ(1u, dropped_counter_.total_dropped());
+  EXPECT_EQ(3u, frame_sorter_.total_frames());
+  EXPECT_EQ(1u, frame_sorter_.total_dropped());
 }
 
 // Testing CompositorLatency.Type metrics
@@ -1671,8 +1671,8 @@
   // In total, two frames have been completed: R1C, and R1M.
   // R2C has been presented, but it is blocked on R2M to know whether R2C
   // contains partial update, or complete updates. So it is kept alive.
-  EXPECT_EQ(2u, dropped_counter_.total_frames());
-  EXPECT_EQ(1u, dropped_counter_.total_partial());
+  EXPECT_EQ(2u, frame_sorter_.total_frames());
+  EXPECT_EQ(1u, frame_sorter_.total_partial());
   EXPECT_EQ(1u, reporting_controller_.GetBlockingReportersCount());
   EXPECT_EQ(1u, reporting_controller_.GetBlockedReportersCount());
 
@@ -1710,8 +1710,8 @@
   EXPECT_EQ(1u, reporting_controller_.GetAdoptedReportersCount());
 
   // At this point no frame has been completed, yet.
-  EXPECT_EQ(0u, dropped_counter_.total_frames());
-  EXPECT_EQ(0u, dropped_counter_.total_dropped());
+  EXPECT_EQ(0u, frame_sorter_.total_frames());
+  EXPECT_EQ(0u, frame_sorter_.total_dropped());
 
   // Start yet another frame that has impl-thread update and submit it, but with
   // failed presentation. The reporter for this frame should become dependent of
@@ -1735,8 +1735,8 @@
   EXPECT_EQ(1u, reporting_controller_.GetAdoptedReportersCount());
 
   // At this point 1 frame has been completed and it's a dropped frame.
-  EXPECT_EQ(1u, dropped_counter_.total_frames());
-  EXPECT_EQ(1u, dropped_counter_.total_dropped());
+  EXPECT_EQ(1u, frame_sorter_.total_frames());
+  EXPECT_EQ(1u, frame_sorter_.total_dropped());
 
   reporting_controller_.ResetReporters();
   reporting_controller_.ClearFrameSequenceTrackerCollection();
@@ -1748,34 +1748,35 @@
        SkippedFramesFromDisplayCompositorAreDropped) {
   // Submit and present two compositor frames.
   SimulatePresentCompositorFrame();
-  EXPECT_EQ(1u, dropped_counter_.total_frames());
-  EXPECT_EQ(0u, dropped_counter_.total_partial());
-  EXPECT_EQ(0u, dropped_counter_.total_dropped());
+  EXPECT_EQ(1u, frame_sorter_.total_frames());
+  EXPECT_EQ(0u, frame_sorter_.total_partial());
+  EXPECT_EQ(0u, frame_sorter_.total_dropped());
 
   SimulatePresentCompositorFrame();
-  EXPECT_EQ(2u, dropped_counter_.total_frames());
-  EXPECT_EQ(0u, dropped_counter_.total_partial());
-  EXPECT_EQ(0u, dropped_counter_.total_dropped());
+  EXPECT_EQ(2u, frame_sorter_.total_frames());
+  EXPECT_EQ(0u, frame_sorter_.total_partial());
+  EXPECT_EQ(0u, frame_sorter_.total_dropped());
 
   // Now skip over a few frames, and submit + present another frame.
   const uint32_t kSkipFrames = 5;
   for (uint32_t i = 0; i < kSkipFrames; ++i)
     IncrementCurrentId();
   SimulatePresentCompositorFrame();
-  EXPECT_EQ(3u + kSkipFrames, dropped_counter_.total_frames());
-  EXPECT_EQ(0u, dropped_counter_.total_partial());
-  EXPECT_EQ(kSkipFrames, dropped_counter_.total_dropped());
+  EXPECT_EQ(3u + kSkipFrames, frame_sorter_.total_frames());
+  EXPECT_EQ(0u, frame_sorter_.total_partial());
+  EXPECT_EQ(kSkipFrames, frame_sorter_.total_dropped());
 
   // Stop requesting frames, skip over a few frames, and submit + present
   // another frame. There should no new dropped frames.
   dropped_counter_.Reset();
+  frame_sorter_.Reset();
   reporting_controller_.OnStoppedRequestingBeginFrames();
   for (uint32_t i = 0; i < kSkipFrames; ++i)
     IncrementCurrentId();
   SimulatePresentCompositorFrame();
-  EXPECT_EQ(1u, dropped_counter_.total_frames());
-  EXPECT_EQ(0u, dropped_counter_.total_partial());
-  EXPECT_EQ(0u, dropped_counter_.total_dropped());
+  EXPECT_EQ(1u, frame_sorter_.total_frames());
+  EXPECT_EQ(0u, frame_sorter_.total_partial());
+  EXPECT_EQ(0u, frame_sorter_.total_dropped());
 
   reporting_controller_.ResetReporters();
   reporting_controller_.ClearFrameSequenceTrackerCollection();
@@ -1787,14 +1788,14 @@
        SkippedFramesFromDisplayCompositorAreDroppedUpToLimit) {
   // Submit and present two compositor frames.
   SimulatePresentCompositorFrame();
-  EXPECT_EQ(1u, dropped_counter_.total_frames());
-  EXPECT_EQ(0u, dropped_counter_.total_partial());
-  EXPECT_EQ(0u, dropped_counter_.total_dropped());
+  EXPECT_EQ(1u, frame_sorter_.total_frames());
+  EXPECT_EQ(0u, frame_sorter_.total_partial());
+  EXPECT_EQ(0u, frame_sorter_.total_dropped());
 
   SimulatePresentCompositorFrame();
-  EXPECT_EQ(2u, dropped_counter_.total_frames());
-  EXPECT_EQ(0u, dropped_counter_.total_partial());
-  EXPECT_EQ(0u, dropped_counter_.total_dropped());
+  EXPECT_EQ(2u, frame_sorter_.total_frames());
+  EXPECT_EQ(0u, frame_sorter_.total_partial());
+  EXPECT_EQ(0u, frame_sorter_.total_dropped());
 
   // Now skip over a 101 frames (It should be ignored as it more than 100)
   // and submit + present another frame.
@@ -1803,9 +1804,9 @@
   for (uint32_t i = 0; i < kSkipFrames; ++i)
     IncrementCurrentId();
   SimulatePresentCompositorFrame();
-  EXPECT_EQ(3u + kSkipFramesActual, dropped_counter_.total_frames());
-  EXPECT_EQ(0u, dropped_counter_.total_partial());
-  EXPECT_EQ(kSkipFramesActual, dropped_counter_.total_dropped());
+  EXPECT_EQ(3u + kSkipFramesActual, frame_sorter_.total_frames());
+  EXPECT_EQ(0u, frame_sorter_.total_partial());
+  EXPECT_EQ(kSkipFramesActual, frame_sorter_.total_dropped());
 }
 
 TEST_F(CompositorFrameReportingControllerTest,
@@ -1825,7 +1826,7 @@
   reporting_controller_.WillBeginImplFrame(args_1);
   reporting_controller_.WillBeginMainFrame(args_1);
   reporting_controller_.OnFinishImplFrame(current_id_1);
-  EXPECT_EQ(0u, dropped_counter_.total_dropped());
+  EXPECT_EQ(0u, frame_sorter_.total_dropped());
   reporting_controller_.DidNotProduceFrame(args_1.frame_id,
                                            FrameSkippedReason::kWaitingOnMain);
 
@@ -1843,23 +1844,23 @@
   EXPECT_EQ(3u, reporting_controller_.GetBlockedReportersCount());
 
   // All frames are waiting for the main frame
-  EXPECT_EQ(0u, dropped_counter_.total_partial());
-  EXPECT_EQ(0u, dropped_counter_.total_dropped());
-  EXPECT_EQ(0u, dropped_counter_.total_frames());
+  EXPECT_EQ(0u, frame_sorter_.total_partial());
+  EXPECT_EQ(0u, frame_sorter_.total_dropped());
+  EXPECT_EQ(0u, frame_sorter_.total_frames());
 
   reporting_controller_.BeginMainFrameAborted(
       args_1.frame_id, CommitEarlyOutReason::kFinishedNoUpdates);
   reporting_controller_.DidNotProduceFrame(args_1.frame_id,
                                            FrameSkippedReason::kNoDamage);
-  EXPECT_EQ(0u, dropped_counter_.total_dropped());
+  EXPECT_EQ(0u, frame_sorter_.total_dropped());
 
   // New reporters replace older reporters
   reporting_controller_.WillBeginImplFrame(args_4);
   reporting_controller_.WillBeginMainFrame(args_4);
 
-  EXPECT_EQ(4u, dropped_counter_.total_frames());
-  EXPECT_EQ(0u, dropped_counter_.total_partial());
-  EXPECT_EQ(0u, dropped_counter_.total_dropped());
+  EXPECT_EQ(4u, frame_sorter_.total_frames());
+  EXPECT_EQ(0u, frame_sorter_.total_partial());
+  EXPECT_EQ(0u, frame_sorter_.total_dropped());
 }
 
 TEST_F(CompositorFrameReportingControllerTest,
@@ -1872,23 +1873,23 @@
 
   // Submit and present two compositor frames.
   SimulatePresentCompositorFrame();
-  EXPECT_EQ(1u, dropped_counter_.total_frames());
-  EXPECT_EQ(0u, dropped_counter_.total_partial());
-  EXPECT_EQ(0u, dropped_counter_.total_dropped());
+  EXPECT_EQ(1u, frame_sorter_.total_frames());
+  EXPECT_EQ(0u, frame_sorter_.total_partial());
+  EXPECT_EQ(0u, frame_sorter_.total_dropped());
 
   SimulatePresentCompositorFrame();
-  EXPECT_EQ(2u, dropped_counter_.total_frames());
-  EXPECT_EQ(0u, dropped_counter_.total_partial());
-  EXPECT_EQ(0u, dropped_counter_.total_dropped());
+  EXPECT_EQ(2u, frame_sorter_.total_frames());
+  EXPECT_EQ(0u, frame_sorter_.total_partial());
+  EXPECT_EQ(0u, frame_sorter_.total_dropped());
 
   // Now skip over a few frames, and submit + present another frame.
   const uint32_t kSkipFrames_1 = 5;
   for (uint32_t i = 0; i < kSkipFrames_1; ++i)
     IncrementCurrentId();
   SimulatePresentCompositorFrame();
-  EXPECT_EQ(3u + kSkipFrames_1, dropped_counter_.total_frames());
-  EXPECT_EQ(0u, dropped_counter_.total_partial());
-  EXPECT_EQ(kSkipFrames_1, dropped_counter_.total_dropped());
+  EXPECT_EQ(3u + kSkipFrames_1, frame_sorter_.total_frames());
+  EXPECT_EQ(0u, frame_sorter_.total_partial());
+  EXPECT_EQ(kSkipFrames_1, frame_sorter_.total_dropped());
   EXPECT_EQ(kSkipFrames_1, dropped_counter_.total_smoothness_dropped());
 
   // Now skip over a few frames which are not affecting smoothness.
@@ -1900,10 +1901,9 @@
   for (uint32_t i = 0; i < kSkipFrames_2; ++i)
     IncrementCurrentId();
   SimulatePresentCompositorFrame();  // Present another frame.
-  EXPECT_EQ(4u + kSkipFrames_1 + kSkipFrames_2,
-            dropped_counter_.total_frames());
-  EXPECT_EQ(0u, dropped_counter_.total_partial());
-  EXPECT_EQ(kSkipFrames_1 + kSkipFrames_2, dropped_counter_.total_dropped());
+  EXPECT_EQ(4u + kSkipFrames_1 + kSkipFrames_2, frame_sorter_.total_frames());
+  EXPECT_EQ(0u, frame_sorter_.total_partial());
+  EXPECT_EQ(kSkipFrames_1 + kSkipFrames_2, frame_sorter_.total_dropped());
   EXPECT_EQ(kSkipFrames_1, dropped_counter_.total_smoothness_dropped());
 
   // Now skip over a few frames more frames which are affecting smoothness.
@@ -1915,10 +1915,10 @@
     IncrementCurrentId();
   SimulatePresentCompositorFrame();  // Present another frame.
   EXPECT_EQ(5u + kSkipFrames_1 + kSkipFrames_2 + kSkipFrames_3,
-            dropped_counter_.total_frames());
-  EXPECT_EQ(0u, dropped_counter_.total_partial());
+            frame_sorter_.total_frames());
+  EXPECT_EQ(0u, frame_sorter_.total_partial());
   EXPECT_EQ(kSkipFrames_1 + kSkipFrames_2 + kSkipFrames_3,
-            dropped_counter_.total_dropped());
+            frame_sorter_.total_dropped());
   EXPECT_EQ(kSkipFrames_1 + kSkipFrames_3,
             dropped_counter_.total_smoothness_dropped());
 }
@@ -1927,14 +1927,14 @@
        SkippedFramesFromClientRequestedThrottlingAreDropped) {
   // Submit and present two compositor frames.
   SimulatePresentCompositorFrame();
-  EXPECT_EQ(1u, dropped_counter_.total_frames());
-  EXPECT_EQ(0u, dropped_counter_.total_partial());
-  EXPECT_EQ(0u, dropped_counter_.total_dropped());
+  EXPECT_EQ(1u, frame_sorter_.total_frames());
+  EXPECT_EQ(0u, frame_sorter_.total_partial());
+  EXPECT_EQ(0u, frame_sorter_.total_dropped());
 
   SimulatePresentCompositorFrame();
-  EXPECT_EQ(2u, dropped_counter_.total_frames());
-  EXPECT_EQ(0u, dropped_counter_.total_partial());
-  EXPECT_EQ(0u, dropped_counter_.total_dropped());
+  EXPECT_EQ(2u, frame_sorter_.total_frames());
+  EXPECT_EQ(0u, frame_sorter_.total_partial());
+  EXPECT_EQ(0u, frame_sorter_.total_dropped());
 
   // Now skip over a few frames, and submit + present another frame.
   const uint32_t kTotalFrames = 5;
@@ -1943,10 +1943,9 @@
     IncrementCurrentId();
   args_.frames_throttled_since_last = kThrottledFrames;
   SimulatePresentCompositorFrame();
-  EXPECT_EQ(3u + kTotalFrames - kThrottledFrames,
-            dropped_counter_.total_frames());
-  EXPECT_EQ(0u, dropped_counter_.total_partial());
-  EXPECT_EQ(kTotalFrames - kThrottledFrames, dropped_counter_.total_dropped());
+  EXPECT_EQ(3u + kTotalFrames - kThrottledFrames, frame_sorter_.total_frames());
+  EXPECT_EQ(0u, frame_sorter_.total_partial());
+  EXPECT_EQ(kTotalFrames - kThrottledFrames, frame_sorter_.total_dropped());
 }
 
 TEST_F(CompositorFrameReportingControllerTest,
@@ -1959,7 +1958,7 @@
     reporting_controller_.BeginMainFrameAborted(
         current_id_, CommitEarlyOutReason::kFinishedNoUpdates);
   }
-  EXPECT_EQ(0u, dropped_counter_.total_dropped());
+  EXPECT_EQ(0u, frame_sorter_.total_dropped());
 
   // Start a few begin-main-frames, but abort the main-frames due to no damage.
   for (int i = 0; i < 5; ++i) {
@@ -1971,7 +1970,7 @@
     SimulateSubmitCompositorFrame({});
   }
   SimulatePresentCompositorFrame();
-  EXPECT_EQ(5u, dropped_counter_.total_dropped());
+  EXPECT_EQ(5u, frame_sorter_.total_dropped());
 }
 
 // Verifies that presentation feedbacks that arrive out of order are handled
@@ -1994,16 +1993,16 @@
   details_2.presentation_feedback = {AdvanceNowByMs(10), base::TimeDelta(),
                                      gfx::PresentationFeedback::kFailure};
   reporting_controller_.DidPresentCompositorFrame(frame_token_2, details_2);
-  DCHECK_EQ(1u, dropped_counter_.total_frames());
-  DCHECK_EQ(1u, dropped_counter_.total_dropped());
+  DCHECK_EQ(1u, frame_sorter_.total_frames());
+  DCHECK_EQ(1u, frame_sorter_.total_dropped());
 
   // Send a successful presentation feedback for frame 3. This should drop frame
   // 1.
   viz::FrameTimingDetails details_3;
   details_3.presentation_feedback.timestamp = AdvanceNowByMs(10);
   reporting_controller_.DidPresentCompositorFrame(frame_token_3, details_3);
-  DCHECK_EQ(3u, dropped_counter_.total_frames());
-  DCHECK_EQ(2u, dropped_counter_.total_dropped());
+  DCHECK_EQ(3u, frame_sorter_.total_frames());
+  DCHECK_EQ(2u, frame_sorter_.total_dropped());
 }
 
 TEST_F(CompositorFrameReportingControllerTest,
@@ -2049,7 +2048,7 @@
   SimulatePresentCompositorFrame();
 
   // There are two frames with partial updates
-  EXPECT_EQ(2u, dropped_counter_.total_partial());
+  EXPECT_EQ(2u, frame_sorter_.total_partial());
   // Which one is accompanied with new main thread update so only one affects
   // smoothness
   EXPECT_EQ(1u, dropped_counter_.total_smoothness_dropped());
@@ -2065,28 +2064,27 @@
   // thread (i.e. 'begin main frame').
   SimulateBeginMainFrame();
   EXPECT_EQ(1, reporting_controller_.ActiveReporters());
-  EXPECT_EQ(0u, dropped_counter_.total_frames());
+  EXPECT_EQ(0u, frame_sorter_.total_frames());
 
   // Terminate the frame without submitting a frame.
   reporting_controller_.OnFinishImplFrame(current_id_);
   reporting_controller_.DidNotProduceFrame(current_id_,
                                            FrameSkippedReason::kWaitingOnMain);
-  EXPECT_EQ(0u, dropped_counter_.total_frames());
+  EXPECT_EQ(0u, frame_sorter_.total_frames());
 
   // Main thread responds.
   SimulateActivate();
   EXPECT_EQ(1, reporting_controller_.ActiveReporters());
-  EXPECT_EQ(0u, dropped_counter_.total_frames());
+  EXPECT_EQ(0u, frame_sorter_.total_frames());
 
   // Start and submit a second frame.
   SimulateBeginImplFrame();
   EXPECT_EQ(2, reporting_controller_.ActiveReporters());
-  EXPECT_EQ(0u, dropped_counter_.total_frames());
+  EXPECT_EQ(0u, frame_sorter_.total_frames());
 
   reporting_controller_.OnFinishImplFrame(current_id_);
   SimulatePresentCompositorFrame();
-  EXPECT_EQ(0u, dropped_counter_.total_smoothness_dropped());
-  EXPECT_EQ(3u, dropped_counter_.total_frames());
+  EXPECT_EQ(3u, frame_sorter_.total_frames());
 }
 
 TEST_F(CompositorFrameReportingControllerTest, MainFrameBeforeCommit) {
diff --git a/cc/metrics/dropped_frame_counter_unittest.cc b/cc/metrics/dropped_frame_counter_unittest.cc
index caab103..8f39b058 100644
--- a/cc/metrics/dropped_frame_counter_unittest.cc
+++ b/cc/metrics/dropped_frame_counter_unittest.cc
@@ -157,7 +157,8 @@
     if (presented_frames_ < config_.animation_frames)
       return;
 
-    auto* dropped_frame_counter = host_impl->dropped_frame_counter();
+    auto* dropped_frame_counter =
+        host_impl->dropped_frame_counter_for_testing();
     DCHECK(dropped_frame_counter);
 
     total_frames_ = dropped_frame_counter->total_frames();
diff --git a/cc/metrics/frame_sorter.cc b/cc/metrics/frame_sorter.cc
index 30749b98..f8b48b18 100644
--- a/cc/metrics/frame_sorter.cc
+++ b/cc/metrics/frame_sorter.cc
@@ -77,6 +77,19 @@
   }
 }
 
+void FrameSorter::AddFrameInfoToBuffer(const FrameInfo& frame_info) {
+  ring_buffer_.SaveToBuffer(frame_info.final_state);
+  ++total_frames_;
+  if (frame_info.final_state == FrameInfo::FrameFinalState::kDropped) {
+    ++total_dropped_;
+  } else if (frame_info.final_state ==
+                 FrameInfo::FrameFinalState::kPresentedPartialNewMain ||
+             frame_info.final_state ==
+                 FrameInfo::FrameFinalState::kPresentedPartialOldMain) {
+    ++total_partial_;
+  }
+}
+
 void FrameSorter::AddFrameResult(const viz::BeginFrameArgs& args,
                                  const FrameInfo& frame_info) {
   if (pending_frames_.empty() || current_source_id_ > args.frame_id.source_id) {
@@ -136,6 +149,9 @@
 }
 
 void FrameSorter::Reset() {
+  total_frames_ = 0;
+  total_partial_ = 0;
+  total_dropped_ = 0;
   for (const auto& pending_frame : pending_frames_) {
     const auto& frame_id = pending_frame.frame_id;
     auto& frame_state = frame_states_[frame_id];
@@ -150,6 +166,7 @@
     frame_state.OnReset();
   }
   pending_frames_.clear();
+  ring_buffer_.Clear();
 }
 
 void FrameSorter::FlushFrames() {
@@ -172,4 +189,16 @@
   DCHECK_GT(flushed_count, 0u);
 }
 
+uint32_t FrameSorter::GetAverageThroughput() const {
+  size_t good_frames = 0;
+  for (auto it = End(); it; --it) {
+    if (**it == FrameInfo::FrameFinalState::kPresentedAll ||
+        **it == FrameInfo::FrameFinalState::kPresentedPartialOldMain) {
+      ++good_frames;
+    }
+  }
+  double throughput = 100. * good_frames / ring_buffer_.BufferSize();
+  return static_cast<uint32_t>(throughput);
+}
+
 }  // namespace cc
diff --git a/cc/metrics/frame_sorter.h b/cc/metrics/frame_sorter.h
index bff6415..c723c779 100644
--- a/cc/metrics/frame_sorter.h
+++ b/cc/metrics/frame_sorter.h
@@ -13,16 +13,16 @@
 
 #include "base/containers/circular_deque.h"
 #include "base/containers/flat_set.h"
+#include "base/containers/ring_buffer.h"
 #include "base/functional/callback.h"
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
 #include "cc/cc_export.h"
+#include "cc/metrics/frame_info.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 
 namespace cc {
 
-struct FrameInfo;
-
 // FrameSorterObserver class notifies registered
 // observers when frames are flushed by the FrameSorter.
 class FrameSorterObserver : public base::CheckedObserver {
@@ -63,6 +63,9 @@
   // The frames must be added in the correct order.
   void AddNewFrame(const viz::BeginFrameArgs& args);
 
+  // Called on all frames, including those before FirstContentfulPaint.
+  void AddFrameInfoToBuffer(const FrameInfo& frame_info);
+
   // The results can be added in any order. However, the frame must have been
   // added by an earlier call to |AddNewFrame()|.
   void AddFrameResult(const viz::BeginFrameArgs& args,
@@ -71,6 +74,20 @@
   // Check if a frame has been previously reported as dropped.
   bool IsAlreadyReportedDropped(const viz::BeginFrameId& id) const;
 
+  // Ring buffer which keeps a state shorthand of recently finished frames.
+  typedef base::RingBuffer<FrameInfo::FrameFinalState, 180> RingBufferType;
+  RingBufferType::Iterator Begin() const { return ring_buffer_.Begin(); }
+  // `End()` points to the last `FrameState`, not past it.
+  RingBufferType::Iterator End() const { return ring_buffer_.End(); }
+
+  // For requesting recent frame state information.
+  size_t frame_history_size() const { return ring_buffer_.BufferSize(); }
+  size_t total_frames() const { return total_frames_; }
+  size_t total_dropped() const { return total_dropped_; }
+  size_t total_partial() const { return total_partial_; }
+
+  uint32_t GetAverageThroughput() const;
+
   void Reset();
 
  private:
@@ -88,6 +105,13 @@
   std::map<viz::BeginFrameId, FrameState> frame_states_;
   std::map<viz::BeginFrameId, FrameInfo> frame_infos_;
 
+  // Ring buffer that stores the state of recently completed frames
+  // and the associated counters.
+  RingBufferType ring_buffer_;
+  size_t total_frames_ = 0;
+  size_t total_partial_ = 0;
+  size_t total_dropped_ = 0;
+
   std::optional<uint64_t> current_source_id_;
 };
 
diff --git a/cc/test/fake_picture_layer_tiling_client.h b/cc/test/fake_picture_layer_tiling_client.h
index 848ee0f5..087c6e0 100644
--- a/cc/test/fake_picture_layer_tiling_client.h
+++ b/cc/test/fake_picture_layer_tiling_client.h
@@ -81,8 +81,8 @@
   std::unique_ptr<TileManager> tile_manager_;
   scoped_refptr<RasterSource> raster_source_;
   gfx::Size tile_size_;
-  raw_ptr<PictureLayerTilingSet, DanglingUntriaged> twin_set_;
-  raw_ptr<PictureLayerTiling, DanglingUntriaged> twin_tiling_;
+  raw_ptr<PictureLayerTilingSet> twin_set_;
+  raw_ptr<PictureLayerTiling> twin_tiling_;
   gfx::Rect text_rect_;
   Region invalidation_;
   bool has_valid_tile_priorities_;
diff --git a/cc/tiles/picture_layer_tiling_set_unittest.cc b/cc/tiles/picture_layer_tiling_set_unittest.cc
index 7c92a5fc..71e0b9d6 100644
--- a/cc/tiles/picture_layer_tiling_set_unittest.cc
+++ b/cc/tiles/picture_layer_tiling_set_unittest.cc
@@ -418,6 +418,10 @@
   for (auto* tile : active_tiles) {
     EXPECT_EQ(tile_size3, tile->content_rect().size());
   }
+
+  // Clear the raw_ptr to pending_set before it goes out of scope to prevent
+  // dangling pointer when active_client is destroyed after pending_set.
+  active_client.set_twin_tiling_set(nullptr);
 }
 
 TEST(PictureLayerTilingSetTest, ModifyPendingTilingSetTwiceInOneVsync) {
@@ -480,6 +484,10 @@
   EXPECT_EQ(tile_size2, pending_set->tiling_at(0)->tile_size());
   // The pending tiling should have tiles.
   EXPECT_TRUE(pending_set->tiling_at(0)->has_tiles());
+
+  // Clear the raw_ptr to pending_set before it goes out of scope to prevent
+  // dangling pointer when active_client is destroyed after pending_set.
+  active_client.set_twin_tiling_set(nullptr);
 }
 
 TEST(PictureLayerTilingSetTest, MaxContentScale) {
@@ -537,6 +545,10 @@
       raster_source.get(), pending_set.get(), Region(), 1.f, max_content_scale);
   // All the tilings are on the active tree.
   EXPECT_EQ(2u, active_set->num_tilings());
+
+  // Clear the raw_ptr to pending_set before it goes out of scope to prevent
+  // dangling pointer when active_client is destroyed after pending_set.
+  active_client.set_twin_tiling_set(nullptr);
 }
 
 TEST(PictureLayerTilingSetTest, SkewportLimits) {
@@ -1093,6 +1105,10 @@
   // No changes for active set until activation.
   EXPECT_FALSE(
       active_set->UpdateTilePriorities(viewport, 1.f, time, Occlusion(), true));
+
+  // Clear the raw_ptr to pending_set before it goes out of scope to prevent
+  // dangling pointer when active_client is destroyed after pending_set.
+  active_client.set_twin_tiling_set(nullptr);
 }
 
 TEST(PictureLayerTilingSetTest, TilingTranslationChanges) {
@@ -1152,6 +1168,10 @@
   ASSERT_EQ(1u, active_set->num_tilings());
   EXPECT_EQ(active_set->tiling_at(0)->raster_transform(), raster_transform2);
   EXPECT_EQ(1u, active_set->tiling_at(0)->AllTilesForTesting().size());
+
+  // Clear the raw_ptr to pending_set before it goes out of scope to prevent
+  // dangling pointer when active_client is destroyed after pending_set.
+  active_client.set_twin_tiling_set(nullptr);
 }
 
 TEST(PictureLayerTilingSetTest, LcdChanges) {
@@ -1205,6 +1225,11 @@
                                     Occlusion(), false);
   // We should have created all tiles because lcd state changed.
   EXPECT_EQ(4u, pending_set->tiling_at(0)->AllTilesForTesting().size());
+
+  // Clear the raw_ptrs to prevent dangling pointers when objects go out of
+  // scope.
+  active_client.set_twin_tiling_set(nullptr);
+  pending_client.set_twin_tiling_set(nullptr);
 }
 
 }  // namespace
diff --git a/cc/tiles/picture_layer_tiling_unittest.cc b/cc/tiles/picture_layer_tiling_unittest.cc
index c3ae885..b2d3700 100644
--- a/cc/tiles/picture_layer_tiling_unittest.cc
+++ b/cc/tiles/picture_layer_tiling_unittest.cc
@@ -639,6 +639,10 @@
   // respect to the tiles that it invalidates
   EXPECT_FALSE(active_tiling->TileAt(0, 1));
   EXPECT_FALSE(active_tiling->TileAt(0, 2));
+
+  // Clear the raw_ptr to active_tiling before it goes out of scope to prevent
+  // dangling pointer when client_ is destroyed after active_tiling.
+  client_.set_twin_tiling(nullptr);
 }
 
 TEST_F(PictureLayerTilingIteratorTest, LiveTilesExactlyCoverLiveTileRect) {
diff --git a/cc/tiles/software_image_decode_cache.cc b/cc/tiles/software_image_decode_cache.cc
index d5cff29..8f149942 100644
--- a/cc/tiles/software_image_decode_cache.cc
+++ b/cc/tiles/software_image_decode_cache.cc
@@ -11,7 +11,6 @@
 #include <string>
 #include <utility>
 
-#include "base/debug/stack_trace.h"
 #include "base/format_macros.h"
 #include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
diff --git a/cc/tiles/tile_manager_unittest.cc b/cc/tiles/tile_manager_unittest.cc
index 8155f1e..88090bc 100644
--- a/cc/tiles/tile_manager_unittest.cc
+++ b/cc/tiles/tile_manager_unittest.cc
@@ -1676,6 +1676,10 @@
     EXPECT_TRUE(queue);
     EXPECT_FALSE(queue->IsEmpty());
   }
+
+  // Clear the raw_ptr to tiling_set before it goes out of scope to prevent
+  // dangling pointer when pending_client is destroyed after tiling_set.
+  pending_client.set_twin_tiling_set(nullptr);
 }
 
 TEST_F(TileManagerTilePriorityQueueTest, NoRasterTasksforSolidColorTiles) {
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index fe6137d3..a4ce122 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -695,6 +695,7 @@
   DroppedFrameCounter* dropped_frame_counter() {
     return &dropped_frame_counter_;
   }
+  FrameSorter* frame_sorter() { return &frame_sorter_; }
   MemoryHistory* memory_history() { return memory_history_.get(); }
   DebugRectHistory* debug_rect_history() { return debug_rect_history_.get(); }
   viz::ClientResourceProvider* resource_provider() {
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 342fdff..68a6dc0 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -46,6 +46,7 @@
 #include "cc/layers/video_layer_impl.h"
 #include "cc/layers/viewport.h"
 #include "cc/metrics/compositor_frame_reporting_controller.h"
+#include "cc/metrics/frame_info.h"
 #include "cc/resources/ui_resource_bitmap.h"
 #include "cc/resources/ui_resource_manager.h"
 #include "cc/test/animation_test_common.h"
@@ -14408,15 +14409,16 @@
   // there is less than 20 frames in its pending frames list.
 }
 
-// Test that DroppedFrameCounter and TotalFrameCounter reset themselves under
-// certain conditions
+// Test that TotalFrameCounter resets itself under certain conditions
 TEST_P(LayerTreeHostImplTest, FrameCounterReset) {
   DroppedFrameCounter* dropped_frame_counter =
       host_impl_->dropped_frame_counter_for_testing();
   FrameSorter* frame_sorter = host_impl_->frame_sorter_for_testing();
-  EXPECT_EQ(dropped_frame_counter->total_frames(), 0u);
-  dropped_frame_counter->AddGoodFrame();
-  EXPECT_EQ(dropped_frame_counter->total_frames(), 1u);
+  EXPECT_EQ(frame_sorter->total_frames(), 0u);
+  FrameInfo frame_info;
+  frame_info.final_state = FrameInfo::FrameFinalState::kPresentedAll;
+  frame_sorter->AddFrameInfoToBuffer(frame_info);
+  EXPECT_EQ(frame_sorter->total_frames(), 1u);
 
   auto interval = base::Milliseconds(16);
   base::TimeTicks now = base::TimeTicks::Now();
@@ -14443,18 +14445,16 @@
   frame_sorter->AddFrameResult(
       args, CreateFakeFrameInfo(FrameInfo::FrameFinalState::kDropped));
   EXPECT_EQ(dropped_frame_counter->total_smoothness_dropped(), 1u);
-  dropped_frame_counter->AddGoodFrame();
+  frame_sorter->AddFrameInfoToBuffer(frame_info);
   host_impl_->SetActiveURL(GURL(), 1u);
-  EXPECT_EQ(dropped_frame_counter->total_frames(), 0u);
-  EXPECT_EQ(dropped_frame_counter->total_smoothness_dropped(), 0u);
+  EXPECT_EQ(frame_sorter->total_frames(), 0u);
+  EXPECT_EQ(dropped_frame_counter->total_dropped(), 0u);
 }
 
-// Test that DroppedFrameCounter and TotalFrameCounter do not reset themselves
-// under certain conditions
+// Test that TotalFrameCounter does not reset itself under certain conditions
 TEST_P(LayerTreeHostImplTest, FrameCounterNotReset) {
-  DroppedFrameCounter* dropped_frame_counter =
-      host_impl_->dropped_frame_counter_for_testing();
-  EXPECT_EQ(dropped_frame_counter->total_frames(), 0u);
+  FrameSorter* frame_sorter = host_impl_->frame_sorter_for_testing();
+  EXPECT_EQ(frame_sorter->total_frames(), 0u);
 
   auto interval = base::Milliseconds(16);
   base::TimeTicks now = base::TimeTicks::Now();
@@ -14466,9 +14466,11 @@
   begin_frame_metrics.should_measure_smoothness = true;
   host_impl_->ReadyToCommit(arg1, /*scroll_and_viewport_changes_synced=*/true,
                             &begin_frame_metrics, /*commit_timeout=*/false);
-  EXPECT_EQ(dropped_frame_counter->total_frames(), 0u);
-  dropped_frame_counter->AddGoodFrame();
-  EXPECT_EQ(dropped_frame_counter->total_frames(), 1u);
+  EXPECT_EQ(frame_sorter->total_frames(), 0u);
+  FrameInfo frame_info;
+  frame_info.final_state = FrameInfo::FrameFinalState::kPresentedAll;
+  frame_sorter->AddFrameInfoToBuffer(frame_info);
+  EXPECT_EQ(frame_sorter->total_frames(), 1u);
 
   now = deadline;
   deadline = now + interval;
@@ -14479,7 +14481,7 @@
   // flag should not reset the counter.
   host_impl_->ReadyToCommit(arg2, /*scroll_and_viewport_changes_synced=*/true,
                             &begin_frame_metrics, /*commit_timeout=*/false);
-  EXPECT_EQ(dropped_frame_counter->total_frames(), 1u);
+  EXPECT_EQ(frame_sorter->total_frames(), 1u);
 }
 
 // Tests that the scheduled autoscroll task aborts if a 2nd mousedown occurs in
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 736dde2..e92dd52 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -1966,6 +1966,10 @@
   return host_impl_->dropped_frame_counter();
 }
 
+FrameSorter* LayerTreeImpl::frame_sorter() const {
+  return host_impl_->frame_sorter();
+}
+
 MemoryHistory* LayerTreeImpl::memory_history() const {
   return host_impl_->memory_history();
 }
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h
index 6356a05..8531318 100644
--- a/cc/trees/layer_tree_impl.h
+++ b/cc/trees/layer_tree_impl.h
@@ -133,6 +133,7 @@
   ImageDecodeCache* image_decode_cache() const;
   ImageAnimationController* image_animation_controller() const;
   DroppedFrameCounter* dropped_frame_counter() const;
+  FrameSorter* frame_sorter() const;
   MemoryHistory* memory_history() const;
   DebugRectHistory* debug_rect_history() const;
   const GlobalStateThatImpactsTilePriority& global_tile_state() const {
diff --git a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
index 5f1c3fc..da385afd 100644
--- a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
+++ b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
@@ -148,7 +148,7 @@
     private final ObservableSupplierImpl<Integer> mKeyboardInsetSupplier =
             new ObservableSupplierImpl<>();
     private final ObservableSupplierImpl<EdgeToEdgeController> mMockEdgeToEdgeControllerSupplier =
-            new ObservableSupplierImpl<EdgeToEdgeController>();
+            new ObservableSupplierImpl<>();
 
     private static class MockActivityTabProvider extends ActivityTabProvider {
         public Tab mTab;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
index 9a9cc9ed..533a997 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
@@ -546,7 +546,8 @@
         ViewCompat.setBackgroundTintList(cardView, ColorStateList.valueOf(backgroundColor));
 
         titleView.setTextColor(
-                TabUiThemeUtils.getTitleTextColor(titleView.getContext(), isIncognito, isSelected));
+                TabUiThemeUtils.getTitleTextColor(
+                        titleView.getContext(), isIncognito, isSelected, colorId));
 
         thumbnail.updateThumbnailPlaceholder(isIncognito, isSelected, colorId);
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListCoordinator.java
index 736c5ff..945b1454 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListCoordinator.java
@@ -111,7 +111,7 @@
 
         ViewBuilder<TabGroupRowView> innerBuilder = new LayoutViewBuilder<>(R.layout.tab_group_row);
         ViewBuilder<TabGroupRowView> tabGroupRowLayoutBuilder =
-                new ViewBuilder<TabGroupRowView>() {
+                new ViewBuilder<>() {
                     @Override
                     public TabGroupRowView buildView(ViewGroup parent) {
                         TabGroupRowView view = innerBuilder.buildView(parent);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinatorUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinatorUnitTest.java
index fdcbe586..269f1f3 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinatorUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinatorUnitTest.java
@@ -48,7 +48,7 @@
 
     @Rule
     public ActivityScenarioRule<TestActivity> mActivityScenarioRule =
-            new ActivityScenarioRule<TestActivity>(TestActivity.class);
+            new ActivityScenarioRule<>(TestActivity.class);
 
     @Mock private BrowserControlsStateProvider mBrowserControlsStateProvider;
     @Mock private ScrimManager mScrimManager;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorShareAction.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorShareAction.java
index 3d42d8a..7942553 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorShareAction.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorShareAction.java
@@ -258,9 +258,9 @@
     // no shareable URLs are present after filtering.
     private List<Integer> filterTabs(List<Tab> tabs, TabList tabList) {
         assert tabs.size() > 0;
-        List<Integer> sortedTabIndexList = new ArrayList<Integer>();
+        List<Integer> sortedTabIndexList = new ArrayList<>();
 
-        HashSet<Tab> selectedTabs = new HashSet<Tab>(tabs);
+        HashSet<Tab> selectedTabs = new HashSet<>(tabs);
         for (int i = 0; i < tabList.getCount(); i++) {
             Tab tab = tabList.getTabAt(i);
             if (!selectedTabs.contains(tab)) continue;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
index 4eeb855..507ce7d8 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -1405,7 +1405,7 @@
                 && mTabActionState != TabActionState.SELECTABLE
                 && PriceTrackingFeatures.isPriceAnnotationsEnabled(originalProfile)) {
             mListObserver =
-                    new ListObserver<Void>() {
+                    new ListObserver<>() {
                         @Override
                         public void onItemRangeInserted(
                                 ListObservable source, int index, int count) {
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
index 847443b74..d72216be 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
@@ -973,7 +973,7 @@
                 sActivityTestRule.getTestServer().getURL(PAGE_WITH_HTTP_CANONICAL_URL);
         sActivityTestRule.loadUrlInNewTab(httpCanonicalUrl);
 
-        ArrayList<String> urls = new ArrayList<String>();
+        ArrayList<String> urls = new ArrayList<>();
         urls.add(httpsCanonicalUrl);
         urls.add(httpCanonicalUrl);
 
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorTestingRobot.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorTestingRobot.java
index 8ca3b736..aabdc42 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorTestingRobot.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorTestingRobot.java
@@ -99,7 +99,7 @@
      * @return A view matcher that matches a divider view.
      */
     public static Matcher<View> isDivider() {
-        return new TypeSafeMatcher<View>() {
+        return new TypeSafeMatcher<>() {
             @Override
             protected boolean matchesSafely(View view) {
                 return view.getId() == R.id.divider_view;
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorActionUnitTestHelper.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorActionUnitTestHelper.java
index def7cf10..7fcf980 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorActionUnitTestHelper.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorActionUnitTestHelper.java
@@ -131,9 +131,7 @@
         List<Tab> selectedTabs = new ArrayList<>();
         List<Tab> selectedAndRelatedTabs = new ArrayList<>();
         Set<TabListEditorItemSelectionId> selectedItemIds =
-                deterministicSetOrder
-                        ? new LinkedHashSet<TabListEditorItemSelectionId>()
-                        : new HashSet<TabListEditorItemSelectionId>();
+                deterministicSetOrder ? new LinkedHashSet<>() : new HashSet<>();
 
         for (TabIdGroup group : tabIdGroups) {
             List<Tab> groupTabs = new ArrayList<>();
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorBookmarkActionUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorBookmarkActionUnitTest.java
index f5501900..6de5641 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorBookmarkActionUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorBookmarkActionUnitTest.java
@@ -111,7 +111,7 @@
 
     @Test
     public void testBookmarkActionNoTabs() {
-        mAction.onSelectionStateChange(new ArrayList<TabListEditorItemSelectionId>());
+        mAction.onSelectionStateChange(new ArrayList<>());
         Assert.assertEquals(
                 false, mAction.getPropertyModel().get(TabListEditorActionProperties.ENABLED));
         Assert.assertEquals(
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorShareActionUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorShareActionUnitTest.java
index eaa06b3..bb58691 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorShareActionUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorShareActionUnitTest.java
@@ -145,7 +145,7 @@
     public void testShareActionNoTabs() {
         mAction.configure(() -> mTabGroupModelFilter, mSelectionDelegate, mDelegate, false);
 
-        mAction.onSelectionStateChange(new ArrayList<TabListEditorItemSelectionId>());
+        mAction.onSelectionStateChange(new ArrayList<>());
         Assert.assertEquals(
                 false, mAction.getPropertyModel().get(TabListEditorActionProperties.ENABLED));
         Assert.assertEquals(
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
index 63a1fa3a..c8d5497 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
@@ -990,7 +990,7 @@
                         mTabModelActionListenerCaptor.capture());
         assertTrue(mModelList.get(1).model.get(TabProperties.USE_SHRINK_CLOSE_ANIMATION));
 
-        when(mTabGroupModelFilter.getRelatedTabList(anyInt())).thenReturn(new ArrayList<Tab>());
+        when(mTabGroupModelFilter.getRelatedTabList(anyInt())).thenReturn(new ArrayList<>());
         TabModelActionListener listener = mTabModelActionListenerCaptor.getValue();
         listener.onConfirmationDialogResult(
                 DialogType.SYNC, ActionConfirmationResult.CONFIRMATION_POSITIVE);
@@ -5118,7 +5118,7 @@
             @OptimizationGuideDecision int decision, Map<GURL, Any> responses) {
         for (Map.Entry<GURL, Any> responseEntry : responses.entrySet()) {
             doAnswer(
-                            new Answer<Void>() {
+                            new Answer<>() {
                                 @Override
                                 public Void answer(InvocationOnMock invocation) {
                                     OptimizationGuideCallback callback =
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
index e2e043c..c261000 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
@@ -1270,7 +1270,7 @@
 
     @Override
     public boolean isFeedExpanded() {
-        return mSectionHeaderModel.get(SectionHeaderListProperties.IS_SECTION_ENABLED_KEY);
+        return mMediator.isSuggestionsVisible();
     }
 
     @Override
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java
index 326fe10..33119ae 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java
@@ -1031,8 +1031,9 @@
                 mTabToStreamMap.clear();
             }
         } else {
-            if (mCurrentStream != null) {
-                mCurrentStream.removeOnContentChangedListener(mStreamContentChangedListener);
+            if (mStreamHolder != null) {
+                mStreamHolder.removeOnContentChangedListener(mStreamContentChangedListener);
+                mStreamHolder = null;
             }
         }
         mStreamContentChangedListener = null;
@@ -1599,7 +1600,7 @@
         }
     }
 
-    private boolean isSuggestionsVisible() {
+    public boolean isSuggestionsVisible() {
         return getPrefService().getBoolean(Pref.ARTICLES_LIST_VISIBLE);
     }
 
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedV2NewTabPageTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedV2NewTabPageTest.java
index 2e9b89d..b9df56e 100644
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedV2NewTabPageTest.java
+++ b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedV2NewTabPageTest.java
@@ -554,7 +554,7 @@
      */
     private int getFakeboxTop(final NewTabPage ntp) {
         return ThreadUtils.runOnUiThreadBlocking(
-                new Callable<Integer>() {
+                new Callable<>() {
                     @Override
                     public Integer call() {
                         final View fakebox = ntp.getView().findViewById(R.id.search_box);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeWindow.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeWindow.java
index 87972eb..c7f49dbc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeWindow.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeWindow.java
@@ -62,7 +62,7 @@
                 compositorViewHolderSupplier,
                 modalDialogManagerSupplier,
                 sKeyboardVisibilityDelegateFactory.create(
-                        new WeakReference<Activity>(activity), manualFillingComponentSupplier),
+                        new WeakReference<>(activity), manualFillingComponentSupplier),
                 activityTopResumedSupported,
                 intentRequestTracker,
                 insetObserver);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/DefaultBrowserInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/DefaultBrowserInfo.java
index ce181350..e2571738 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/DefaultBrowserInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/DefaultBrowserInfo.java
@@ -51,7 +51,7 @@
                             @Override
                             protected ArrayList<String> doInBackground() {
                                 Context context = ContextUtils.getApplicationContext();
-                                ArrayList<String> menuTitles = new ArrayList<String>(2);
+                                ArrayList<String> menuTitles = new ArrayList<>(2);
                                 // Store the package label of current application.
                                 menuTitles.add(
                                         getTitleFromPackageLabel(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
index 0db8d02..697c1fe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
@@ -719,7 +719,7 @@
                     IntentUtils.safeGetStringExtra(
                             intent, RecognizerResultsIntent.EXTRA_VOICE_SEARCH_RESULT_STRINGS);
             if (testResult != null) {
-                results = new ArrayList<String>();
+                results = new ArrayList<>();
                 results.add(testResult);
             }
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/WarmupManager.java b/chrome/android/java/src/org/chromium/chrome/browser/WarmupManager.java
index a48c960..d12b56c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/WarmupManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/WarmupManager.java
@@ -508,7 +508,7 @@
     public void transferViewHierarchyTo(ViewGroup contentView) {
         ThreadUtils.assertOnUiThread();
         ViewGroup from = mMainView;
-        Set<Theme> rebasedThemes = new ArraySet<Theme>(from.getChildCount());
+        Set<Theme> rebasedThemes = new ArraySet<>(from.getChildCount());
         mMainView = null;
         if (from == null) return;
         ((CctContextWrapper) from.getContext()).mActivityContext = contentView.getContext();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ai/AiAssistantService.java b/chrome/android/java/src/org/chromium/chrome/browser/ai/AiAssistantService.java
index cb33091..fb622b25 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ai/AiAssistantService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ai/AiAssistantService.java
@@ -262,7 +262,7 @@
                 mSystemAiProvider.isAvailable(context, availabilityRequestBuilder.build());
         Futures.addCallback(
                 availabilityFuture,
-                new FutureCallback<AvailabilityResponse>() {
+                new FutureCallback<>() {
                     @Override
                     public void onSuccess(@Nullable AvailabilityResponse result) {
                         onAvailabilityResponse(result);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/download/DownloadMessageUiDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/app/download/DownloadMessageUiDelegate.java
index 137db583..09d189a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/download/DownloadMessageUiDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/download/DownloadMessageUiDelegate.java
@@ -26,7 +26,7 @@
 
 /** Delegate for {@link DownloadMessageUiController} to provide chrome layer dependencies. */
 public class DownloadMessageUiDelegate implements DownloadMessageUiController.Delegate {
-    private WeakReference<ChromeActivity> mActivity = new WeakReference<ChromeActivity>(null);
+    private WeakReference<ChromeActivity> mActivity = new WeakReference<>(null);
 
     /** Constructor. */
     public DownloadMessageUiDelegate() {
@@ -61,7 +61,7 @@
         boolean shouldSwitchToFocusedActivity =
                 focusedActivity instanceof ChromeActivity && focusedActivity != mActivity.get();
         if (!shouldSwitchToFocusedActivity) return false;
-        mActivity = new WeakReference<ChromeActivity>((ChromeActivity) focusedActivity);
+        mActivity = new WeakReference<>((ChromeActivity) focusedActivity);
         return true;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/download/home/DownloadActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/download/home/DownloadActivity.java
index d7b29da..f939c7c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/download/home/DownloadActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/download/home/DownloadActivity.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.app.download.home;
 
-import android.app.Activity;
 import android.os.Bundle;
 
 import org.chromium.chrome.browser.SnackbarActivity;
@@ -72,8 +71,7 @@
         OfflineContentAggregatorNotificationBridgeUiFactory.instance();
         boolean showPrefetchContent =
                 DownloadActivityLauncher.shouldShowPrefetchContent(getIntent());
-        mPermissionDelegate =
-                new ActivityAndroidPermissionDelegate(new WeakReference<Activity>(this));
+        mPermissionDelegate = new ActivityAndroidPermissionDelegate(new WeakReference<>(this));
         mOtrProfileId = DownloadUtils.getOtrProfileIdFromIntent(getIntent());
 
         DownloadManagerUiConfig config =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/HeadlessBrowserControlsStateProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/HeadlessBrowserControlsStateProvider.java
index ae983fe..f6688f6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/HeadlessBrowserControlsStateProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/HeadlessBrowserControlsStateProvider.java
@@ -89,4 +89,9 @@
     public @ControlsPosition int getControlsPosition() {
         return ControlsPosition.NONE;
     }
+
+    @Override
+    public boolean isVisibilityForced() {
+        return false;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillCreditCardEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillCreditCardEditor.java
index a2bea06..95a89a43 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillCreditCardEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillCreditCardEditor.java
@@ -53,8 +53,7 @@
 
         // Populate the billing address dropdown.
         ArrayAdapter<AutofillProfile> profilesAdapter =
-                new ArrayAdapter<AutofillProfile>(
-                        getActivity(), android.R.layout.simple_spinner_item);
+                new ArrayAdapter<>(getActivity(), android.R.layout.simple_spinner_item);
         profilesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
 
         AutofillProfile noSelection = AutofillProfile.builder().build();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillLocalCardEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillLocalCardEditor.java
index 66a2da2e..a9c7f732 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillLocalCardEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillLocalCardEditor.java
@@ -245,7 +245,7 @@
     @SuppressWarnings("DuplicateDateFormatField") // There's probably a bug here...
     void addSpinnerAdapters() {
         ArrayAdapter<CharSequence> adapter =
-                new ArrayAdapter<CharSequence>(getActivity(), android.R.layout.simple_spinner_item);
+                new ArrayAdapter<>(getActivity(), android.R.layout.simple_spinner_item);
 
         // Populate the month dropdown.
         Calendar calendar = Calendar.getInstance();
@@ -260,8 +260,7 @@
         assumeNonNull(mExpirationMonth).setAdapter(adapter);
 
         // Populate the year dropdown.
-        adapter =
-                new ArrayAdapter<CharSequence>(getActivity(), android.R.layout.simple_spinner_item);
+        adapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_spinner_item);
         int initialYear = calendar.get(Calendar.YEAR);
         for (int year = initialYear; year < initialYear + 10; year++) {
             adapter.add(Integer.toString(year));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetServiceImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetServiceImpl.java
index 8abab11b..aaa661bb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetServiceImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetServiceImpl.java
@@ -216,7 +216,7 @@
             // Move folders to the beginning of the list.
             Collections.sort(
                     items,
-                    new Comparator<BookmarkItem>() {
+                    new Comparator<>() {
                         @Override
                         public int compare(BookmarkItem lhs, BookmarkItem rhs) {
                             return lhs.isFolder() == rhs.isFolder() ? 0 : lhs.isFolder() ? -1 : 1;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityBrowserControlsVisibilityManager.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityBrowserControlsVisibilityManager.java
index 24120459..5311805 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityBrowserControlsVisibilityManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityBrowserControlsVisibilityManager.java
@@ -7,7 +7,6 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
-import org.chromium.blink.mojom.DisplayMode;
 import org.chromium.cc.input.BrowserControlsState;
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider;
 import org.chromium.chrome.browser.customtabs.CloseButtonVisibilityManager;
@@ -16,8 +15,6 @@
 import org.chromium.chrome.browser.customtabs.content.TabObserverRegistrar.CustomTabTabObserver;
 import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarCoordinator;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderUtils;
-import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateManager;
 import org.chromium.components.security_state.ConnectionSecurityLevel;
 import org.chromium.components.security_state.SecurityStateModel;
 
@@ -25,8 +22,7 @@
  * Updates the browser controls state based on whether the browser is in TWA mode, the page's
  * security level, and desktop windowing state.
  */
-public class TrustedWebActivityBrowserControlsVisibilityManager
-        implements DesktopWindowStateManager.AppHeaderObserver {
+public class TrustedWebActivityBrowserControlsVisibilityManager {
     static final @BrowserControlsState int DEFAULT_BROWSER_CONTROLS_STATE =
             BrowserControlsState.BOTH;
 
@@ -34,12 +30,10 @@
     private final CustomTabActivityTabProvider mTabProvider;
     private final CustomTabToolbarCoordinator mToolbarCoordinator;
     private final CloseButtonVisibilityManager mCloseButtonVisibilityManager;
-    private final @Nullable DesktopWindowStateManager mDesktopWindowStateManager;
     private final BrowserServicesIntentDataProvider mIntentDataProvider;
 
     private boolean mInAppMode;
     private final boolean mShowBrowserControlsForChildTab;
-    private boolean mIsInDesktopWindow;
 
     private @BrowserControlsState int mBrowserControlsState = DEFAULT_BROWSER_CONTROLS_STATE;
 
@@ -63,35 +57,14 @@
             CustomTabActivityTabProvider tabProvider,
             CustomTabToolbarCoordinator toolbarCoordinator,
             CloseButtonVisibilityManager closeButtonVisibilityManager,
-            @Nullable DesktopWindowStateManager desktopWindowStateManager,
             BrowserServicesIntentDataProvider intentDataProvider) {
         mTabObserverRegistrar = tabObserverRegistrar;
         mTabProvider = tabProvider;
         mToolbarCoordinator = toolbarCoordinator;
         mCloseButtonVisibilityManager = closeButtonVisibilityManager;
-        mDesktopWindowStateManager = desktopWindowStateManager;
         mIntentDataProvider = intentDataProvider;
 
         mShowBrowserControlsForChildTab = (mIntentDataProvider.getWebappExtras() != null);
-        mIsInDesktopWindow = AppHeaderUtils.isAppInDesktopWindow(mDesktopWindowStateManager);
-
-        if (mDesktopWindowStateManager != null) {
-            mDesktopWindowStateManager.addObserver(this);
-        }
-    }
-
-    @Override
-    public void onDesktopWindowingModeChanged(boolean isInDesktopWindow) {
-        if (mIsInDesktopWindow == isInDesktopWindow) return;
-        mIsInDesktopWindow = isInDesktopWindow;
-
-        if (!shouldShowWebAppControls()) return;
-        updateBrowserControlsState();
-        updateCloseButtonVisibility();
-    }
-
-    private boolean shouldShowWebAppControls() {
-        return mInAppMode && mIntentDataProvider.getResolvedDisplayMode() == DisplayMode.MINIMAL_UI;
     }
 
     /** Should be called when the browser enters and exits TWA mode. */
@@ -146,13 +119,6 @@
             return BrowserControlsState.SHOWN;
         }
 
-        // Fallback to browser controls in fullscreen mode when running WebAPK or shortcut web app.
-        if (mIntentDataProvider.isWebappOrWebApkActivity()
-                && shouldShowWebAppControls()
-                && !mIsInDesktopWindow) {
-            return BrowserControlsState.BOTH;
-        }
-
         return shouldShowBrowserControlsAndCloseButton(tab)
                 ? BrowserControlsState.BOTH
                 : BrowserControlsState.HIDDEN;
@@ -162,13 +128,6 @@
         return tab != null && tab.getParentId() != Tab.INVALID_TAB_ID;
     }
 
-    /** Clears up current instance. Can't be used after this method is called. */
-    public void destroy() {
-        if (mDesktopWindowStateManager != null) {
-            mDesktopWindowStateManager.removeObserver(this);
-        }
-    }
-
     @ConnectionSecurityLevel
     @VisibleForTesting
     int getSecurityLevel(Tab tab) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/splashscreen/webapps/WebappSplashController.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/splashscreen/webapps/WebappSplashController.java
index b9f21ee..f786be1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/splashscreen/webapps/WebappSplashController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/splashscreen/webapps/WebappSplashController.java
@@ -104,7 +104,7 @@
         }
 
         storage.getSplashScreenImage(
-                new WebappDataStorage.FetchCallback<Bitmap>() {
+                new WebappDataStorage.FetchCallback<>() {
                     @Override
                     public void onDataRetrieved(Bitmap splashImage) {
                         initializeWebApkInfoSplashLayout(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/BrowsingDataBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/BrowsingDataBridge.java
index b7f766b3..66e8c20 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/BrowsingDataBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/BrowsingDataBridge.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.browsing_data;
 
-
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JniType;
 import org.jni_zero.NativeMethods;
@@ -12,8 +11,6 @@
 import org.chromium.base.Callback;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.lifetime.Destroyable;
-import org.chromium.build.annotations.NullMarked;
-import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.app.tabwindow.TabWindowManagerSingleton;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileKeyedMap;
@@ -31,9 +28,8 @@
  * Communicates between ClearBrowsingData, HatsService, ImportantSitesUtils (C++) and
  * ClearBrowsingDataFragment (Java UI).
  */
-@NullMarked
 public final class BrowsingDataBridge implements Destroyable {
-    private static @Nullable ProfileKeyedMap<BrowsingDataBridge> sProfileMap;
+    private static ProfileKeyedMap<BrowsingDataBridge> sProfileMap;
 
     /**
      * List of observers to track the active tab in each {@link TabModelSelector}. This is used to
@@ -168,7 +164,7 @@
             OnClearBrowsingDataListener listener, int[] dataTypes, @TimePeriod int timePeriod) {
         BrowsingDataBridgeJni.get()
                 .clearBrowsingData(
-                        mProfile.getOrCreatePrimaryOtrProfile(),
+                        mProfile.getPrimaryOtrProfile(/* createIfNeeded= */ true),
                         listener,
                         dataTypes,
                         timePeriod,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFetcher.java b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFetcher.java
index 10ff96a..7c60005 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFetcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFetcher.java
@@ -55,7 +55,7 @@
     }
 
     public static final Creator<ClearBrowsingDataFetcher> CREATOR =
-            new Creator<ClearBrowsingDataFetcher>() {
+            new Creator<>() {
                 @Override
                 public ClearBrowsingDataFetcher createFromParcel(Parcel in) {
                     return new ClearBrowsingDataFetcher(in);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/UrlFilterBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/UrlFilterBridge.java
index 7e20c95..db4c48c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/UrlFilterBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/UrlFilterBridge.java
@@ -4,20 +4,16 @@
 
 package org.chromium.chrome.browser.browsing_data;
 
-
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JniType;
 import org.jni_zero.NativeMethods;
 
-import org.chromium.build.annotations.NullMarked;
-
 /**
  * A {@link UrlFilter} that delegates the matching to the native side.
  *
  * <p>BrowsingDataRemover on the C++ side will instantiate this class through its C++ counterpart
  * and pass it to browsing data storage backends on the Java side.
  */
-@NullMarked
 public class UrlFilterBridge implements UrlFilter {
     private long mNativeUrlFilterBridge;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/UrlFilters.java b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/UrlFilters.java
index 7120ab3..3ddb28a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/UrlFilters.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/UrlFilters.java
@@ -4,13 +4,9 @@
 
 package org.chromium.chrome.browser.browsing_data;
 
-
 import android.text.TextUtils;
 
-import org.chromium.build.annotations.NullMarked;
-
 /** Implementations of URLFilter used in tests. */
-@NullMarked
 public final class UrlFilters {
     /** A trivial implementation of {@link UrlFilter} that matches all urls. */
     public static class AllUrls implements UrlFilter {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelManager.java
index b61cf95c..35795d2f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelManager.java
@@ -79,7 +79,7 @@
         mSuppressedPanels =
                 new PriorityQueue<>(
                         INITIAL_QUEUE_CAPACITY,
-                        new Comparator<OverlayPanel>() {
+                        new Comparator<>() {
                             @Override
                             public int compare(OverlayPanel p1, OverlayPanel p2) {
                                 // The head of the queue is the smallest element, so subtract p1's
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
index da1f16f9..5c8b541 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
@@ -319,7 +319,7 @@
         // Prioritize toTabId because fromTabId likely has a live layer.
         int fromTabId = dragFromLeftEdge ? rightTabId : leftTabId;
         int toTabId = !dragFromLeftEdge ? rightTabId : leftTabId;
-        List<Integer> visibleTabs = new ArrayList<Integer>();
+        List<Integer> visibleTabs = new ArrayList<>();
         if (toTabId != Tab.INVALID_TAB_ID) visibleTabs.add(toTabId);
         if (fromTabId != Tab.INVALID_TAB_ID) visibleTabs.add(fromTabId);
         updateCacheVisibleIds(visibleTabs);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/components/CompositorButton.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/components/CompositorButton.java
index f9f65420..f64690f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/components/CompositorButton.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/components/CompositorButton.java
@@ -28,7 +28,7 @@
      * org.chromium.chrome.browser.layouts.animation.CompositorAnimator}.
      */
     public static final FloatProperty<CompositorButton> OPACITY =
-            new FloatProperty<CompositorButton>("opacity") {
+            new FloatProperty<>("opacity") {
                 @Override
                 public void setValue(CompositorButton object, float value) {
                     object.setOpacity(value);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/eventfilter/OverlayPanelEventFilter.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/eventfilter/OverlayPanelEventFilter.java
index 480e5b6..96a7165 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/eventfilter/OverlayPanelEventFilter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/eventfilter/OverlayPanelEventFilter.java
@@ -111,7 +111,7 @@
     private float mSyntheticActionDownY;
 
     /** The list of recorded events. */
-    private final ArrayList<MotionEvent> mRecordedEvents = new ArrayList<MotionEvent>();
+    private final ArrayList<MotionEvent> mRecordedEvents = new ArrayList<>();
 
     /** The initial Y position of the current gesture. */
     private float mInitialEventY;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/SimpleAnimationLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/SimpleAnimationLayout.java
index 5c0d468..3f9021d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/SimpleAnimationLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/SimpleAnimationLayout.java
@@ -168,7 +168,7 @@
         sourceLayoutTab.setBorderAlpha(0.0f);
 
         mLayoutTabs = new LayoutTab[] {sourceLayoutTab};
-        updateCacheVisibleIds(new LinkedList<Integer>(Arrays.asList(sourceTabId)));
+        updateCacheVisibleIds(new LinkedList<>(Arrays.asList(sourceTabId)));
     }
 
     @Override
@@ -221,7 +221,7 @@
         } else {
             mLayoutTabs = new LayoutTab[] {mLayoutTabs[0], newLayoutTab};
         }
-        updateCacheVisibleIds(new LinkedList<Integer>(Arrays.asList(id, sourceId)));
+        updateCacheVisibleIds(new LinkedList<>(Arrays.asList(id, sourceId)));
 
         newLayoutTab.setBorderAlpha(0.0f);
 
@@ -292,7 +292,7 @@
         assert mLayoutTabs.length == 1;
         LayoutTab sourceLayoutTab = mLayoutTabs[0];
         mLayoutTabs = new LayoutTab[] {sourceLayoutTab, newLayoutTab};
-        updateCacheVisibleIds(new LinkedList<Integer>(Arrays.asList(id, sourceId)));
+        updateCacheVisibleIds(new LinkedList<>(Arrays.asList(id, sourceId)));
 
         forceAnimationToFinish();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
index 4b757ed..eb9f66c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
@@ -702,7 +702,7 @@
         // Create tab menu
         mCloseButtonMenu = new ListPopupWindow(mContext);
         mCloseButtonMenu.setAdapter(
-                new ArrayAdapter<String>(
+                new ArrayAdapter<>(
                         mContext,
                         R.layout.one_line_list_item,
                         new String[] {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
index d6182fd..b8cc16ce 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
@@ -205,7 +205,6 @@
     private float mWidth; // in dp units
     private float mHeight; // Height of the entire tab strip compositor layer in DP.
     private final float mScrollableStripHeight; // Height of the scrollable tab strip layer in DP.
-    private boolean mIsVerticalScrollInProgress; // Is the tab strip is being scrolled by a gesture.
 
     // Padding regions that tabs should remain untouchable.
     private float mLeftPadding; // in dp units
@@ -807,17 +806,19 @@
                     getStripTransitionScrimColor(), mStripTransitionScrimOpacity);
 
             yOffset = 0;
-        } else if (ChromeFeatureList.sBrowserControlsInViz.isEnabled()
-                && mIsVerticalScrollInProgress) {
-            // With bciv, we don't want anything else controlling the offset while scrolling.
-            // Tabstrip currently has no min height, so setting to 0 is ok.
-            yOffset = 0;
         } else if ((getStripVisibilityState() & StripVisibilityState.HIDDEN_BY_HEIGHT_TRANSITION)
                 != 0) {
             // When the tab strip is hidden by a height transition, the stable offset of this scene
             // layer should be a negative value.
             yOffset -= getHeight();
+        } else if (ChromeFeatureList.sBrowserControlsInViz.isEnabled()
+                && !mBrowserControlsStateProvider.isVisibilityForced()) {
+            // With bciv, as long as if the visibility isn't forced by the browser, and if the
+            // tabstrip isn't hidden, the composited layers should positioned at their fully visible
+            // positions.
+            yOffset = 0;
         }
+
         mTabStripTreeProvider.pushAndUpdateStrip(
                 this,
                 mLayerTitleCacheSupplier.get(),
@@ -1453,11 +1454,6 @@
                                     offsetTagsInfo.getContentOffsetTag());
                         }
                     }
-
-                    @Override
-                    public void onContentViewScrollingStateChanged(boolean scrolling) {
-                        mIsVerticalScrollInProgress = scrolling;
-                    }
                 };
 
         mTabModelSelector.getCurrentTabModelSupplier().addObserver(mCurrentTabModelObserver);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchHeuristics.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchHeuristics.java
index ff45bfa..7b8451d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchHeuristics.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchHeuristics.java
@@ -19,7 +19,7 @@
 
     /** Manages a set of heuristics. */
     ContextualSearchHeuristics() {
-        mHeuristics = new HashSet<ContextualSearchHeuristic>();
+        mHeuristics = new HashSet<>();
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
index 4919616..1797307 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -131,8 +131,7 @@
     // A separator that we expect in the title of a dictionary response.
     private static final String DEFINITION_MID_DOT = "\u00b7";
 
-    private final ObserverList<ContextualSearchObserver> mObservers =
-            new ObserverList<ContextualSearchObserver>();
+    private final ObserverList<ContextualSearchObserver> mObservers = new ObserverList<>();
 
     private final Activity mActivity;
     private final Profile mProfile;
@@ -1900,7 +1899,7 @@
             return queries;
         }
 
-        List<String> relatedSearches = new ArrayList<String>(queries.size() + 1);
+        List<String> relatedSearches = new ArrayList<>(queries.size() + 1);
         relatedSearches.add(defaultSearch);
         relatedSearches.addAll(queries);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
index 85ae086..8ac3773 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
@@ -109,7 +109,7 @@
         if (isReadAloudTapToSeekEnabled()) {
             mReadAloudControllerSupplier = getReadAloudControllerSupplier(tab);
             if (mReadAloudControllerSupplier != null) {
-                new OneShotCallback<ReadAloudController>(
+                new OneShotCallback<>(
                         mReadAloudControllerSupplier, this::onReadAloudControllerSupplierReady);
             }
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/RelatedSearchesList.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/RelatedSearchesList.java
index c8e99ab..d7d5fee 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/RelatedSearchesList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/RelatedSearchesList.java
@@ -58,12 +58,13 @@
 
     /**
      * Returns a list of queries. This implementation may change based on whether we're showing
-     * suggestions in more than one place or not. This just returns the "default" list with
-     * the current interpretation of that concept.
+     * suggestions in more than one place or not. This just returns the "default" list with the
+     * current interpretation of that concept.
+     *
      * @return A {@code List<String>} of search suggestions.
      */
     List<String> getQueries() {
-        List<String> results = new ArrayList<String>();
+        List<String> results = new ArrayList<>();
         JSONArray suggestions = getSuggestions();
         if (suggestions == null) return results;
         for (int i = 0; i < suggestions.length(); i++) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
index 2410e289..378b232 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
@@ -817,10 +817,6 @@
             getCustomTabActivityTabController().destroy();
         }
 
-        if (mBrowserControlsVisibilityManager != null) {
-            mBrowserControlsVisibilityManager.destroy();
-        }
-
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM
                 && mAppHeaderCoordinator != null) {
             mAppHeaderCoordinator.destroy();
@@ -1488,7 +1484,6 @@
                         getCustomTabActivityTabProvider(),
                         getCustomTabToolbarCoordinator(),
                         getCloseButtonVisibilityManager(),
-                        getAppHeaderCoordinator(),
                         getIntentDataProvider());
         return mBrowserControlsVisibilityManager;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java
index 4ae4dd8..24aca31 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java
@@ -389,6 +389,7 @@
         return false;
     }
 
+    @ExperimentalOpenInBrowser
     @Override
     protected void initializeToolbar() {
         CustomTabsConnection connection = CustomTabsConnection.getInstance();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParamsImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParamsImpl.java
index 274606f7..c67aaffc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParamsImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParamsImpl.java
@@ -21,13 +21,13 @@
 import android.view.ViewGroup;
 import android.widget.ImageButton;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.browser.customtabs.CustomTabsIntent;
 
 import org.chromium.base.IntentUtils;
 import org.chromium.base.Log;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.browserservices.intents.CustomButtonParams;
 import org.chromium.chrome.browser.theme.ThemeUtils;
@@ -41,10 +41,11 @@
 import java.util.Set;
 
 /** Container for all parameters related to creating a customizable button. */
+@NullMarked
 public class CustomButtonParamsImpl implements CustomButtonParams {
     private static final String TAG = "CustomTabs";
 
-    private final PendingIntent mPendingIntent;
+    private final @Nullable PendingIntent mPendingIntent;
     private final int mId;
     private Bitmap mIcon;
     private String mDescription;
@@ -74,7 +75,7 @@
 
     /** Replaces the current icon and description with new ones. */
     @Override
-    public void update(@NonNull Bitmap icon, @NonNull String description) {
+    public void update(Bitmap icon, String description) {
         mIcon = icon;
         mDescription = description;
     }
@@ -131,7 +132,7 @@
      * @return The {@link PendingIntent} that will be sent when user clicks the customized button.
      */
     @Override
-    public PendingIntent getPendingIntent() {
+    public @Nullable PendingIntent getPendingIntent() {
         return mPendingIntent;
     }
 
@@ -255,7 +256,7 @@
     private static List<CustomButtonParams> addToParamListfromBundleList(
             List<CustomButtonParams> paramsList,
             Context context,
-            List<Bundle> bundleList,
+            @Nullable List<Bundle> bundleList,
             boolean tinted) {
         if (bundleList != null) {
             Set<Integer> ids = new HashSet<>();
@@ -280,7 +281,7 @@
      * @param fromList Whether the bundle is contained in a list or it is the single bundle that
      *                 directly comes from the intent.
      */
-    private static CustomButtonParams fromBundle(
+    private static @Nullable CustomButtonParams fromBundle(
             Context context, Bundle bundle, boolean tinted, boolean fromList) {
         if (bundle == null) return null;
 
@@ -386,7 +387,7 @@
      * @return The bitmap contained in the given {@link Bundle}. Will return null if input is
      *     invalid.
      */
-    static Bitmap parseBitmapFromBundle(Bundle bundle) {
+    static @Nullable Bitmap parseBitmapFromBundle(Bundle bundle) {
         if (bundle == null) return null;
         Bitmap bitmap = IntentUtils.safeGetParcelable(bundle, CustomTabsIntent.KEY_ICON);
         if (bitmap == null) return null;
@@ -408,7 +409,7 @@
      * @return The content description contained in the given {@link Bundle}. Will return null if
      *         input is invalid.
      */
-    static String parseDescriptionFromBundle(Bundle bundle) {
+    static @Nullable String parseDescriptionFromBundle(Bundle bundle) {
         if (bundle == null) return null;
         String description = IntentUtils.safeGetString(bundle, CustomTabsIntent.KEY_DESCRIPTION);
         if (TextUtils.isEmpty(description)) return null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAdaptiveToolbarBehavior.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAdaptiveToolbarBehavior.java
index 8496488..2fed85d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAdaptiveToolbarBehavior.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAdaptiveToolbarBehavior.java
@@ -15,6 +15,7 @@
 import androidx.browser.customtabs.ExperimentalOpenInBrowser;
 
 import org.chromium.base.supplier.Supplier;
+import org.chromium.build.annotations.NullMarked;
 import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider;
 import org.chromium.chrome.browser.browserservices.intents.CustomButtonParams;
@@ -32,6 +33,7 @@
 import java.util.Set;
 
 /** Implements CustomTab-specific behavior of adaptive toolbar button. */
+@NullMarked
 public class CustomTabAdaptiveToolbarBehavior implements AdaptiveToolbarBehavior {
     private final Context mContext;
     private final ActivityTabProvider mActivityTabProvider;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabCookiesFetcher.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabCookiesFetcher.java
index 642518a..c08326c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabCookiesFetcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabCookiesFetcher.java
@@ -4,13 +4,13 @@
 
 package org.chromium.chrome.browser.customtabs;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.Log;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
+import org.chromium.build.annotations.NullMarked;
 import org.chromium.chrome.browser.cookies.CookiesFetcher;
 import org.chromium.chrome.browser.crypto.CipherFactory;
 import org.chromium.chrome.browser.profiles.ProfileProvider;
@@ -23,6 +23,7 @@
 /**
  * Handles the Custom Tab specific behaviors of cookies persistence for off the record Custom tabs.
  */
+@NullMarked
 public class CustomTabCookiesFetcher extends CookiesFetcher {
     @VisibleForTesting static final String COOKIE_FILE_PREFIX = "COOKIES_";
     @VisibleForTesting static final String COOKIE_FILE_EXTENSION = ".DAT";
@@ -65,7 +66,7 @@
     }
 
     @Override
-    public void restoreCookies(@NonNull Runnable restoreCompletedAction) {
+    public void restoreCookies(Runnable restoreCompletedAction) {
         super.restoreCookies(restoreCompletedAction);
 
         PostTask.postTask(TaskTraits.BEST_EFFORT_MAY_BLOCK, this::cleanupUnneededCookieFiles);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
index 1c0bd28..fc0de1e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
@@ -342,6 +342,7 @@
     private PendingIntent.OnFinished mOnFinishedForTesting;
     private @DisplayMode.EnumType int mResolvedDisplayMode = DisplayMode.UNDEFINED;
     private final @OpenInBrowserState int mOpenInBrowserState;
+    private final int mShareState;
 
     /** Whether this CustomTabActivity was explicitly started by another Chrome Activity. */
     private final boolean mIsOpenedByChrome;
@@ -581,6 +582,19 @@
                         intent,
                         EXTRA_OPEN_IN_BROWSER_STATE,
                         OpenInBrowserButtonState.OPEN_IN_BROWSER_STATE_DEFAULT);
+        if (mUiType == CustomTabsUiType.POPUP) {
+            mShareState = CustomTabsIntent.SHARE_STATE_OFF;
+        } else {
+            boolean usingInteractiveOmnibox =
+                    CustomTabsConnection.getInstance().shouldEnableOmniboxForIntent(this);
+            int defState =
+                    usingInteractiveOmnibox
+                            ? CustomTabsIntent.SHARE_STATE_OFF
+                            : CustomTabsIntent.SHARE_STATE_DEFAULT;
+            mShareState =
+                    IntentUtils.safeGetIntExtra(
+                            intent, CustomTabsIntent.EXTRA_SHARE_STATE, defState);
+        }
 
         List<Bundle> menuItems =
                 IntentUtils.getParcelableArrayListExtra(intent, CustomTabsIntent.EXTRA_MENU_ITEMS);
@@ -914,19 +928,7 @@
      * </ul>
      */
     private void addShareOption(Intent intent, Context context) {
-        boolean usingInteractiveOmnibox =
-                CustomTabsConnection.getInstance().shouldEnableOmniboxForIntent(this);
-        int shareState =
-                IntentUtils.safeGetIntExtra(
-                        intent,
-                        CustomTabsIntent.EXTRA_SHARE_STATE,
-                        usingInteractiveOmnibox
-                                ? CustomTabsIntent.SHARE_STATE_OFF
-                                : CustomTabsIntent.SHARE_STATE_DEFAULT);
-        if (mUiType == CustomTabsUiType.POPUP) {
-            shareState = CustomTabsIntent.SHARE_STATE_OFF;
-        }
-        if (shareState == CustomTabsIntent.SHARE_STATE_DEFAULT) {
+        if (mShareState == CustomTabsIntent.SHARE_STATE_DEFAULT) {
             if (mToolbarButtons.isEmpty()) {
                 mToolbarButtons.add(
                         CustomButtonParamsImpl.createShareButton(
@@ -934,7 +936,7 @@
             } else if (mMenuEntries.isEmpty()) {
                 mShowShareItemInMenu = true;
             }
-        } else if (shareState == CustomTabsIntent.SHARE_STATE_ON) {
+        } else if (mShareState == CustomTabsIntent.SHARE_STATE_ON) {
             if (mToolbarButtons.isEmpty()) {
                 mToolbarButtons.add(
                         CustomButtonParamsImpl.createShareButton(
@@ -1455,7 +1457,7 @@
             return mAllTrustedWebActivityOrigins;
         }
 
-        mAllTrustedWebActivityOrigins = new HashSet<Origin>();
+        mAllTrustedWebActivityOrigins = new HashSet<>();
         Origin initialOrigin = Origin.create(getUrlToLoad());
         if (initialOrigin != null) mAllTrustedWebActivityOrigins.add(initialOrigin);
         if (mTrustedWebActivityAdditionalOrigins != null) {
@@ -1714,6 +1716,11 @@
         return mOpenInBrowserState;
     }
 
+    @Override
+    public int getShareButtonState() {
+        return mShareState;
+    }
+
     private @DisplayMode.EnumType int resolveDisplayMode() {
         TrustedWebActivityDisplayMode displayMode = getProvidedTwaDisplayMode();
         if (displayMode == null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsClientFileProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsClientFileProcessor.java
index 22709a15..8a42496 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsClientFileProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsClientFileProcessor.java
@@ -8,24 +8,26 @@
 import android.net.Uri;
 import android.os.Bundle;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.WorkerThread;
 import androidx.browser.customtabs.CustomTabsService;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.FileUtils;
 import org.chromium.base.Log;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.browserservices.intents.SessionHolder;
 import org.chromium.chrome.browser.browserservices.ui.splashscreen.trustedwebactivity.SplashImageHolder;
 
 /** Processes the files received via Custom Tab connection from client apps. */
+@NullMarked
 public class CustomTabsClientFileProcessor {
 
     private static final String TAG = "CustomTabFiles";
 
     private boolean mTwaSplashImageHolderCreated;
 
-    private static CustomTabsClientFileProcessor sInstance;
+    private static @Nullable CustomTabsClientFileProcessor sInstance;
 
     public static CustomTabsClientFileProcessor getInstance() {
         if (sInstance == null) sInstance = new CustomTabsClientFileProcessor();
@@ -64,7 +66,7 @@
     }
 
     /** Cleans up files associated with the session that has been disconnected. */
-    public void onSessionDisconnected(@NonNull SessionHolder<?> session) {
+    public void onSessionDisconnected(SessionHolder<?> session) {
         if (mTwaSplashImageHolderCreated && session.isCustomTab()) {
             // If the image still hasn't been claimed, delete it.
             SplashImageHolder.getInstance().takeImage(session);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabSnapshotController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabSnapshotController.java
index 19772af..2531696 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabSnapshotController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabSnapshotController.java
@@ -6,15 +6,15 @@
 
 import android.app.Activity;
 
-import androidx.annotation.NonNull;
-
 import org.chromium.base.supplier.Supplier;
+import org.chromium.build.annotations.NullMarked;
 import org.chromium.chrome.browser.incognito.IncognitoSnapshotController;
 
 /**
  * This is the controller that prevents incognito tabs from being visible in Android Recents for
  * {@link CustomTabActivity}.
  */
+@NullMarked
 public class IncognitoCustomTabSnapshotController extends IncognitoSnapshotController {
     /**
      * @param activity The {@link Activity} on which the snapshot capability needs to be controlled.
@@ -22,7 +22,7 @@
      *     showing Incognito or not currently.
      */
     IncognitoCustomTabSnapshotController(
-            @NonNull Activity activity, @NonNull Supplier<Boolean> isShowingIncognitoSupplier) {
+            Activity activity, Supplier<Boolean> isShowingIncognitoSupplier) {
         super(activity, isShowingIncognitoSupplier);
         updateIncognitoTabSnapshotState();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabProvider.java
index e436b5485..b6ecef0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabProvider.java
@@ -4,11 +4,10 @@
 
 package org.chromium.chrome.browser.customtabs.content;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
 import org.chromium.base.ObserverList;
 import org.chromium.base.supplier.Supplier;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
 import org.chromium.chrome.browser.tab.Tab;
@@ -20,12 +19,13 @@
  * navigating by links with target="_blank". Thus it is a single source of truth about the current
  * Tab of a Custom Tab activity.
  */
-public class CustomTabActivityTabProvider implements Supplier<Tab> {
+@NullMarked
+public class CustomTabActivityTabProvider implements Supplier<@Nullable Tab> {
     private final ObserverList<Observer> mObservers = new ObserverList<>();
 
-    @Nullable private Tab mTab;
+    private @Nullable Tab mTab;
     private @TabCreationMode int mTabCreationMode = TabCreationMode.NONE;
-    @Nullable private String mSpeculatedUrl;
+    private @Nullable String mSpeculatedUrl;
 
     public CustomTabActivityTabProvider(String speculatedUrl) {
         mSpeculatedUrl = speculatedUrl;
@@ -73,7 +73,7 @@
         return mSpeculatedUrl;
     }
 
-    public void setInitialTab(@NonNull Tab tab, @TabCreationMode int creationMode) {
+    public void setInitialTab(Tab tab, @TabCreationMode int creationMode) {
         assert mTab == null;
         assert creationMode != TabCreationMode.NONE;
         mTab = tab;
@@ -99,7 +99,7 @@
         if (mTab == tab) return;
         assert mTab != null : "swapTab shouldn't be called before setInitialTab";
         mTab = tab;
-        if (mTab == null) {
+        if (tab == null) {
             for (Observer observer : mObservers) {
                 observer.onAllTabsClosed();
             }
@@ -116,13 +116,13 @@
      */
     public abstract static class Observer {
         /** Fired when the initial tab has been created. */
-        public void onInitialTabCreated(@NonNull Tab tab, @TabCreationMode int mode) {}
+        public void onInitialTabCreated(Tab tab, @TabCreationMode int mode) {}
 
         /**
          * Fired when the currently visible tab has changed when navigating by a link with
          * target="_blank" or backwards.
          */
-        public void onTabSwapped(@NonNull Tab tab) {}
+        public void onTabSwapped(Tab tab) {}
 
         /**
          * Fired when all the Tabs are closed (during shutdown or reparenting).
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/RealtimeEngagementSignalObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/RealtimeEngagementSignalObserver.java
index 8259c10..c6ec8bb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/RealtimeEngagementSignalObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/RealtimeEngagementSignalObserver.java
@@ -338,7 +338,7 @@
             long currentTimeInNanos = SystemClock.elapsedRealtimeNanos();
 
             if (ChromeFeatureList.sCctRealtimeEngagementEventsInBackground.isEnabled()) {
-                new BackgroundOnlyAsyncTask<Void>() {
+                new BackgroundOnlyAsyncTask<>() {
                     @Override
                     protected Void doInBackground() {
                         mCallback.onVerticalScrollEvent(isDirectionUp, Bundle.EMPTY);
@@ -365,7 +365,7 @@
         try {
             long currentTimeInNanos = SystemClock.elapsedRealtimeNanos();
             if (ChromeFeatureList.sCctRealtimeEngagementEventsInBackground.isEnabled()) {
-                new BackgroundOnlyAsyncTask<Void>() {
+                new BackgroundOnlyAsyncTask<>() {
                     @Override
                     protected Void doInBackground() {
                         mCallback.onGreatestScrollPercentageIncreased(
@@ -397,7 +397,7 @@
         try {
             long currentTimeInNanos = SystemClock.elapsedRealtimeNanos();
             if (ChromeFeatureList.sCctRealtimeEngagementEventsInBackground.isEnabled()) {
-                new BackgroundOnlyAsyncTask<Void>() {
+                new BackgroundOnlyAsyncTask<>() {
                     @Override
                     protected Void doInBackground() {
                         mCallback.onSessionEnded(didGetUserInteraction, Bundle.EMPTY);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/WebAppLaunchHandlerHistogram.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/WebAppLaunchHandlerHistogram.java
index f9256ba9..57ce4f9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/WebAppLaunchHandlerHistogram.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/WebAppLaunchHandlerHistogram.java
@@ -7,6 +7,7 @@
 import androidx.annotation.IntDef;
 
 import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.build.annotations.NullMarked;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -15,6 +16,7 @@
  * Records a histogram that tracks usage of Launch Handler API. All actions must be kept in sync
  * with the definition in tools/metrics/histograms/metadata/custom_tabs/enums.xml.
  */
+@NullMarked
 public class WebAppLaunchHandlerHistogram {
     private WebAppLaunchHandlerHistogram() {}
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dagger.md b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dagger.md
deleted file mode 100644
index d851cc6..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dagger.md
+++ /dev/null
@@ -1,130 +0,0 @@
-# Dagger in Custom Tabs
-
-The Custom Tabs code (code in org.chromium.chrome.browser.customtabs, ...browserservices and ...webapps) makes use of Dagger for dependency injection.
-It was introduced with the hope that it would be more widely adopted in Chrome, but due to other ongoing refactorings it was decided not to spread it further until a cohesive end state could be understood.
-
-This document isn’t about the pros and cons of Dagger or an argument about whether it should be pushed further or ripped out, it is meant as a quick way for developers to understand what’s going on in Custom Tabs land and how to make changes.
-
-## What is Dagger?
-
-Dagger is a dependency injection framework.
-You can essentially think of it as a magic box that creates your classes and wires them together for you.
-For example:
-
-```java
-@ActivityScope
-public class Programmer {
-  private final Coffee mCoffee;
-
-  @Inject
-  public Programmer(Coffee coffee) {
-    mCoffee = coffee;
-  }
-}
-```
-
-If you ask for a `Programmer`, `Dagger` will do its best to find a `Coffee` and then create a `Programmer` for you.
-
-There are two annotations in the above example, `@ActivityScope` and `@Inject`.
-`@Inject` is the simpler one and it tells Dagger that this is the constructor it should use (Dagger won’t create objects without an `@Inject` constructor).
-`@ActivityScope` tells Dagger about the life cycle of the object - there should be one instance of this object per Android Activity.
-This is probably the one you’ll be using most of the time, but there is also `@Singleton` which can be used for singletons and a couple of others.
-
-### Modules and Components
-
-The above is all very well and good, but how does non-Dagger code interact with the magic box that is Dagger?
-We need to introduce two more concepts, *Modules* and *Components*.
-(In the “I want to…” section below I’ve linked to the Modules and Components used in Chrome if you want to see a less contrived example.)
-
-A *Module* is a way of giving objects to Dagger.
-Eg:
-
-```java
-@Module
-public class MyDaggerModule {
-  private final Coffee mCoffee;
-
-  public MyDaggerModule(Coffee coffee) {
-    mCoffee = coffee;
-  }
-
-  @Provides
-  public Coffee provideCoffee() {
-    return mCoffee;
-  }
-}
-```
-
-In non-Dagger code you would create a `MyDaggerModule` and provide it with a `Coffee` (that you created yourself).
-Armed with this, Dagger can now go and create the `Programmer`.
-
-On the other side we have *Components*, which are how you get objects out of `Dagger`.
-
-```java
-@ActivityScope
-public interface MyDaggerComponent {
-  Programmer resolveProgrammer();
-}
-```
-
-You write this interface, and then during the build process, Dagger will go and implement all of these methods for you.
-When you call `resolveProgrammer`, Dagger will create a `Programmer` if it doesn’t already have one and give it to you.
-That’s the basics of Dagger, let’s look at a few changes you may want to make and how to go about them.
-
-## I want to...
-
-### access a Dagger class from a Dagger class
-
-This is pretty simple, say you want to access `Foo` from `Bar` and both of them are constructed by Dagger.
-You add `Foo` to `Bar`’s constructor and Dagger should sort it all out for you.
-
-Dagger will complain if the life cycles don’t match up (for example, you’re trying to access a `@ActivityScope` class from a `@Singleton`), and in that case you’ll have to rethink how to do things.
-
-If there’s a circular dependency, take `Lazy<Foo>` instead of `Foo`.
-A `Lazy<T>` will be resolved the first time it is accessed.
-
-`Lazy` can also be used when you want Dagger to provide a class that won't be ready at the time it
-needs to be injected. For example,`ChromeActivityCommonsModule` contains a bunch of
-`Supplier<Foo>`s that will only return non-null once some stage of startup has been completed.
-To use `Foo` in a class that's created earlier than it, take a `Lazy<Foo>` in the constructor.
-
-### access a non-Dagger class from a Dagger class
-
-There are quite a few non-Dagger classes already provided to Dagger, so see if the class you want is provided in any of the Modules:
-
-* [BaseCustomTabActivityModule][1] - used for anything available to BaseCustomTabActivity.
-* [ChromeActivityCommonsModule][2] - used for anything available to ChromeActivity.
-* [ChromeAppModule][3] - used for singletons.
-
-If not, add it to the appropriate Module and then take it in the constructor of the class you want to access it from.
-
-### access a Dagger class from a non-Dagger class
-
-If the class is a singleton, add a suitable *resolve* method to [ChromeAppComponent][4], you can then call `ChromeApplicationImpl.getComponent().resolveMyClass()` to get access.
-
-If the class is `@ActivityScope`, add a suitable resolve method to [BaseCustomTabActivityComponent][5], and then get the object out of Dagger in `BaseCustomTabActivity#createComponent`.
-You can then fetch the instance off `BaseCustomTabActivity`.
-
-### override a class in unit tests
-
-You shouldn't need to interact with Dagger in unit tests, since you'll be constructing the class yourself.
-Since Dagger encourages injecting a class' dependencies in the constructor, it should make it easier to create an instance for testing.
-Most likely you'll want to use fakes or mocks for the class' dependencies.
-
-### override a class in instrumentation tests
-
-You can use a [ModuleOverridesRule][6] which will allow you to override the instances of class passed in to Dagger.
-For example, see the [RunningInChromeTest][7].
-
-## Finals words
-
-Hopefully that should be enough to help you make changes to Custom Tabs code.
-If you need to do something more complicated, feel free to reach out to peconn@, either by email or by adding me on your code review.
-
-[1]: https://source.chromium.org/chromium/chromium/src/+/main:chrome/android/java/src/org/chromium/chrome/browser/customtabs/dependency_injection/BaseCustomTabActivityModule.java
-[2]: https://source.chromium.org/chromium/chromium/src/+/main:chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityCommonsModule.java
-[3]: https://source.chromium.org/chromium/chromium/src/+/main:chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppModule.java
-[4]: https://source.chromium.org/chromium/chromium/src/+/main:chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppComponent.java
-[5]: https://source.chromium.org/chromium/chromium/src/+/main:chrome/android/java/src/org/chromium/chrome/browser/customtabs/dependency_injection/BaseCustomTabActivityComponent.java
-[6]: https://source.chromium.org/chromium/chromium/src/+/main:chrome/android/javatests/src/org/chromium/chrome/browser/dependency_injection/ModuleOverridesRule.java
-[7]: https://source.chromium.org/chromium/chromium/src/+/main:chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/RunningInChromeTest.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/ImmersiveModeController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/ImmersiveModeController.java
index 7365962..a11cd03 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/ImmersiveModeController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/ImmersiveModeController.java
@@ -19,6 +19,7 @@
 import androidx.core.view.WindowCompat;
 import androidx.core.view.WindowInsetsControllerCompat;
 
+import org.chromium.build.annotations.NullMarked;
 import org.chromium.chrome.browser.display_cutout.ActivityDisplayCutoutModeSupplier;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.DestroyObserver;
@@ -26,6 +27,7 @@
 import org.chromium.ui.base.WindowAndroid;
 
 /** Allows to enter and exit immersive mode in TWAs and WebAPKs. */
+@NullMarked
 public class ImmersiveModeController implements WindowFocusChangedObserver, DestroyObserver {
     private static final int ENTER_IMMERSIVE_MODE_ON_WINDOW_FOCUS_DELAY_MILLIS = 300;
     private static final int RESTORE_IMMERSIVE_MODE_DELAY_MILLIS = 3000;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/minimizedcustomtab/MinimizedCardViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/minimizedcustomtab/MinimizedCardViewBinder.java
index a5b4498..8d36850 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/minimizedcustomtab/MinimizedCardViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/minimizedcustomtab/MinimizedCardViewBinder.java
@@ -15,6 +15,7 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import org.chromium.build.annotations.NullMarked;
 import org.chromium.chrome.R;
 import org.chromium.ui.UiUtils;
 import org.chromium.ui.base.ViewUtils;
@@ -22,6 +23,7 @@
 import org.chromium.ui.modelutil.PropertyModel;
 
 /** View binder for the minimized card. */
+@NullMarked
 public class MinimizedCardViewBinder {
     public static void bind(PropertyModel model, View view, PropertyKey key) {
         if (TITLE == key) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/minimizedcustomtab/MinimizedCustomTabIphController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/minimizedcustomtab/MinimizedCustomTabIphController.java
index 5c38628..2af1a06 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/minimizedcustomtab/MinimizedCustomTabIphController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/minimizedcustomtab/MinimizedCustomTabIphController.java
@@ -8,6 +8,8 @@
 import android.view.View;
 
 import org.chromium.base.supplier.Supplier;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.ActivityTabProvider.ActivityTabTabObserver;
@@ -23,13 +25,14 @@
 import org.chromium.url.GURL;
 
 /** Controls showing IPH for Minimized Custom Tabs. */
+@NullMarked
 public class MinimizedCustomTabIphController
         implements MinimizedCustomTabFeatureEngagementDelegate {
     private final Activity mActivity;
     private final ActivityTabProvider mTabProvider;
     private final UserEducationHelper mUserEducationHelper;
     private final Supplier<Profile> mProfileSupplier;
-    private ActivityTabTabObserver mTabObserver;
+    private @Nullable ActivityTabTabObserver mTabObserver;
 
     /**
      * Constructs the controller.
@@ -99,7 +102,7 @@
                         .build());
     }
 
-    ActivityTabTabObserver getTabObserverForTesting() {
+    @Nullable ActivityTabTabObserver getTabObserverForTesting() {
         return mTabObserver;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabTabObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabTabObserver.java
index ae0d013..116e351 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabTabObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabTabObserver.java
@@ -5,6 +5,8 @@
 package org.chromium.chrome.browser.customtabs.features.partialcustomtab;
 
 import org.chromium.base.Callback;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.content_public.browser.ImeAdapter;
@@ -15,10 +17,11 @@
  * {@link PartialCustomTabInputMethodManagerWrapper}. This lets the tab detect the event of
  * soft keyboard showing up.
  */
+@NullMarked
 public class PartialCustomTabTabObserver extends EmptyTabObserver {
     private final Callback<Runnable> mShowSoftInputCallback;
-    private PartialCustomTabInputMethodWrapper mImmWrapper;
-    private Tab mCurrentTab;
+    private @Nullable PartialCustomTabInputMethodWrapper mImmWrapper;
+    private @Nullable Tab mCurrentTab;
 
     /**
      * @param showSoftInputCallback Callback to invoke when {@link #onShowSoftInput}
@@ -46,8 +49,12 @@
         updateImmWrapper(tab);
     }
 
+    // Suppress NullAway since |mImmWrapper| might be null, but it's unclear what to do in this case
+    // and it wouldn't immediately crash.
+    @SuppressWarnings("NullAway")
     private void updateImmWrapper(Tab tab) {
         WebContents webContents = tab.getWebContents();
+        assert webContents != null;
         ImeAdapter imeAdapter = ImeAdapter.fromWebContents(webContents);
         imeAdapter.setInputMethodManagerWrapper(mImmWrapper);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/ButtonVisibilityRule.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/ButtonVisibilityRule.java
index c6a14ed..ba0d0e8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/ButtonVisibilityRule.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/ButtonVisibilityRule.java
@@ -10,6 +10,8 @@
 import androidx.annotation.Px;
 
 import org.chromium.build.annotations.NullMarked;
+import org.chromium.chrome.browser.browserservices.intents.CustomButtonParams.ButtonType;
+import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider.CustomTabsButtonState;
 
 /**
  * Button visibility checker using the rule set based on priority. The rule checker works only when
@@ -41,21 +43,31 @@
     // no-op, meaning the rule set will be enforced by some other logic.
     private final boolean mActivated;
 
+    @CustomTabsButtonState int mShareState;
+    @CustomTabsButtonState int mOpenInBrowserState;
+
     // The current toolbar width. It can change over time, for instance, due to
     // device rotation or the window width adjustment in multi-window mode.
     private int mToolbarWidth;
 
     static class Button {
         private final View mView;
+        // Type of the button. Valid for CUSTOM_1 or CUSTOM_2 only; the rest of the buttons always
+        // have ButtonType.OTHER.
+        private final @ButtonType int mCustomType;
+
+        // Visibility of the button. It can be hidden either because there is no space (handled by
+        // this class) or because of outside factors.
         private boolean mVisible;
 
         // True if the button view is suppressed to hidden state by this rule checker.
         // Only the buttons suppressed get turned on later again.
         private boolean mSuppressed;
 
-        Button(View view, boolean visible) {
+        Button(View view, boolean visible, @ButtonType int customType) {
             mView = view;
             mVisible = visible;
+            mCustomType = customType;
         }
     }
 
@@ -71,6 +83,19 @@
     }
 
     /**
+     * Set the state (default/on/off) of Share/Open-in-Browser custom button. These states are used
+     * to determine the relative priority of the custom action button and the minimize button.
+     *
+     * @param shareState State of Share action.
+     * @param openInBrowserState State of Open-in-browser action.
+     */
+    public void setCustomButtonState(
+            @CustomTabsButtonState int shareState, @CustomTabsButtonState int openInBrowserState) {
+        mShareState = shareState;
+        mOpenInBrowserState = openInBrowserState;
+    }
+
+    /**
      * Set the new toolbar width and apply the update.
      *
      * @param width The updated width of the toolbar.
@@ -95,9 +120,23 @@
      * @param visible {@code true} if the button is to be visible.
      */
     public void addButton(int index, View view, boolean visible) {
+        addButtonForCustomAction(index, view, visible, ButtonType.OTHER);
+    }
+
+    /**
+     * Add a button for custom action with its button type.
+     *
+     * @param index Index of the button.
+     * @param view {@link View} of the button to which the visibility is applied.
+     * @param visible {@code true} if the button is to be visible.
+     * @param customType Button type if this is custom action (CUSTOM_1 or CUSTOM_2). Otherwise
+     *     {@code ButtonType.OTHER}.
+     */
+    public void addButtonForCustomAction(
+            int index, View view, boolean visible, @ButtonType int customType) {
         if (!mActivated) return;
 
-        mButtons.put(index, new Button(view, visible));
+        mButtons.put(index, new Button(view, visible, customType));
         if (mToolbarWidth > 0 && visible) refresh();
     }
 
@@ -138,10 +177,47 @@
             button.mSuppressed = true;
             urlBarWidth = getUrlBarWidth();
         }
+        adjustMinimizeButtonPriority();
         assert urlBarWidth >= mMinUrlWidthPx || isAllButtonHidden()
                 : "There is not enough space for URL bar!!!!";
     }
 
+    private void adjustMinimizeButtonPriority() {
+        // Chrome-created custom buttons (Share, Open-in-Chrome) of state DEFAULT has a priority
+        // lower than minimize button i.e. MINIMIZE > SHARE > OPEN-IN-CHROME > EXPAND. If MINIMIZE
+        // was suppressed hidden and either SHARE or OPEN-IN-CHROME is visible, flip their state.
+        Button minimize = mButtons.get(ButtonId.MINIMIZE);
+        if (minimize != null && !minimize.mVisible && minimize.mSuppressed) {
+            if (maybeSuppressCustomButtonOfType(
+                            ButtonType.CCT_OPEN_IN_BROWSER_BUTTON, mOpenInBrowserState)
+                    || maybeSuppressCustomButtonOfType(ButtonType.CCT_SHARE_BUTTON, mShareState)) {
+                minimize.mVisible = true;
+                minimize.mSuppressed = false;
+                minimize.mView.setVisibility(View.VISIBLE);
+            }
+        }
+    }
+
+    private boolean maybeSuppressCustomButtonOfType(@ButtonType int buttonType, int buttonState) {
+        return maybeSuppressSingleCustomButton(ButtonId.CUSTOM_2, buttonType, buttonState)
+                || maybeSuppressSingleCustomButton(ButtonId.CUSTOM_1, buttonType, buttonState);
+    }
+
+    private boolean maybeSuppressSingleCustomButton(
+            @ButtonId int id, @ButtonType int buttonType, @CustomTabsButtonState int buttonState) {
+        Button button = mButtons.get(id);
+        if (button != null
+                && button.mVisible
+                && button.mCustomType == buttonType
+                && buttonState == CustomTabsButtonState.BUTTON_STATE_DEFAULT) {
+            button.mVisible = false;
+            button.mSuppressed = true;
+            button.mView.setVisibility(View.GONE);
+            return true;
+        }
+        return false;
+    }
+
     /**
      * Refresh the visibility when the toolbar width expands. Make some of the suppressed buttons
      * visible again if the width allows.
@@ -167,6 +243,7 @@
                 button.mSuppressed = false;
             }
         }
+        adjustMinimizeButtonPriority();
     }
 
     // Returns the current URL/title bar width, which is the toolbar width minus the total
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabHistoryIphController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabHistoryIphController.java
index 326743e..64fff216 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabHistoryIphController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabHistoryIphController.java
@@ -10,6 +10,8 @@
 import android.view.View;
 
 import org.chromium.base.supplier.Supplier;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.ActivityTabProvider.ActivityTabTabObserver;
@@ -27,13 +29,14 @@
 import org.chromium.url.GURL;
 
 /** Controls showing IPH for Custom Tabs history. */
+@NullMarked
 public class CustomTabHistoryIphController {
     private final Activity mActivity;
     private final ActivityTabProvider mTabProvider;
     private final Supplier<Profile> mProfileSupplier;
     private final AppMenuHandler mAppMenuHandler;
-    private ActivityTabTabObserver mTabObserver;
-    private UserEducationHelper mUserEducationHelper;
+    private @Nullable ActivityTabTabObserver mTabObserver;
+    private @Nullable UserEducationHelper mUserEducationHelper;
 
     /**
      * Constructs the controller.
@@ -116,7 +119,7 @@
         mAppMenuHandler.clearMenuHighlight();
     }
 
-    ActivityTabTabObserver getTabObserverForTesting() {
+    @Nullable ActivityTabTabObserver getTabObserverForTesting() {
         return mTabObserver;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java
index 98864f5..c499ed2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java
@@ -53,7 +53,9 @@
 import androidx.annotation.Px;
 import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.content.res.AppCompatResources;
+import androidx.browser.customtabs.CustomTabsIntent;
 import androidx.browser.customtabs.CustomTabsIntent.CloseButtonPosition;
+import androidx.browser.customtabs.ExperimentalOpenInBrowser;
 import androidx.core.view.MarginLayoutParamsCompat;
 import androidx.core.widget.ImageViewCompat;
 
@@ -69,7 +71,9 @@
 import org.chromium.chrome.browser.browser_controls.BrowserStateBrowserControlsVisibilityDelegate;
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider;
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider.CustomTabProfileType;
+import org.chromium.chrome.browser.browserservices.intents.CustomButtonParams.ButtonType;
 import org.chromium.chrome.browser.customtabs.CustomTabFeatureOverridesManager;
+import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider.CustomTabsButtonState;
 import org.chromium.chrome.browser.customtabs.features.CustomTabDimensionUtils;
 import org.chromium.chrome.browser.customtabs.features.branding.ToolbarBrandingDelegate;
 import org.chromium.chrome.browser.customtabs.features.branding.ToolbarBrandingOverlayCoordinator;
@@ -409,9 +413,35 @@
      * @param intentDataProvider {@link BrowserServicesIntentDataProvider} for accessing CCT intent
      *     data.
      */
+    @ExperimentalOpenInBrowser
     public void calculateToolbarWidthBeforeMeasure(
             Activity activity, BrowserServicesIntentDataProvider intentDataProvider) {
-        if (mIntentDataProvider == null) mIntentDataProvider = intentDataProvider;
+        if (mIntentDataProvider == null) {
+            mIntentDataProvider = intentDataProvider;
+            @CustomTabsButtonState
+            int shareState =
+                    switch (intentDataProvider.getShareButtonState()) {
+                        case CustomTabsIntent.SHARE_STATE_OFF -> CustomTabsButtonState
+                                .BUTTON_STATE_OFF;
+                        case CustomTabsIntent.SHARE_STATE_DEFAULT -> CustomTabsButtonState
+                                .BUTTON_STATE_DEFAULT;
+                        case CustomTabsIntent.SHARE_STATE_ON -> CustomTabsButtonState
+                                .BUTTON_STATE_ON;
+                        default -> CustomTabsButtonState.BUTTON_STATE_DEFAULT;
+                    };
+            @CustomTabsButtonState
+            int oibState =
+                    switch (intentDataProvider.getOpenInBrowserButtonState()) {
+                        case CustomTabsIntent.OPEN_IN_BROWSER_STATE_OFF -> CustomTabsButtonState
+                                .BUTTON_STATE_OFF;
+                        case CustomTabsIntent.OPEN_IN_BROWSER_STATE_DEFAULT -> CustomTabsButtonState
+                                .BUTTON_STATE_DEFAULT;
+                        case CustomTabsIntent.OPEN_IN_BROWSER_STATE_ON -> CustomTabsButtonState
+                                .BUTTON_STATE_ON;
+                        default -> CustomTabsButtonState.BUTTON_STATE_DEFAULT;
+                    };
+            mButtonVisibilityRule.setCustomButtonState(shareState, oibState);
+        }
         mButtonVisibilityRule.setToolbarWidth(
                 CustomTabDimensionUtils.getInitialWidth(activity, intentDataProvider));
     }
@@ -450,7 +480,7 @@
 
     @Override
     protected void addCustomActionButton(
-            Drawable drawable, String description, OnClickListener listener) {
+            Drawable drawable, String description, OnClickListener listener, @ButtonType int type) {
         if (ChromeFeatureList.sCctToolbarRefactor.isEnabled()) return;
 
         ImageButton button =
@@ -477,12 +507,12 @@
             // 16:act1:8 - 8:menu:16
             paddingStart = getDimensionPx(R.dimen.custom_tabs_toolbar_button_spacer_16);
             paddingEnd = getDimensionPx(R.dimen.custom_tabs_toolbar_button_spacer_8);
-            mButtonVisibilityRule.addButton(ButtonId.CUSTOM_1, button, true);
+            mButtonVisibilityRule.addButtonForCustomAction(ButtonId.CUSTOM_1, button, true, type);
         } else {
             // 24:act2:0 - 16:act1:8 - 8:menu:16
             paddingStart = getDimensionPx(R.dimen.custom_tabs_toolbar_button_spacer_24);
             paddingEnd = 0;
-            mButtonVisibilityRule.addButton(ButtonId.CUSTOM_2, button, true);
+            mButtonVisibilityRule.addButtonForCustomAction(ButtonId.CUSTOM_2, button, true, type);
 
             // The 2nd custom button disables optional button.
             mButtonVisibilityRule.addButton(ButtonId.MTB, /* view= */ null, /* visible= */ false);
@@ -1715,7 +1745,7 @@
 
             if (hideUrlBar && mState == STATE_DOMAIN_AND_TITLE) {
                 mState = STATE_TITLE_ONLY;
-                mAnimDelegate.setTitleAnimationEnabled(false);
+                mAnimDelegate.disableTitleAnimation();
                 mUrlBar.setVisibility(View.GONE);
                 mTitleBar.setVisibility(View.VISIBLE);
                 LayoutParams lp = (LayoutParams) mTitleBar.getLayoutParams();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarAnimationDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarAnimationDelegate.java
index a43acaf..6ebcf49 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarAnimationDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarAnimationDelegate.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.customtabs.features.toolbar;
 
+import static org.chromium.build.NullUtil.assumeNonNull;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
@@ -15,6 +17,9 @@
 import androidx.annotation.DimenRes;
 import androidx.annotation.DrawableRes;
 
+import org.chromium.build.annotations.EnsuresNonNullIf;
+import org.chromium.build.annotations.MonotonicNonNull;
+import org.chromium.build.annotations.NullMarked;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.ui.base.ViewUtils;
@@ -37,14 +42,15 @@
  * </p>
  * See {@link SecurityButtonAnimationDelegate} and {@link BrandingSecurityButtonAnimationDelegate}.
  */
+@NullMarked
 class CustomTabToolbarAnimationDelegate {
     private final SecurityButtonAnimationDelegate mSecurityButtonAnimationDelegate;
     private final BrandingSecurityButtonAnimationDelegate mBrandingAnimationDelegate;
     private final Runnable mAnimationEndRunnable;
     private final View mSecurityButtonOffsetTarget;
 
-    private TextView mUrlBar;
-    private TextView mTitleBar;
+    private @MonotonicNonNull TextView mUrlBar;
+    private @MonotonicNonNull TextView mTitleBar;
     // A flag controlling whether the animation has run before.
     private boolean mShouldRunTitleAnimation;
     private boolean mUseRotationTransition;
@@ -69,6 +75,7 @@
             new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
+                    assumeNonNull(mTitleBar);
                     mTitleBar
                             .animate()
                             .alpha(1f)
@@ -113,9 +120,9 @@
         mSecurityButtonAnimationDelegate.setSecurityButtonWidth(width);
     }
 
-    /** Sets whether the title scaling animation is enabled. */
-    void setTitleAnimationEnabled(boolean enabled) {
-        mShouldRunTitleAnimation = enabled;
+    /** Disables the title scaling animation. */
+    void disableTitleAnimation() {
+        mShouldRunTitleAnimation = false;
     }
 
     void prepareTitleAnim(TextView urlBar, TextView titleBar) {
@@ -126,12 +133,18 @@
         mShouldRunTitleAnimation = true;
     }
 
+    @EnsuresNonNullIf({"mTitleBar", "mUrlBar"})
+    @SuppressWarnings("NullAway")
+    private boolean shouldRunTitleAnimation() {
+        return mShouldRunTitleAnimation;
+    }
+
     /**
      * Starts animation for urlbar scaling and title fading-in. If this animation has already run
      * once, does nothing.
      */
     void startTitleAnimation(Context context) {
-        if (!mShouldRunTitleAnimation) return;
+        if (!shouldRunTitleAnimation()) return;
         mShouldRunTitleAnimation = false;
 
         var titleBar = mTitleBar;
@@ -208,7 +221,7 @@
      *     When this is null, the icon is animated to the left and faded out.
      */
     void updateSecurityButton(@DrawableRes int securityIconResource) {
-        if (mShouldRunTitleAnimation) {
+        if (shouldRunTitleAnimation()) {
             mPendingSecurityIconRes = securityIconResource;
             return;
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarCoordinator.java
index 199ab1f..8440370 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarCoordinator.java
@@ -214,7 +214,10 @@
         for (CustomButtonParams params : mIntentDataProvider.getCustomButtonsOnToolbar()) {
             View.OnClickListener onClickListener = v -> onCustomButtonClick(params);
             mToolbarManager.addCustomActionButton(
-                    params.getIcon(mActivity), params.getDescription(), onClickListener);
+                    params.getIcon(mActivity),
+                    params.getDescription(),
+                    onClickListener,
+                    params.getType());
         }
     }
 
@@ -292,7 +295,7 @@
                 /* bookmarkClickHandler= */ null,
                 /* customTabsBackClickHandler= */ v -> onCloseButtonClick(),
                 /* archivedTabCountSupplier= */ null,
-                /* tabModelNotificationDotSupplier= */ new ObservableSupplierImpl<TabModelDotInfo>(
+                /* tabModelNotificationDotSupplier= */ new ObservableSupplierImpl<>(
                         TabModelDotInfo.HIDE),
                 /* undoBarThrottle= */ null);
         mInitializedToolbarWithNative = true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/DistilledPagePrefsView.java b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/DistilledPagePrefsView.java
index 9383d5b..98454e8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/DistilledPagePrefsView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/DistilledPagePrefsView.java
@@ -116,8 +116,7 @@
             getResources().getString(R.string.monospace)
         };
         ArrayAdapter<CharSequence> adapter =
-                new ArrayAdapter<CharSequence>(
-                        getContext(), android.R.layout.simple_spinner_item, fonts) {
+                new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_item, fonts) {
                     @Override
                     public View getView(int position, View convertView, ViewGroup parent) {
                         View view = super.getView(position, convertView, parent);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
index 2aecc17..70db86d1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
@@ -89,7 +89,7 @@
     private static final long UPDATE_DELAY_MILLIS = 1000;
     public static final long UNKNOWN_BYTES_RECEIVED = -1;
 
-    private static final Set<String> sFirstSeenDownloadIds = new HashSet<String>();
+    private static final Set<String> sFirstSeenDownloadIds = new HashSet<>();
 
     private static DownloadManagerService sDownloadManagerService;
     private static boolean sIsNetworkListenerDisabled;
@@ -123,8 +123,7 @@
         default void broadcastDownloadSuccessful(DownloadInfo downloadInfo) {}
     }
 
-    @VisibleForTesting
-    protected final List<String> mAutoResumableDownloadIds = new ArrayList<String>();
+    @VisibleForTesting protected final List<String> mAutoResumableDownloadIds = new ArrayList<>();
 
     private final ObserverList<DownloadObserver> mDownloadObservers = new ObserverList<>();
 
@@ -486,7 +485,7 @@
         if (mIsUiUpdateScheduled) return;
 
         mIsUiUpdateScheduled = true;
-        final List<DownloadProgress> progressPendingUpdate = new ArrayList<DownloadProgress>();
+        final List<DownloadProgress> progressPendingUpdate = new ArrayList<>();
         Iterator<DownloadProgress> iter = mDownloadProgressMap.values().iterator();
         while (iter.hasNext()) {
             DownloadProgress progress = iter.next();
@@ -1230,7 +1229,7 @@
     // Deprecated after new download backend.
     @CalledByNative
     private List<DownloadItem> createDownloadItemList() {
-        return new ArrayList<DownloadItem>();
+        return new ArrayList<>();
     }
 
     // Deprecated after new download backend.
@@ -1399,7 +1398,7 @@
         assert (downloadItem.getDownloadInfo().state() == DownloadState.COMPLETE);
 
         AsyncTask<Boolean> task =
-                new AsyncTask<Boolean>() {
+                new AsyncTask<>() {
                     @Override
                     public Boolean doInBackground() {
                         DownloadInfo info = downloadItem.getDownloadInfo();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
index 89bf462..1d3e197 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
@@ -748,8 +748,7 @@
                         && ProfileManager.getLastUsedRegularProfile().hasPrimaryOtrProfile();
 
         List<DownloadSharedPreferenceEntry> entries = mDownloadSharedPreferenceHelper.getEntries();
-        List<DownloadSharedPreferenceEntry> copies =
-                new ArrayList<DownloadSharedPreferenceEntry>(entries);
+        List<DownloadSharedPreferenceEntry> copies = new ArrayList<>(entries);
         for (DownloadSharedPreferenceEntry entry : copies) {
             if (!OtrProfileId.isOffTheRecord(entry.otrProfileId)) continue;
             ContentId id = entry.id;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSharedPreferenceHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSharedPreferenceHelper.java
index 7c61e72a..d55bcc5c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSharedPreferenceHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSharedPreferenceHelper.java
@@ -25,7 +25,7 @@
     }
 
     private final List<DownloadSharedPreferenceEntry> mDownloadSharedPreferenceEntries =
-            new ArrayList<DownloadSharedPreferenceEntry>();
+            new ArrayList<>();
     private final ObserverList<Observer> mObservers = new ObserverList<>();
 
     private final SharedPreferencesManager mSharedPrefs;
@@ -171,10 +171,11 @@
 
     /**
      * Helper method to store all the SharedPreferences entries.
-     * @param forceCommit   Whether SharedPreferences should be updated synchronously.
+     *
+     * @param forceCommit Whether SharedPreferences should be updated synchronously.
      */
     private void storeDownloadSharedPreferenceEntries(boolean forceCommit) {
-        Set<String> entries = new HashSet<String>();
+        Set<String> entries = new HashSet<>();
         for (int i = 0; i < mDownloadSharedPreferenceEntries.size(); ++i) {
             entries.add(mDownloadSharedPreferenceEntries.get(i).getSharedPreferenceString());
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
index 043a7f9..2de9782 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
@@ -152,9 +152,8 @@
 
     private final Context mContext;
     private final SharedPreferencesManager mSharedPrefs;
-    private final LongSparseArray<DownloadItem> mSystemDownloadIdMap =
-            new LongSparseArray<DownloadItem>();
-    private final LongSparseArray<OMAInfo> mPendingOMADownloads = new LongSparseArray<OMAInfo>();
+    private final LongSparseArray<DownloadItem> mSystemDownloadIdMap = new LongSparseArray<>();
+    private final LongSparseArray<OMAInfo> mPendingOMADownloads = new LongSparseArray<>();
     private final ObserverList<TestObserver> mObservers = new ObserverList<>();
 
     /**
@@ -168,7 +167,7 @@
 
         OMAInfo() {
             mDescription = new HashMap<String, String>();
-            mTypes = new ArrayList<String>();
+            mTypes = new ArrayList<>();
         }
 
         /**
@@ -679,7 +678,7 @@
             OMAInfo info = new OMAInfo();
             StringBuilder sb = null;
             List<String> attributeList =
-                    new ArrayList<String>(
+                    new ArrayList<>(
                             Arrays.asList(
                                     OMA_TYPE,
                                     OMA_SIZE,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarManager.java
index 8313d5f37..7061d57 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarManager.java
@@ -44,7 +44,7 @@
         mWindowAndroid = windowAndroid;
         mCallback = callback;
         mBackPressManager = backPressManager;
-        mObservers = new ObserverList<FindToolbarObserver>();
+        mObservers = new ObserverList<>();
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java
index 0a5fa31f..bc140bd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java
@@ -685,6 +685,24 @@
     }
 
     @Override
+    public boolean isVisibilityForced() {
+        Tab tab = getTab();
+
+        // If the tab gets destroyed, we should be transitioning to a state
+        // where there are no tabs (in which case we show the grid tab
+        // switcher) or we change to another tab (in which case we force the
+        // controls to be visible for a while.)
+        if (tab != null && tab.isDestroyed()) {
+            return true;
+        }
+
+        @BrowserControlsState
+        int constraints = TabBrowserControlsConstraintsHelper.getConstraints(tab);
+        return constraints == BrowserControlsState.HIDDEN
+                || constraints == BrowserControlsState.SHOWN;
+    }
+
+    @Override
     public void setControlsPosition(
             @ControlsPosition int controlsPosition,
             int newTopControlsHeight,
@@ -895,13 +913,6 @@
         notifyControlOffsetChanged();
     }
 
-    private boolean isVisibilityForced() {
-        @BrowserControlsState
-        int constraints = TabBrowserControlsConstraintsHelper.getConstraints(getTab());
-        return constraints == BrowserControlsState.HIDDEN
-                || constraints == BrowserControlsState.SHOWN;
-    }
-
     private void notifyControlOffsetChanged() {
         try (TraceEvent e =
                 TraceEvent.scoped("BrowserControlsManager.notifyControlOffsetChanged")) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManagerSupplier.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManagerSupplier.java
index 07c94eb..973bf77 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManagerSupplier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManagerSupplier.java
@@ -12,14 +12,13 @@
 import org.chromium.ui.base.WindowAndroid;
 
 /**
- * A {@link UnownedUserDataSupplier} which manages the supplier and UnownedUserData for a
- * {@link BrowserControlsManager}.
+ * A {@link UnownedUserDataSupplier} which manages the supplier and UnownedUserData for a {@link
+ * BrowserControlsManager}.
  */
 public class BrowserControlsManagerSupplier
         extends UnownedUserDataSupplier<BrowserControlsManager> {
     private static final UnownedUserDataKey<BrowserControlsManagerSupplier> KEY =
-            new UnownedUserDataKey<BrowserControlsManagerSupplier>(
-                    BrowserControlsManagerSupplier.class);
+            new UnownedUserDataKey<>(BrowserControlsManagerSupplier.class);
 
     /** Return {@link TabModelSelector} supplier associated with the given {@link WindowAndroid}. */
     public static ObservableSupplier<BrowserControlsManager> from(
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 1e7233f..b83537d 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
@@ -145,8 +145,7 @@
         private final WeakReference<FullscreenHtmlApiHandlerBase> mFullscreenHtmlApiHandler;
 
         public FullscreenHandler(FullscreenHtmlApiHandlerBase fullscreenHtmlApiHandlerBase) {
-            mFullscreenHtmlApiHandler =
-                    new WeakReference<FullscreenHtmlApiHandlerBase>(fullscreenHtmlApiHandlerBase);
+            mFullscreenHtmlApiHandler = new WeakReference<>(fullscreenHtmlApiHandlerBase);
         }
 
         @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/BrowsingHistoryBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/history/BrowsingHistoryBridge.java
index 2e45234..7b6684c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/BrowsingHistoryBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/BrowsingHistoryBridge.java
@@ -49,7 +49,7 @@
                 .queryHistory(
                         mNativeHistoryBridge,
                         BrowsingHistoryBridge.this,
-                        new ArrayList<HistoryItem>(),
+                        new ArrayList<>(),
                         query,
                         appId,
                         false);
@@ -61,7 +61,7 @@
                 .queryHistory(
                         mNativeHistoryBridge,
                         BrowsingHistoryBridge.this,
-                        new ArrayList<HistoryItem>(),
+                        new ArrayList<>(),
                         hostName,
                         null,
                         true);
@@ -71,16 +71,13 @@
     public void queryHistoryContinuation() {
         BrowsingHistoryBridgeJni.get()
                 .queryHistoryContinuation(
-                        mNativeHistoryBridge,
-                        BrowsingHistoryBridge.this,
-                        new ArrayList<HistoryItem>());
+                        mNativeHistoryBridge, BrowsingHistoryBridge.this, new ArrayList<>());
     }
 
     @Override
     public void queryApps() {
         BrowsingHistoryBridgeJni.get()
-                .getAllAppIds(
-                        mNativeHistoryBridge, BrowsingHistoryBridge.this, new ArrayList<String>());
+                .getAllAppIds(mNativeHistoryBridge, BrowsingHistoryBridge.this, new ArrayList<>());
     }
 
     @CalledByNative
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryContentManager.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryContentManager.java
index 917ca8e..fd21f34 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryContentManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryContentManager.java
@@ -212,7 +212,7 @@
         mSelectionDelegate =
                 selectionDelegate != null
                         ? selectionDelegate
-                        : new SelectionDelegate<HistoryItem>() {
+                        : new SelectionDelegate<>() {
                             @Override
                             public boolean toggleSelectionForItem(HistoryItem bookmark) {
                                 return false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayoutUnitTest.java b/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayoutUnitTest.java
index f2c2955..0b380e8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayoutUnitTest.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayoutUnitTest.java
@@ -296,7 +296,7 @@
         when(mTab.isNativePage()).thenReturn(false);
         when(mTabModelSelector.getCurrentTab()).thenReturn(mTab);
 
-        mHubLayoutAnimatorSupplier = new SyncOneshotSupplierImpl<HubLayoutAnimator>();
+        mHubLayoutAnimatorSupplier = new SyncOneshotSupplierImpl<>();
         when(mHubLayoutAnimatorProviderMock.getAnimatorSupplier())
                 .thenReturn(mHubLayoutAnimatorSupplier);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AutofillSaveCardInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AutofillSaveCardInfoBar.java
index e638515..722c5c4d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AutofillSaveCardInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AutofillSaveCardInfoBar.java
@@ -45,8 +45,7 @@
     private final String mTitleText;
     private @Nullable String mDescriptionText;
     private final boolean mIsGooglePayBrandingEnabled;
-    private final LinkedList<LegalMessageLine> mLegalMessageLines =
-            new LinkedList<LegalMessageLine>();
+    private final LinkedList<LegalMessageLine> mLegalMessageLines = new LinkedList<>();
 
     /**
      * Creates a new instance of the infobar.
@@ -58,10 +57,10 @@
      * @param linkText Link text to display in addition to the message.
      * @param buttonOk String to display on the OK button.
      * @param buttonCancel String to display on the Cancel button.
-     * @param accountFooterEmail The email to be shown on the footer, or null. The footer is
-     * only shown if both this and |accountFooterAvatar| are provided.
-     * @param accountFooterAvatar The avatar to be shown on the footer, or null. The footer is
-     * only shown if both this and |accountFooterEmail| are provided.
+     * @param accountFooterEmail The email to be shown on the footer, or null. The footer is only
+     *     shown if both this and |accountFooterAvatar| are provided.
+     * @param accountFooterAvatar The avatar to be shown on the footer, or null. The footer is only
+     *     shown if both this and |accountFooterEmail| are provided.
      */
     private AutofillSaveCardInfoBar(
             long nativeAutofillSaveCardInfoBar,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AutofillVirtualCardEnrollmentInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AutofillVirtualCardEnrollmentInfoBar.java
index f980562..814d0ae7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AutofillVirtualCardEnrollmentInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AutofillVirtualCardEnrollmentInfoBar.java
@@ -41,16 +41,14 @@
     private final String mTitleText;
     private String mDescriptionText;
     private String mLearnMoreLinkText;
-    private final LinkedList<LegalMessageLine> mGoogleLegalMessageLines =
-            new LinkedList<LegalMessageLine>();
-    private final LinkedList<LegalMessageLine> mIssuerLegalMessageLines =
-            new LinkedList<LegalMessageLine>();
+    private final LinkedList<LegalMessageLine> mGoogleLegalMessageLines = new LinkedList<>();
+    private final LinkedList<LegalMessageLine> mIssuerLegalMessageLines = new LinkedList<>();
 
     /**
      * Creates a new instance of the infobar.
      *
      * @param nativeAutofillVirtualCardEnrollmentInfoBar The pointer to the native object for
-     *         callbacks.
+     *     callbacks.
      * @param iconId ID corresponding to the icon that will be shown for the InfoBar.
      * @param iconBitmap Bitmap to use if there is no equivalent Java resource for iconId.
      * @param message Title of the infobar to display along the icon.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitTaskRunner.java b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitTaskRunner.java
index 56c7d50..f527664b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitTaskRunner.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitTaskRunner.java
@@ -111,7 +111,7 @@
             ChromeActivitySessionTracker sessionTracker =
                     ChromeActivitySessionTracker.getInstance();
             sessionTracker.getVariationsRestrictModeValue(
-                    new Callback<String>() {
+                    new Callback<>() {
                         @Override
                         public void onResult(String restrictMode) {
                             mFetchSeedTask = new FetchSeedTask(restrictMode);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
index b8e2522b..3f0ee893 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
@@ -68,7 +68,7 @@
             task.run();
         } else {
             if (mTasksToRunWithFullBrowser == null) {
-                mTasksToRunWithFullBrowser = new ArrayList<Runnable>();
+                mTasksToRunWithFullBrowser = new ArrayList<>();
             }
             mTasksToRunWithFullBrowser.add(task);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/ChromeMediaNotificationControllerDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/ChromeMediaNotificationControllerDelegate.java
index 1781ad2d..b127eb2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/ChromeMediaNotificationControllerDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/ChromeMediaNotificationControllerDelegate.java
@@ -57,7 +57,7 @@
     @VisibleForTesting static SparseArray<NotificationOptions> sMapNotificationIdToOptions;
 
     static {
-        sMapNotificationIdToOptions = new SparseArray<NotificationOptions>();
+        sMapNotificationIdToOptions = new SparseArray<>();
 
         sMapNotificationIdToOptions.put(
                 PlaybackListenerServiceImpl.NOTIFICATION_ID,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/metrics/VariationsSession.java b/chrome/android/java/src/org/chromium/chrome/browser/metrics/VariationsSession.java
index c0e8a38..28dfcbe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/metrics/VariationsSession.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/metrics/VariationsSession.java
@@ -35,7 +35,7 @@
 
         mRestrictModeFetchStarted = true;
         getRestrictModeValue(
-                new Callback<String>() {
+                new Callback<>() {
                     @Override
                     public void onResult(String restrictMode) {
                         VariationsSessionJni.get()
@@ -59,7 +59,7 @@
             return;
         }
         getRestrictMode(
-                new Callback<String>() {
+                new Callback<>() {
                     @Override
                     public void onResult(String restrictMode) {
                         assert restrictMode != null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageAssassin.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageAssassin.java
index bc12a28..7c28570 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageAssassin.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageAssassin.java
@@ -68,7 +68,7 @@
      * @param tab The tab being hidden.
      */
     public void tabHidden(Tab tab) {
-        mRecentTabs.add(new WeakReference<Tab>(tab));
+        mRecentTabs.add(new WeakReference<>(tab));
 
         // If a tab has just passed the threshold from "recent" to "not recent" and it's displaying
         // a native page, freeze the native page.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationSuspender.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationSuspender.java
index 4ab33c3..a412ffa8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationSuspender.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationSuspender.java
@@ -97,7 +97,7 @@
      */
     public List<String> storeNotificationResources(List<NotificationWrapper> notifications) {
         if (notifications.isEmpty()) {
-            return new ArrayList<String>();
+            return new ArrayList<>();
         }
 
         String[] notificationIds = new String[notifications.size()];
@@ -117,7 +117,7 @@
 
         NotificationSuspenderJni.get()
                 .storeNotificationResources(mProfile, notificationIds, origins, resources);
-        return new ArrayList<String>(Arrays.asList(notificationIds));
+        return new ArrayList<>(Arrays.asList(notificationIds));
     }
 
     /**
@@ -172,7 +172,7 @@
 
     private List<Uri> getOriginsForDomains(List<String> fqdns) {
         final String[] notificationSchemes = {UrlConstants.HTTPS_SCHEME, UrlConstants.HTTP_SCHEME};
-        ArrayList<Uri> origins = new ArrayList<Uri>();
+        ArrayList<Uri> origins = new ArrayList<>();
         for (String fqdn : fqdns) {
             for (String scheme : notificationSchemes) {
                 origins.add(new Uri.Builder().scheme(scheme).authority(fqdn).build());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/scheduler/NotificationSchedulerTask.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/scheduler/NotificationSchedulerTask.java
index 0ba8cdf..317bc84d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/scheduler/NotificationSchedulerTask.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/scheduler/NotificationSchedulerTask.java
@@ -39,7 +39,7 @@
         // Wrap to a Callback<Boolean> because JNI generator can't recognize TaskFinishedCallback as
         // a Java interface in the function parameter.
         Callback<Boolean> taskCallback =
-                new Callback<Boolean>() {
+                new Callback<>() {
                     @Override
                     public void onResult(Boolean needsReschedule) {
                         callback.taskFinished(needsReschedule);
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 185eda10..49e87b94 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
@@ -242,7 +242,7 @@
 
             // Fallback added for metric records only.
             restoringState.addObserver(
-                    new Callback<Integer>() {
+                    new Callback<>() {
                         long mStart;
 
                         @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridge.java
index d5e3204..8cd1120 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridge.java
@@ -130,7 +130,7 @@
 
     @Override
     public List<RecentlyClosedEntry> getRecentlyClosedEntries(int maxEntryCount) {
-        List<RecentlyClosedEntry> entries = new ArrayList<RecentlyClosedEntry>();
+        List<RecentlyClosedEntry> entries = new ArrayList<>();
         boolean received =
                 RecentlyClosedBridgeJni.get()
                         .getRecentlyClosedEntries(mNativeBridge, entries, maxEntryCount);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflineBackgroundTask.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflineBackgroundTask.java
index c7deb37..b4a93e4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflineBackgroundTask.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflineBackgroundTask.java
@@ -78,7 +78,7 @@
 
     /** Wraps the callback for code reuse */
     private Callback<Boolean> wrapCallback(final TaskFinishedCallback callback) {
-        return new Callback<Boolean>() {
+        return new Callback<>() {
             @Override
             public void onResult(Boolean result) {
                 callback.taskFinished(result);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
index 45bddad6..da6992e9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
@@ -334,7 +334,7 @@
     @VisibleForTesting
     public void deletePage(final ClientId clientId, Callback<Integer> callback) {
         assert mIsNativeOfflinePageModelLoaded;
-        ArrayList<ClientId> ids = new ArrayList<ClientId>();
+        ArrayList<ClientId> ids = new ArrayList<>();
         ids.add(clientId);
 
         deletePagesByClientId(ids, callback);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java
index f212464..fb96ac8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java
@@ -494,7 +494,7 @@
             final Callback<ShareParams> shareCallback) {
         RecordUserAction.record("OfflinePages.Sharing.SharePageFromOverflowMenu");
         AsyncTask<Uri> task =
-                new AsyncTask<Uri>() {
+                new AsyncTask<>() {
                     @Override
                     protected Uri doInBackground() {
                         // Android Q+: If we already have a content URI for the published page,
@@ -765,7 +765,7 @@
 
             bridge.deletePagesByClientId(
                     clientIds,
-                    new Callback<Integer>() {
+                    new Callback<>() {
                         @Override
                         public void onResult(Integer result) {
                             // Result is ignored.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridge.java
index 47e5ede3..00cd541 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridge.java
@@ -163,11 +163,10 @@
      * @param url The given URL to save for later.
      * @param clientId the clientId for the offline page to be saved later.
      * @param userRequested Whether this request should be prioritized because the user explicitly
-     *                      requested it.
+     *     requested it.
      * @param origin The app that initiated the request.
      * @param callback Callback for whether the URL is successfully added to queue. Non-zero number
-     *                 represents a failure reason (See offline_pages::AddRequestResult enum). 0 is
-     * success.
+     *     represents a failure reason (See offline_pages::AddRequestResult enum). 0 is success.
      */
     public void savePageLater(
             final String url,
@@ -176,7 +175,7 @@
             OfflinePageOrigin origin,
             Callback<Integer> callback) {
         Callback<Integer> wrapper =
-                new Callback<Integer>() {
+                new Callback<>() {
                     @Override
                     public void onResult(Integer i) {
                         if (callback != null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/evaluation/OfflinePageEvaluationBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/evaluation/OfflinePageEvaluationBridge.java
index 8799446..1d795dc0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/evaluation/OfflinePageEvaluationBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/evaluation/OfflinePageEvaluationBridge.java
@@ -84,8 +84,7 @@
 
     private long mNativeOfflinePageEvaluationBridge;
     private boolean mIsOfflinePageModelLoaded;
-    private ObserverList<OfflinePageEvaluationObserver> mObservers =
-            new ObserverList<OfflinePageEvaluationObserver>();
+    private ObserverList<OfflinePageEvaluationObserver> mObservers = new ObserverList<>();
 
     private OutputStreamWriter mLogOutput;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/AccountChooserDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/AccountChooserDialog.java
index 64b86a9b..a544be4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/AccountChooserDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/AccountChooserDialog.java
@@ -128,7 +128,7 @@
 
     private ArrayAdapter<Credential> generateAccountsArrayAdapter(
             Context context, Credential[] credentials) {
-        return new ArrayAdapter<Credential>(context, /* resource= */ 0, credentials) {
+        return new ArrayAdapter<>(context, /* resource= */ 0, credentials) {
             @Override
             public View getView(int position, View convertView, ViewGroup parent) {
                 if (convertView == null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java
index 7b7e4c97..420fd21 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java
@@ -235,7 +235,7 @@
 
         mCountryField.set(
                 DROPDOWN_CALLBACK,
-                new Callback<String>() {
+                new Callback<>() {
                     /** Load admin areas for the selected country. */
                     @Override
                     public void onResult(String countryCode) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/AnimatorProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/AnimatorProperties.java
index 10d0c66..3a5c017 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/AnimatorProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/AnimatorProperties.java
@@ -10,7 +10,7 @@
 /** Holds different {@link android.util.Property} types that can be used with ObjectAnimators. */
 class AnimatorProperties {
     static final IntProperty<Drawable> DRAWABLE_ALPHA_PROPERTY =
-            new IntProperty<Drawable>("alpha") {
+            new IntProperty<>("alpha") {
                 @Override
                 public Integer get(Drawable d) {
                     return d.getAlpha();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/ContactDetailsSection.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/ContactDetailsSection.java
index 9858db5..1c748b8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/ContactDetailsSection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/ContactDetailsSection.java
@@ -48,7 +48,7 @@
         mContext = context;
         mContactEditor = contactEditor;
         // Copy the profiles from which this section is derived.
-        mProfiles = new ArrayList<AutofillProfile>(unmodifiableProfiles);
+        mProfiles = new ArrayList<>(unmodifiableProfiles);
 
         // Refresh the contact section items and selection.
         createContactListFromAutofillProfiles(journeyLogger);
@@ -114,7 +114,7 @@
         // The sort is stable, so contacts with the same relevance score are sorted by frecency.
         Collections.sort(
                 contacts,
-                new Comparator<AutofillContact>() {
+                new Comparator<>() {
                     @Override
                     public int compare(AutofillContact a, AutofillContact b) {
                         return b.getRelevanceScore() - a.getRelevanceScore();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUi.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUi.java
index bc612e9..6153847c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUi.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUi.java
@@ -387,7 +387,7 @@
 
         // This callback will be fired if mIsClientCheckingSelection is true.
         mUpdateSectionsCallback =
-                new Callback<PaymentInformation>() {
+                new Callback<>() {
                     @Override
                     public void onResult(PaymentInformation result) {
                         mIsClientCheckingSelection = false;
@@ -436,7 +436,7 @@
         mPaymentUisShowStateReconciler.showPaymentRequestDialogWhenNoBottomSheet();
         mClient.getDefaultPaymentInformation(
                 waitForUpdatedDetails,
-                new Callback<PaymentInformation>() {
+                new Callback<>() {
                     @Override
                     public void onResult(PaymentInformation result) {
                         updateOrderSummarySection(result.getShoppingCart());
@@ -1117,7 +1117,7 @@
         mSelectedSection = section;
         if (mSelectedSection == mOrderSummarySection) {
             mClient.getShoppingCart(
-                    new Callback<ShoppingCart>() {
+                    new Callback<>() {
                         @Override
                         public void onResult(ShoppingCart result) {
                             updateOrderSummarySection(result);
@@ -1202,7 +1202,7 @@
     }
 
     private Callback<SectionInformation> createUpdateSectionCallback(@DataType final int type) {
-        return new Callback<SectionInformation>() {
+        return new Callback<>() {
             @Override
             public void onResult(SectionInformation result) {
                 updateSection(type, result);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentUiService.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentUiService.java
index 03444f3a..0d49b39 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentUiService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentUiService.java
@@ -930,7 +930,7 @@
     private void editContactOnPaymentRequestUi(@Nullable final AutofillContact toEdit) {
         mContactEditor.showEditPrompt(
                 toEdit,
-                new Callback<AutofillContact>() {
+                new Callback<>() {
                     @Override
                     public void onResult(AutofillContact editedContact) {
                         if (mPaymentRequestUi == null) return;
@@ -977,7 +977,7 @@
     private void editAddress(@Nullable final AutofillAddress toEdit) {
         mAddressEditor.showEditPrompt(
                 toEdit,
-                new Callback<AutofillAddress>() {
+                new Callback<>() {
                     @Override
                     public void onResult(AutofillAddress editedAddress) {
                         if (mPaymentRequestUi == null) return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/permissions/PermissionUpdateRequester.java b/chrome/android/java/src/org/chromium/chrome/browser/permissions/PermissionUpdateRequester.java
index f35e3b7f..1e558a9f9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/permissions/PermissionUpdateRequester.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/permissions/PermissionUpdateRequester.java
@@ -58,10 +58,10 @@
         mNativePtr = nativePtr;
         mWebContents = webContents;
 
-        mRequiredAndroidPermissions = new HashSet<String>();
+        mRequiredAndroidPermissions = new HashSet<>();
         Collections.addAll(mRequiredAndroidPermissions, requiredPermissions);
 
-        Set<String> allPermissions = new HashSet<String>();
+        Set<String> allPermissions = new HashSet<>();
         Collections.addAll(allPermissions, requiredPermissions);
         Collections.addAll(allPermissions, optionalPermissions);
         mAndroidPermisisons = allPermissions.toArray(new String[allPermissions.size()]);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/printing/TabPrinter.java b/chrome/android/java/src/org/chromium/chrome/browser/printing/TabPrinter.java
index e784574..0e432d2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/printing/TabPrinter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/printing/TabPrinter.java
@@ -43,7 +43,7 @@
     }
 
     public TabPrinter(Tab tab) {
-        mTab = new WeakReference<Tab>(tab);
+        mTab = new WeakReference<>(tab);
         mDefaultTitle = ContextUtils.getApplicationContext().getString(R.string.menu_print);
         mErrorMessage =
                 ContextUtils.getApplicationContext().getString(R.string.error_printing_failed);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/AppRestrictionSupplier.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/AppRestrictionSupplier.java
index ac12a09..59e13f2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/AppRestrictionSupplier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/AppRestrictionSupplier.java
@@ -106,7 +106,7 @@
         Context appContext = ContextUtils.getApplicationContext();
         try {
             mFetchAppRestrictionAsyncTask =
-                    new AsyncTask<Boolean>() {
+                    new AsyncTask<>() {
                         @Override
                         protected Boolean doInBackground() {
                             UserManager userManager =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsOfflineModelObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsOfflineModelObserver.java
index 748b02ba..3b1496c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsOfflineModelObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsOfflineModelObserver.java
@@ -86,7 +86,7 @@
         mOfflinePageBridge.selectPageForOnlineUrl(
                 suggestion.getUrl(),
                 /* tabId= */ 0,
-                new Callback<OfflinePageItem>() {
+                new Callback<>() {
                     @Override
                     public void onResult(OfflinePageItem item) {
                         onSuggestionOfflineIdChanged(suggestion, item);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/UrlSimilarityScorer.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/UrlSimilarityScorer.java
index 8a8e0d99..e5bb90b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/UrlSimilarityScorer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/UrlSimilarityScorer.java
@@ -108,7 +108,7 @@
         mLaxQuery = laxQuery;
         mLaxPath = laxPath;
 
-        mCompatibleSchemes = new HashSet<String>();
+        mCompatibleSchemes = new HashSet<>();
         String keyScheme = mKeyUrl.getScheme();
         mCompatibleSchemes.add(keyScheme);
         if (laxSchemeHost && keyScheme.contentEquals(UrlConstants.HTTP_SCHEME)) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileDragDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileDragDelegateImpl.java
index 926ee68..5757ba30 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileDragDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileDragDelegateImpl.java
@@ -174,7 +174,7 @@
 
     @Override
     public List<TileView> getDraggableTileViews() {
-        List<TileView> draggableTileViews = new ArrayList<TileView>();
+        List<TileView> draggableTileViews = new ArrayList<>();
         int tileCount = mMvTilesLayout.getTileCount();
         for (int i = 0; i < tileCount; ++i) {
             TileView tileView = mMvTilesLayout.getTileAt(i);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java
index ee41f46..647685b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java
@@ -72,7 +72,7 @@
         public boolean customTilesIndicator;
 
         /** List of tasks to run after tiles are reloaded and re-rendered. */
-        public final LinkedList<Runnable> taskToRunAfterTileReload = new LinkedList<Runnable>();
+        public final LinkedList<Runnable> taskToRunAfterTileReload = new LinkedList<>();
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileMovement.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileMovement.java
index 05661241..44e65fd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileMovement.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileMovement.java
@@ -35,7 +35,7 @@
     TileMovement(List<TileView> tileViews) {
         assert !tileViews.isEmpty();
         mTileViews = tileViews;
-        mOriginalX = new ArrayList<Float>();
+        mOriginalX = new ArrayList<>();
         mAnimators = new ArrayList<@Nullable ViewPropertyAnimator>();
         for (TileView tileView : mTileViews) {
             float x = tileView.getX();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java
index ad24c58..b93b392 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java
@@ -111,7 +111,7 @@
         void put(SiteSuggestion key, @NonNull SuggestionsTileView value) {
             LinkedList<SuggestionsTileView> bucket = mStorage.get(key);
             if (bucket == null) {
-                bucket = new LinkedList<SuggestionsTileView>();
+                bucket = new LinkedList<>();
                 mStorage.put(key, bucket);
             }
             bucket.addLast(value);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileUtils.java
index 3538f5f..a1425a7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileUtils.java
@@ -21,7 +21,7 @@
 /** Utility class for {@link Tile}s related queries or operations. */
 public class TileUtils {
     private static final Set<String> CUSTOM_TILE_SCHEMES =
-            new HashSet<String>(
+            new HashSet<>(
                     Arrays.asList(
                             UrlConstants.CHROME_SCHEME,
                             UrlConstants.CHROME_NATIVE_SCHEME,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TilesLinearLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TilesLinearLayout.java
index 70de61e..425fdea5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TilesLinearLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TilesLinearLayout.java
@@ -25,7 +25,7 @@
  */
 @NullMarked
 public class TilesLinearLayout extends LinearLayout {
-    private final List<TileView> mTileList = new ArrayList<TileView>();
+    private final List<TileView> mTileList = new ArrayList<>();
 
     protected float mNonTileViewsTotalWidthDp;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncErrorNotifier.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncErrorNotifier.java
index 3f73303..30231375 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncErrorNotifier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncErrorNotifier.java
@@ -245,7 +245,7 @@
     /** Creates an intent that launches an activity that retrieves the trusted vault key. */
     private Promise<Intent> createTrustedVaultKeyRetrievalIntent() {
         assert mSyncService.getAccountInfo() != null;
-        Promise<Intent> promise = new Promise<Intent>();
+        Promise<Intent> promise = new Promise<>();
         mTrustedVaultClient
                 .createKeyRetrievalIntent(mSyncService.getAccountInfo())
                 // Cf. SyncTrustedVaultProxyActivity as to why use a proxy intent.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/TrustedVaultClient.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/TrustedVaultClient.java
index 7030b47..3ce1c24 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/TrustedVaultClient.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/TrustedVaultClient.java
@@ -149,7 +149,7 @@
     private Backend mBackend;
 
     // Registered native TrustedVaultClientAndroid instances. Usually exactly one.
-    private final Set<Long> mNativeTrustedVaultClientAndroidSet = new TreeSet<Long>();
+    private final Set<Long> mNativeTrustedVaultClientAndroidSet = new TreeSet<>();
 
     @VisibleForTesting
     public TrustedVaultClient(Backend backend) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java
index 8e932e2..3faef9b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java
@@ -190,7 +190,7 @@
             mPendingTabClosureManager =
                     new PendingTabClosureManager(this, new PendingTabClosureDelegateImpl());
         }
-        mObservers = new ObserverList<TabModelObserver>();
+        mObservers = new ObserverList<>();
         // The call to initializeNative() should be as late as possible, as it results in calling
         // observers on the native side, which may in turn call |addObserver()| on this object.
         initializeNative(profile);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
index 30d8449..be3a38a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
@@ -2002,7 +2002,7 @@
     /** Asynchronously triggers a cleanup of any unused persistent data. */
     private void cleanUpPersistentData() {
         mPersistencePolicy.cleanupUnusedFiles(
-                new Callback<TabPersistenceFileInfo>() {
+                new Callback<>() {
                     @Override
                     public void onResult(TabPersistenceFileInfo result) {
                         if (result == null) return;
@@ -2047,7 +2047,7 @@
     public void cleanupStateFile(int instanceId) {
         mPersistencePolicy.cleanupInstanceState(
                 instanceId,
-                new Callback<TabPersistenceFileInfo>() {
+                new Callback<>() {
                     @Override
                     public void onResult(TabPersistenceFileInfo result) {
                         // Delete the instance state file (tab_stateX) as well.
@@ -2080,7 +2080,7 @@
      */
     private void deleteFileAsync(final String file, boolean useSerialExecution) {
         if (useSerialExecution) {
-            new BackgroundOnlyAsyncTask<Void>() {
+            new BackgroundOnlyAsyncTask<>() {
                 @Override
                 protected Void doInBackground() {
                     deleteStateFile(file);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index fc248d9..2576e5e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -1594,7 +1594,7 @@
         mFindToolbarManager.addObserver(mFindToolbarObserver);
 
         Callback<Profile> profileObserver =
-                new Callback<Profile>() {
+                new Callback<>() {
                     @Override
                     public void onResult(Profile profile) {
                         mTemplateUrlService = TemplateUrlServiceFactory.getForProfile(profile);
@@ -2297,11 +2297,12 @@
      * @param drawable The {@link Drawable} to use as the background for the button.
      * @param description The content description for the custom action button.
      * @param listener The {@link OnClickListener} to use for clicks to the button.
+     * @param type The {@link ButtonType} of the action button.
      * @see #updateCustomActionButton
      */
     public void addCustomActionButton(
-            Drawable drawable, String description, OnClickListener listener) {
-        mToolbar.addCustomActionButton(drawable, description, listener);
+            Drawable drawable, String description, OnClickListener listener, int type) {
+        mToolbar.addCustomActionButton(drawable, description, listener, type);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java
index ba883d2..79be1cd7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java
@@ -164,7 +164,7 @@
                 controlsVisibilityManager.getBottomControlsHeight());
 
         mOmniboxFocusObserver =
-                new Callback<Boolean>() {
+                new Callback<>() {
                     /** A token held while this object is suppressing the bottom sheet. */
                     private int mToken;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/MediaCaptureOverlayController.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/MediaCaptureOverlayController.java
index e094962b4..ef0c7ee 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/MediaCaptureOverlayController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/MediaCaptureOverlayController.java
@@ -19,21 +19,19 @@
 import org.chromium.ui.base.WindowAndroid;
 
 /**
- * This class manages the visibility of an overlay border when tab capture is ongoing.
- * The border will be visible if any of the captured/tracked tabs are user interactible
- * (e.g. visible with no overlays hiding the content), and hidden otherwise. It attempts
- * to respond to control state events to resize the UI as the size of the currently
- * visible captured tab is changed.
+ * This class manages the visibility of an overlay border when tab capture is ongoing. The border
+ * will be visible if any of the captured/tracked tabs are user interactible (e.g. visible with no
+ * overlays hiding the content), and hidden otherwise. It attempts to respond to control state
+ * events to resize the UI as the size of the currently visible captured tab is changed.
  */
 public class MediaCaptureOverlayController implements UnownedUserData {
     private static final UnownedUserDataKey<MediaCaptureOverlayController> KEY =
-            new UnownedUserDataKey<MediaCaptureOverlayController>(
-                    MediaCaptureOverlayController.class);
+            new UnownedUserDataKey<>(MediaCaptureOverlayController.class);
 
     private final CaptureOverlayTabObserver mTabObserver = new CaptureOverlayTabObserver();
 
     private View mOverlayView;
-    private final SparseArray<Tab> mCapturedTabs = new SparseArray<Tab>();
+    private final SparseArray<Tab> mCapturedTabs = new SparseArray<>();
     private Tab mVisibleTab;
 
     private class CaptureOverlayTabObserver extends EmptyTabObserver {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/activity_recreation/ActivityRecreationUiState.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/activity_recreation/ActivityRecreationUiState.java
index 9b9a9ebf..d6adc26d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/activity_recreation/ActivityRecreationUiState.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/activity_recreation/ActivityRecreationUiState.java
@@ -19,7 +19,7 @@
     boolean mIsTabSwitcherShown;
 
     public static final Parcelable.Creator<ActivityRecreationUiState> CREATOR =
-            new Parcelable.Creator<ActivityRecreationUiState>() {
+            new Parcelable.Creator<>() {
                 @Override
                 public ActivityRecreationUiState createFromParcel(Parcel in) {
                     return new ActivityRecreationUiState(in);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkDataProvider.java
index 5273c088..bfb7807 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkDataProvider.java
@@ -141,7 +141,7 @@
 
         if (offlineData == null) return null;
 
-        List<String> fieldValues = new ArrayList<String>();
+        List<String> fieldValues = new ArrayList<>();
         for (int field : fields) {
             switch (field) {
                 case WebApkDetailsForDefaultOfflinePage.SHORT_NAME:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInstaller.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInstaller.java
index 957ced2b..f9a3b85 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInstaller.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInstaller.java
@@ -139,7 +139,7 @@
         }
 
         Callback<Integer> callback =
-                new Callback<Integer>() {
+                new Callback<>() {
                     @Override
                     public void onResult(Integer result) {
                         WebApkInstaller.this.notify(result);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkSyncService.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkSyncService.java
index 5cdafac..0fc3014 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkSyncService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkSyncService.java
@@ -126,7 +126,7 @@
 
     @CalledByNative
     private static List<Bitmap> createBitmapList() {
-        return new ArrayList<Bitmap>();
+        return new ArrayList<>();
     }
 
     @CalledByNative
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUninstallTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUninstallTracker.java
index b536d84..e577202 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUninstallTracker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUninstallTracker.java
@@ -55,7 +55,7 @@
             }
         }
         preferencesManager.writeStringSet(
-                ChromePreferenceKeys.WEBAPK_UNINSTALLED_PACKAGES, new HashSet<String>());
+                ChromePreferenceKeys.WEBAPK_UNINSTALLED_PACKAGES, new HashSet<>());
     }
 
     /** Sets WebAPK uninstall to be recorded next time that native is loaded. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDirectoryManager.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDirectoryManager.java
index d48fe645..a340503a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDirectoryManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDirectoryManager.java
@@ -36,7 +36,7 @@
     public static void cleanUpDirectories() {
         if (!sMustCleanUpOldDirectories.getAndSet(false)) return;
 
-        new BackgroundOnlyAsyncTask<Void>() {
+        new BackgroundOnlyAsyncTask<>() {
             @Override
             protected final Void doInBackground() {
                 recordNumberOfStaleWebApkUpdateRequestFiles();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java
index 8812b1ce..69552cb1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java
@@ -273,7 +273,7 @@
      */
     public List<WebApkSpecifics> getWebApkSpecificsImpl(
             GetWebApkSpecificsImplSetWebappInfoForTesting setWebappInfoForTesting) {
-        List<WebApkSpecifics> webApkSpecificsList = new ArrayList<WebApkSpecifics>();
+        List<WebApkSpecifics> webApkSpecificsList = new ArrayList<>();
         for (WebappDataStorage storage : mStorages.values()) {
             String scope = getWebApkScopeFromStorage(storage);
             if (scope.isEmpty()) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ContentViewFocusTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ContentViewFocusTest.java
index 7c41811..792a92e8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ContentViewFocusTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ContentViewFocusTest.java
@@ -58,7 +58,7 @@
 
     private static final int WAIT_RESPONSE_MS = 2000;
 
-    private final ArrayDeque<Boolean> mFocusChanges = new ArrayDeque<Boolean>();
+    private final ArrayDeque<Boolean> mFocusChanges = new ArrayDeque<>();
 
     private String mTitle;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ImageFetcherIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ImageFetcherIntegrationTest.java
index 47eff56..89effdd 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ImageFetcherIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ImageFetcherIntegrationTest.java
@@ -61,7 +61,7 @@
             throws Exception {
         TestImageFetcherCallback callbackWaiter = new TestImageFetcherCallback();
         ThreadUtils.runOnUiThreadBlocking(
-                new Callable<Void>() {
+                new Callable<>() {
                     @Override
                     public Void call() throws TimeoutException {
                         ImageFetcher.Params params =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/VirtualKeyboardResizeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/VirtualKeyboardResizeTest.java
index f8c03ec..4bfce2f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/VirtualKeyboardResizeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/VirtualKeyboardResizeTest.java
@@ -184,7 +184,7 @@
                 JavaScriptUtils.executeJavaScriptAndWaitForResult(
                         getWebContents(), "window.resizeEventLog");
         JsonReader jsonReader = new JsonReader(new StringReader(jsonText));
-        ArrayList<Integer> pageHeights = new ArrayList<Integer>();
+        ArrayList<Integer> pageHeights = new ArrayList<>();
         try {
             jsonReader.beginArray();
             while (jsonReader.hasNext()) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java
index d8472c6..527ed15a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java
@@ -1789,7 +1789,7 @@
 
     private boolean isItemPresentInBookmarkList(final String expectedTitle) {
         return ThreadUtils.runOnUiThreadBlocking(
-                new Callable<Boolean>() {
+                new Callable<>() {
                     @Override
                     public Boolean call() {
                         for (int i = 0; i < getBookmarkCount(); i++) {
@@ -1895,7 +1895,7 @@
      */
     private static View getViewWithText(final ViewGroup viewGroup, final String expectedText) {
         return ThreadUtils.runOnUiThreadBlocking(
-                new Callable<View>() {
+                new Callable<>() {
                     @Override
                     public View call() {
                         ArrayList<View> outViews = new ArrayList<>();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/ReadingListTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/ReadingListTest.java
index 15ac7b0..f2f2605 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/ReadingListTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/ReadingListTest.java
@@ -442,7 +442,7 @@
         onView(withText("Reading list"))
                 .check(
                         matches(
-                                new TypeSafeMatcher<View>() {
+                                new TypeSafeMatcher<>() {
                                     @Override
                                     public void describeTo(Description description) {
                                         description.appendText(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetViewBinderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetViewBinderTest.java
index 275ac478..7f7ee62 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetViewBinderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetViewBinderTest.java
@@ -272,7 +272,7 @@
     }
 
     Matcher<BitmapDrawable> drawableWithSameBitmap(Bitmap expectedBitmap) {
-        return new TypeSafeMatcher<BitmapDrawable>() {
+        return new TypeSafeMatcher<>() {
             @Override
             protected boolean matchesSafely(BitmapDrawable drawable) {
                 return drawable.getBitmap().sameAs(expectedBitmap);
@@ -394,7 +394,7 @@
                 mModelBuilder.with(
                         property,
                         new LegalMessages(
-                                new LinkedList<LegalMessageLine>(),
+                                new LinkedList<>(),
                                 VirtualCardEnrollmentLinkType
                                         .VIRTUAL_CARD_ENROLLMENT_ISSUER_TOS_LINK,
                                 /* linkOpener= */ this)));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/BrowsingDataRemoverIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/BrowsingDataRemoverIntegrationTest.java
index 2f99f8d..66f99b0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/BrowsingDataRemoverIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/BrowsingDataRemoverIntegrationTest.java
@@ -109,7 +109,7 @@
 
         // The last two webapps should have been unregistered.
         Assert.assertEquals(
-                new HashSet<String>(Arrays.asList("webapp1")),
+                new HashSet<>(Arrays.asList("webapp1")),
                 WebappRegistry.getRegisteredWebappIdsForTesting());
 
         CallbackHelper dataClearedNoUrlFilterHelper = new CallbackHelper();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/BrowsingDataTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/BrowsingDataTest.java
index a97a0c0f..db23a68 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/BrowsingDataTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/BrowsingDataTest.java
@@ -134,14 +134,9 @@
     @Test
     @SmallTest
     public void testSiteDataDeleted() throws Exception {
-        // TODO(dullweber): Investigate, why WebSql fails this test.
         List<String> siteData =
                 Arrays.asList(
-                        "LocalStorage",
-                        "ServiceWorker",
-                        "CacheStorage",
-                        "FileSystem",
-                        "IndexedDb" /*, "WebSql"*/);
+                        "LocalStorage", "ServiceWorker", "CacheStorage", "FileSystem", "IndexedDb");
         mActivityTestRule.loadUrl(mUrl);
 
         for (String type : siteData) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragmentTest.java
index 0b99b10..fdd6657 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragmentTest.java
@@ -907,7 +907,7 @@
     }
 
     private void setDataTypesToClear(final Integer... typesToClear) {
-        Set<Integer> typesToClearSet = new ArraySet<Integer>(Arrays.asList(typesToClear));
+        Set<Integer> typesToClearSet = new ArraySet<>(Arrays.asList(typesToClear));
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
                     for (@DialogOption Integer option : ClearBrowsingDataFragment.getAllOptions()) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripTest.java
index d99ebb8..a257c43 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripTest.java
@@ -1553,7 +1553,7 @@
         // Only tabs that can currently be seen on the screen should be visible.
         Boolean shouldBeVisible =
                 ThreadUtils.runOnUiThreadBlocking(
-                        new Callable<Boolean>() {
+                        new Callable<>() {
                             @Override
                             public Boolean call() {
                                 return (tabView.getDrawX() + tabView.getWidth()) >= 0
@@ -1573,7 +1573,7 @@
             throws ExecutionException {
         Boolean isVisible =
                 ThreadUtils.runOnUiThreadBlocking(
-                        new Callable<Boolean>() {
+                        new Callable<>() {
                             @Override
                             public Boolean call() {
                                 return tabView.isVisible();
@@ -1597,7 +1597,7 @@
             throws ExecutionException {
         Float tabDrawX =
                 ThreadUtils.runOnUiThreadBlocking(
-                        new Callable<Float>() {
+                        new Callable<>() {
                             @Override
                             public Float call() {
                                 return tabView.getDrawX();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulatorTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulatorTest.java
index 6600cad..4e62dfe 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulatorTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulatorTest.java
@@ -226,7 +226,7 @@
     }
 
     private void checkMenuOptions(int[]... groups) {
-        checkMenuOptions(/* disabled= */ new ArrayList<Integer>(), groups);
+        checkMenuOptions(/* disabled= */ new ArrayList<>(), groups);
     }
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateControllerWrapper.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateControllerWrapper.java
index 5834d39b..6281f92 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateControllerWrapper.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateControllerWrapper.java
@@ -37,8 +37,8 @@
                     InternalState.RESOLVING,
                     InternalState.SHOWING_RESOLVED_LONG_PRESS_SEARCH);
 
-    private final List<Integer> mStartedStates = new ArrayList<Integer>();
-    private final List<Integer> mFinishedStates = new ArrayList<Integer>();
+    private final List<Integer> mStartedStates = new ArrayList<>();
+    private final List<Integer> mFinishedStates = new ArrayList<>();
 
     /**
      * Creates a wrapper around a {@link ContextualSearchInternalStateController} with the given
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRelatedSearchesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRelatedSearchesTest.java
index 6f17f5c0..451772e9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRelatedSearchesTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRelatedSearchesTest.java
@@ -202,7 +202,7 @@
         float normalHeight = mPanel.getHeight();
 
         // Simulate a response that includes both a definition and Related Searches
-        List<String> inBarSuggestions = new ArrayList<String>();
+        List<String> inBarSuggestions = new ArrayList<>();
         inBarSuggestions.add("Related Suggestion 1");
         inBarSuggestions.add("Related Suggestion 2");
         ThreadUtils.runOnUiThreadBlocking(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityAppMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityAppMenuTest.java
index d4acc5f..3a924df 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityAppMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityAppMenuTest.java
@@ -121,7 +121,7 @@
                         @Override
                         public List<ResolveInfo> queryBroadcastReceivers(
                                 Intent intent, int filters) {
-                            return new ArrayList<ResolveInfo>();
+                            return new ArrayList<>();
                         }
                     });
         }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistencePolicyTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistencePolicyTest.java
index d53c58c..31776580 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistencePolicyTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistencePolicyTest.java
@@ -203,7 +203,7 @@
         final AtomicReference<TabPersistenceFileInfo> tabDataToDelete = new AtomicReference<>();
         final CallbackHelper callbackSignal = new CallbackHelper();
         Callback<TabPersistenceFileInfo> tabDataToDeleteCallback =
-                new Callback<TabPersistenceFileInfo>() {
+                new Callback<>() {
                     @Override
                     public void onResult(TabPersistenceFileInfo tabData) {
                         tabDataToDelete.set(tabData);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/display_cutout/DisplayCutoutTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/display_cutout/DisplayCutoutTest.java
index 9d0c5b2..5f09e47 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/display_cutout/DisplayCutoutTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/display_cutout/DisplayCutoutTest.java
@@ -44,8 +44,7 @@
 @MinAndroidSdkLevel(Build.VERSION_CODES.P)
 public class DisplayCutoutTest {
     @Rule
-    public DisplayCutoutTestRule mTestRule =
-            new DisplayCutoutTestRule<ChromeActivity>(ChromeActivity.class);
+    public DisplayCutoutTestRule mTestRule = new DisplayCutoutTestRule<>(ChromeActivity.class);
 
     /** Test that no safe area is applied when we have viewport fit auto */
     @Test
@@ -205,7 +204,7 @@
         final ObservableSupplierImpl<Integer> browserCutoutModeSupplier =
                 ThreadUtils.runOnUiThreadBlocking(
                         () -> {
-                            return new ObservableSupplierImpl<Integer>();
+                            return new ObservableSupplierImpl<>();
                         });
 
         ThreadUtils.runOnUiThreadBlocking(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java
index e0a4f65a..2a83e03 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java
@@ -183,7 +183,7 @@
         private final HashSet<Object> mMatches;
 
         OneTimeMatchSet(Object... params) {
-            mMatches = new HashSet<Object>();
+            mMatches = new HashSet<>();
             Collections.addAll(mMatches, params);
         }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/MockDownloadNotificationService.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/MockDownloadNotificationService.java
index 9661546b..3cf8a8e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/MockDownloadNotificationService.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/MockDownloadNotificationService.java
@@ -24,7 +24,7 @@
 
 /** Mock class to DownloadNotificationService for testing purpose. */
 public class MockDownloadNotificationService extends DownloadNotificationService {
-    private final List<Integer> mNotificationIds = new ArrayList<Integer>();
+    private final List<Integer> mNotificationIds = new ArrayList<>();
     private boolean mPaused;
     private int mLastNotificationId = DEFAULT_NOTIFICATION_ID;
     private int mNumberOfNotifications;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/feedback/ConnectivityTaskTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/feedback/ConnectivityTaskTest.java
index 1e3d660..b7a4671 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/feedback/ConnectivityTaskTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/feedback/ConnectivityTaskTest.java
@@ -48,7 +48,7 @@
     public void testNormalCaseShouldWork() {
         final ConnectivityTask task =
                 ThreadUtils.runOnUiThreadBlocking(
-                        new Callable<ConnectivityTask>() {
+                        new Callable<>() {
                             @Override
                             public ConnectivityTask call() {
                                 // Intentionally make HTTPS-connection fail which should result in
@@ -178,7 +178,7 @@
     public void testTwoTimeoutsShouldFillInTheRest() {
         final ConnectivityTask task =
                 ThreadUtils.runOnUiThreadBlocking(
-                        new Callable<ConnectivityTask>() {
+                        new Callable<>() {
                             @Override
                             public ConnectivityTask call() {
                                 // Intentionally make HTTPS connections slow which should result in
@@ -234,7 +234,7 @@
     private static FeedbackData getResult(final ConnectivityTask task) {
         final FeedbackData result =
                 ThreadUtils.runOnUiThreadBlocking(
-                        new Callable<FeedbackData>() {
+                        new Callable<>() {
                             @Override
                             public FeedbackData call() {
                                 return task.get();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/hardware_acceleration/Utils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/hardware_acceleration/Utils.java
index 65c45824b..18ed104c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/hardware_acceleration/Utils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/hardware_acceleration/Utils.java
@@ -106,7 +106,7 @@
     }
 
     private static Set<String> collectThreadNames() {
-        Set<String> names = new HashSet<String>();
+        Set<String> names = new HashSet<>();
         for (Thread thread : Thread.getAllStackTraces().keySet()) {
             names.add(thread.getName());
         }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/identity_disc/IdentityDiscControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/identity_disc/IdentityDiscControllerTest.java
index 861f14e..57fd447 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/identity_disc/IdentityDiscControllerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/identity_disc/IdentityDiscControllerTest.java
@@ -415,7 +415,7 @@
                     mSigninTestRule.addAccountThenSignin(TestAccounts.ACCOUNT1);
 
                     ObservableSupplierImpl<Profile> profileSupplier =
-                            new ObservableSupplierImpl<Profile>();
+                            new ObservableSupplierImpl<>();
                     IdentityDiscController identityDiscController =
                             new IdentityDiscController(
                                     mActivityTestRule.getActivity(), mDispatcher, profileSupplier);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoDownloadLeakageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoDownloadLeakageTest.java
index abc1fe8b..32aa9e1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoDownloadLeakageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoDownloadLeakageTest.java
@@ -92,9 +92,9 @@
                 public void onAllDownloadsRetrieved(
                         List<DownloadItem> list, ProfileKey profileKey) {
                     if (profileKey.isOffTheRecord()) {
-                        mOffTheRecordDownloadItems = new ArrayList<DownloadItem>(list);
+                        mOffTheRecordDownloadItems = new ArrayList<>(list);
                     } else {
-                        mRegularDownloadItems = new ArrayList<DownloadItem>(list);
+                        mRegularDownloadItems = new ArrayList<>(list);
                     }
                     mRetrieveDownloadsCallback.notifyCalled();
                 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoNotificationServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoNotificationServiceTest.java
index 4774a70..0e98fab 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoNotificationServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoNotificationServiceTest.java
@@ -126,7 +126,7 @@
 
         final Profile incognitoProfile =
                 ThreadUtils.runOnUiThreadBlocking(
-                        new Callable<Profile>() {
+                        new Callable<>() {
                             @Override
                             public Profile call() {
                                 return mActivityTestRule
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/locale/LocaleManagerReferralTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/locale/LocaleManagerReferralTest.java
index b0f42a8..0067194a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/locale/LocaleManagerReferralTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/locale/LocaleManagerReferralTest.java
@@ -53,7 +53,7 @@
                 });
 
         ThreadUtils.runOnUiThreadBlocking(
-                new Callable<Void>() {
+                new Callable<>() {
                     @Override
                     public Void call() {
                         ChromeBrowserInitializer.getInstance().handleSynchronousStartup();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/media/PictureInPictureActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/media/PictureInPictureActivityTest.java
index 79b6dff..a73b766 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/media/PictureInPictureActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/media/PictureInPictureActivityTest.java
@@ -448,7 +448,7 @@
                 ActivityTestUtils.launchActivityWithTimeout(
                         InstrumentationRegistry.getInstrumentation(),
                         PictureInPictureActivity.class,
-                        new Callable<Void>() {
+                        new Callable<>() {
                             @Override
                             public Void call() throws TimeoutException {
                                 ThreadUtils.runOnUiThreadBlocking(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/channels/SiteChannelsManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/channels/SiteChannelsManagerTest.java
index 70b791c..5e72be08 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/channels/SiteChannelsManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/channels/SiteChannelsManagerTest.java
@@ -256,7 +256,7 @@
 
     private static Matcher<Integer> matchesChannelStatus(
             @NotificationChannelStatus final int status) {
-        return new BaseMatcher<Integer>() {
+        return new BaseMatcher<>() {
             @Override
             public boolean matches(Object o) {
                 return status == (int) o;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
index 20e7665c..60fcd52 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
@@ -1018,7 +1018,7 @@
 
     private boolean getUrlFocusAnimationsDisabled() {
         return ThreadUtils.runOnUiThreadBlocking(
-                new Callable<Boolean>() {
+                new Callable<>() {
                     @Override
                     public Boolean call() {
                         return mNtp.getNewTabPageLayout().urlFocusAnimationsDisabled();
@@ -1061,7 +1061,7 @@
      */
     private int getFakeboxTop(final NewTabPage ntp) {
         return ThreadUtils.runOnUiThreadBlocking(
-                new Callable<Integer>() {
+                new Callable<>() {
                     @Override
                     public Integer call() {
                         final View fakebox = ntp.getView().findViewById(R.id.search_box);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java
index 1ff67ad3..2a1574d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java
@@ -606,7 +606,7 @@
                 () -> {
                     mOfflinePageBridge.deletePagesByOfflineId(
                             offlineIds,
-                            new Callback<Integer>() {
+                            new Callback<>() {
                                 @Override
                                 public void onResult(Integer deletePageResult) {
                                     semaphore.release();
@@ -624,7 +624,7 @@
                 () -> {
                     mOfflinePageBridge.deletePage(
                             bookmarkId,
-                            new Callback<Integer>() {
+                            new Callback<>() {
                                 @Override
                                 public void onResult(Integer deletePageResult) {
                                     deletePageResultRef.set(deletePageResult.intValue());
@@ -638,7 +638,7 @@
 
     private List<OfflinePageItem> getPagesByNamespace(final String namespace)
             throws InterruptedException {
-        final List<OfflinePageItem> result = new ArrayList<OfflinePageItem>();
+        final List<OfflinePageItem> result = new ArrayList<>();
         final Semaphore semaphore = new Semaphore(0);
         PostTask.runOrPostTask(
                 TaskTraits.UI_DEFAULT,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageSavePageLaterEvaluationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageSavePageLaterEvaluationTest.java
index 650b365..b3705219 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageSavePageLaterEvaluationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageSavePageLaterEvaluationTest.java
@@ -123,7 +123,7 @@
     @Before
     public void setUp() throws Exception {
         mStartingPage = mActivityTestRule.startOnBlankPage();
-        mRequestMetadata = new LongSparseArray<RequestMetadata>();
+        mRequestMetadata = new LongSparseArray<>();
         mCount = 0;
     }
 
@@ -143,13 +143,13 @@
                             new Callback<SavePageRequest[]>() {
                                 @Override
                                 public void onResult(SavePageRequest[] results) {
-                                    ArrayList<Long> ids = new ArrayList<Long>(results.length);
+                                    ArrayList<Long> ids = new ArrayList<>(results.length);
                                     for (int i = 0; i < results.length; i++) {
                                         ids.add(results[i].getRequestId());
                                     }
                                     mBridge.removeRequestsFromQueue(
                                             ids,
-                                            new Callback<Integer>() {
+                                            new Callback<>() {
                                                 @Override
                                                 public void onResult(Integer removedCount) {
                                                     mClearingSemaphore.release();
@@ -372,7 +372,7 @@
     }
 
     private void getUrlListFromInputFile(String inputFilePath) throws IOException {
-        mUrls = new ArrayList<String>();
+        mUrls = new ArrayList<>();
         try {
             BufferedReader bufferedReader = getInputStream(inputFilePath);
             try {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflineTestUtil.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflineTestUtil.java
index f21934c9..e17880a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflineTestUtil.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflineTestUtil.java
@@ -62,7 +62,7 @@
                 () -> {
                     OfflineTestUtilJni.get()
                             .getAllPages(
-                                    new ArrayList<OfflinePageItem>(),
+                                    new ArrayList<>(),
                                     (List<OfflinePageItem> items) -> {
                                         result.set(items);
                                         callbackHelper.notifyCalled();
@@ -76,7 +76,7 @@
     // For logging out to debug test failures.
     public static String dumpRequestCoordinatorState() throws TimeoutException {
         final CallbackHelper callbackHelper = new CallbackHelper();
-        final AtomicReference<String> result = new AtomicReference<String>();
+        final AtomicReference<String> result = new AtomicReference<>();
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
                     OfflineTestUtilJni.get()
@@ -139,7 +139,7 @@
     // Waits for the offline model to initialize and returns an OfflinePageBridge.
     public static OfflinePageBridge getOfflinePageBridge() throws TimeoutException {
         final CallbackHelper ready = new CallbackHelper();
-        final AtomicReference<OfflinePageBridge> result = new AtomicReference<OfflinePageBridge>();
+        final AtomicReference<OfflinePageBridge> result = new AtomicReference<>();
         PostTask.runOrPostTask(
                 TaskTraits.UI_DEFAULT,
                 () -> {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridgeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridgeTest.java
index 5f85d1d..f905cee7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridgeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridgeTest.java
@@ -189,7 +189,7 @@
                             namespace,
                             /* userRequested= */ true,
                             new OfflinePageOrigin(),
-                            new Callback<Integer>() {
+                            new Callback<>() {
                                 @Override
                                 public void onResult(Integer i) {
                                     Assert.assertEquals(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
index a32348b..f508d81 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
@@ -145,7 +145,7 @@
         CriteriaHelper.pollUiThread(() -> urlBar.hasFocus());
 
         ThreadUtils.runOnUiThreadBlocking(
-                new Callable<Void>() {
+                new Callable<>() {
                     @Override
                     public Void call() throws InterruptedException {
                         mActivityTestRule.typeInOmnibox(text, false);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java
index 06615606..032de346 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java
@@ -14,7 +14,6 @@
 import static org.hamcrest.Matchers.not;
 import static org.junit.Assert.assertFalse;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 
 import android.content.Intent;
@@ -55,7 +54,6 @@
 import org.chromium.chrome.browser.lifecycle.InflationObserver;
 import org.chromium.chrome.browser.locale.LocaleManager;
 import org.chromium.chrome.browser.locale.LocaleManagerDelegate;
-import org.chromium.chrome.browser.omnibox.status.StatusProperties.StatusIconResource;
 import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -123,9 +121,6 @@
                 () -> {
                     TemplateUrlServiceFactory.setInstanceForTesting(mTemplateUrlService);
                     LocaleManager.getInstance().setDelegateForTest(mLocaleManagerDelegate);
-                    doReturn(new StatusIconResource(null))
-                            .when(mSearchEngineUtils)
-                            .getSearchEngineLogo(anyInt());
                 });
     }
 
@@ -195,15 +190,6 @@
                     doReturn(isGoogle ? mGoogleSearchEngine : mNonGoogleSearchEngine)
                             .when(mTemplateUrlService)
                             .getDefaultSearchEngineTemplateUrl();
-
-                    StatusIconResource logo =
-                            new StatusIconResource(
-                                    isGoogle
-                                            ? R.drawable.ic_logo_googleg_20dp
-                                            : R.drawable.ic_search,
-                                    0);
-
-                    doReturn(logo).when(mSearchEngineUtils).getSearchEngineLogo(anyInt());
                 });
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxUrlEmphasizerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxUrlEmphasizerTest.java
index aa677c9b..8354587 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxUrlEmphasizerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxUrlEmphasizerTest.java
@@ -148,7 +148,7 @@
         EmphasizedUrlSpanHelper[] spans = EmphasizedUrlSpanHelper.getSpansForEmphasizedUrl(url);
         Arrays.sort(
                 spans,
-                new Comparator<EmphasizedUrlSpanHelper>() {
+                new Comparator<>() {
                     @Override
                     public int compare(EmphasizedUrlSpanHelper o1, EmphasizedUrlSpanHelper o2) {
                         return o1.getStartIndex() - o2.getStartIndex();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/UrlBarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/UrlBarTest.java
index 5fb8371..a49e3fd3 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/UrlBarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/UrlBarTest.java
@@ -117,9 +117,9 @@
 
     private AutocompleteState getAutocompleteState(final Runnable action) {
         final AtomicBoolean hasAutocomplete = new AtomicBoolean();
-        final AtomicReference<String> textWithoutAutocomplete = new AtomicReference<String>();
-        final AtomicReference<String> textWithAutocomplete = new AtomicReference<String>();
-        final AtomicReference<String> additionalText = new AtomicReference<String>();
+        final AtomicReference<String> textWithoutAutocomplete = new AtomicReference<>();
+        final AtomicReference<String> textWithAutocomplete = new AtomicReference<>();
+        final AtomicReference<String> additionalText = new AtomicReference<>();
 
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
@@ -221,7 +221,7 @@
         mOmnibox.setAutocompleteText(inlineAutocomplete, Optional.of(additionalText));
 
         final CallbackHelper autocompleteHelper = new CallbackHelper();
-        final AtomicReference<String> requestedAutocompleteText = new AtomicReference<String>();
+        final AtomicReference<String> requestedAutocompleteText = new AtomicReference<>();
         final AtomicBoolean didPreventInlineAutocomplete = new AtomicBoolean();
         mUrlBar.setTextChangeListener(
                 (textWithoutAutocomplete) -> {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoDiscoverabilityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoDiscoverabilityTest.java
index e109bc2..0fb32904 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoDiscoverabilityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoDiscoverabilityTest.java
@@ -23,6 +23,7 @@
 import org.mockito.junit.MockitoRule;
 
 import org.chromium.base.ThreadUtils;
+import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.base.test.params.ParameterAnnotations;
 import org.chromium.base.test.params.ParameterProvider;
@@ -31,6 +32,7 @@
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.browser.browsing_data.BrowsingDataBridge;
 import org.chromium.chrome.browser.browsing_data.BrowsingDataType;
 import org.chromium.chrome.browser.browsing_data.TimePeriod;
@@ -44,11 +46,13 @@
 import org.chromium.chrome.browser.permissions.RuntimePermissionTestUtils;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileManager;
+import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
 import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
 import org.chromium.components.content_settings.ContentSettingValues;
 import org.chromium.components.content_settings.ContentSettingsType;
 import org.chromium.components.location.LocationUtils;
+import org.chromium.components.omnibox.OmniboxFeatureList;
 import org.chromium.components.permissions.PermissionDialogController;
 import org.chromium.components.search_engines.TemplateUrlService;
 import org.chromium.content_public.browser.ContentFeatureList;
@@ -219,6 +223,8 @@
                 () -> {
                     mModel = new PropertyModel(StatusProperties.ALL_KEYS);
                     mTemplateUrlServiceSupplier = new OneshotSupplierImpl<>();
+                    mTemplateUrlServiceSupplier.set(mTemplateUrlService);
+                    TemplateUrlServiceFactory.setInstanceForTesting(mTemplateUrlService);
                     mMediator =
                             new StatusMediator(
                                     mModel,
@@ -228,11 +234,10 @@
                                     mLocationBarDataProvider,
                                     mPermissionDialogController,
                                     mTemplateUrlServiceSupplier,
-                                    () -> mProfile,
+                                    new ObservableSupplierImpl(mProfile),
                                     mPageInfoIphController,
                                     sPermissionTestRule.getActivity().getWindowAndroid(),
                                     null);
-                    mTemplateUrlServiceSupplier.set(mTemplateUrlService);
                 });
     }
 
@@ -330,6 +335,7 @@
     @MediumTest
     @Feature({"PageInfoDiscoverability"})
     @ParameterAnnotations.UseMethodParameter(RequestTypeTestParams.class)
+    @EnableFeatures(OmniboxFeatureList.OMNIBOX_MOBILE_PARITY_UPDATE)
     public void testPermissionRequestTypes(
             @ContentSettingsType.EnumType int contentSettingsType, boolean isInSiteSettings) {
         if (contentSettingsType == ContentSettingsType.BLUETOOTH_CHOOSER_DATA) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java
index 6f6fedf..0bb6614e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java
@@ -528,8 +528,7 @@
             @ContentSettingsType.EnumType int type) {
         return ThreadUtils.runOnUiThreadBlocking(
                 () -> {
-                    List<ContentSettingException> exceptions =
-                            new ArrayList<ContentSettingException>();
+                    List<ContentSettingException> exceptions = new ArrayList<>();
                     WebsitePreferenceBridgeJni.get()
                             .getContentSettingsExceptions(
                                     ProfileManager.getLastUsedRegularProfile(), type, exceptions);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderUnitTest.java
index 99af5517..fe3353d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderUnitTest.java
@@ -238,7 +238,7 @@
         Mockito.when(
                         mPackageManagerDelegate.getActivitiesThatCanRespondToIntentWithMetaData(
                                 ArgumentMatchers.argThat(sPayIntentArgumentMatcher)))
-                .thenReturn(new ArrayList<ResolveInfo>());
+                .thenReturn(new ArrayList<>());
 
         verifyNoAppsFound(
                 findApps(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentDetailsUpdateServiceHelperTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentDetailsUpdateServiceHelperTest.java
index 8f16df6..5256528 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentDetailsUpdateServiceHelperTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentDetailsUpdateServiceHelperTest.java
@@ -141,7 +141,7 @@
                 new PaymentCurrencyAmount(/* currency= */ "CAD", /* value= */ "10.00");
 
         // Populate shipping options.
-        List<PaymentShippingOption> shippingOptions = new ArrayList<PaymentShippingOption>();
+        List<PaymentShippingOption> shippingOptions = new ArrayList<>();
         shippingOptions.add(
                 new PaymentShippingOption(
                         "shippingId",
@@ -150,7 +150,7 @@
                         /* selected= */ true));
 
         // Populate modifiers.
-        List<PaymentHandlerModifier> modifiers = new ArrayList<PaymentHandlerModifier>();
+        List<PaymentHandlerModifier> modifiers = new ArrayList<>();
         modifiers.add(
                 new PaymentHandlerModifier(
                         new PaymentCurrencyAmount(/* currency= */ "CAD", /* value= */ "2.00"),
@@ -655,7 +655,7 @@
     @Feature({"Payments"})
     public void testIsCallerAuthorizedEmptyPackageInfos() throws Throwable {
         final int callerUid = 7;
-        mPackageManager.overridePackageInfosForUid(callerUid, new ArrayList<PackageInfo>());
+        mPackageManager.overridePackageInfosForUid(callerUid, new ArrayList<>());
 
         installAndInvokePaymentApp();
         startPaymentDetailsUpdateService();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/WebPaymentIntentHelperTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/WebPaymentIntentHelperTest.java
index 105b31d..f5d3c407 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/WebPaymentIntentHelperTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/WebPaymentIntentHelperTest.java
@@ -92,7 +92,7 @@
 
         PaymentItem total = new PaymentItem(new PaymentCurrencyAmount("CAD", "200"));
 
-        List<PaymentItem> displayItems = new ArrayList<PaymentItem>();
+        List<PaymentItem> displayItems = new ArrayList<>();
         displayItems.add(new PaymentItem(new PaymentCurrencyAmount("CAD", "50")));
         displayItems.add(new PaymentItem(new PaymentCurrencyAmount("CAD", "150")));
 
@@ -111,7 +111,7 @@
                         /* requestShipping= */ true,
                         /* shippingType= */ "delivery");
 
-        List<PaymentShippingOption> shippingOptions = new ArrayList<PaymentShippingOption>();
+        List<PaymentShippingOption> shippingOptions = new ArrayList<>();
         shippingOptions.add(
                 new PaymentShippingOption(
                         "shippingId",
@@ -227,7 +227,7 @@
 
         PaymentItem total = new PaymentItem(new PaymentCurrencyAmount("CAD", "200"));
 
-        List<PaymentItem> displayItems = new ArrayList<PaymentItem>();
+        List<PaymentItem> displayItems = new ArrayList<>();
         displayItems.add(new PaymentItem(new PaymentCurrencyAmount("CAD", "50")));
         displayItems.add(new PaymentItem(new PaymentCurrencyAmount("CAD", "150")));
 
@@ -300,7 +300,7 @@
 
         PaymentItem total = new PaymentItem(new PaymentCurrencyAmount("CAD", "200"));
 
-        List<PaymentItem> displayItems = new ArrayList<PaymentItem>();
+        List<PaymentItem> displayItems = new ArrayList<>();
         displayItems.add(new PaymentItem(new PaymentCurrencyAmount("CAD", "50")));
         displayItems.add(new PaymentItem(new PaymentCurrencyAmount("CAD", "150")));
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/permissions/PermissionUpdateMessageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/permissions/PermissionUpdateMessageTest.java
index bb92af7..5b6b0f6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/permissions/PermissionUpdateMessageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/permissions/PermissionUpdateMessageTest.java
@@ -79,17 +79,16 @@
                 List<String> requestablePermissions,
                 List<String> policyRevokedPermissions) {
             mHasPermissions =
-                    new HashSet<>(
-                            hasPermissions == null ? new ArrayList<String>() : hasPermissions);
+                    new HashSet<>(hasPermissions == null ? new ArrayList<>() : hasPermissions);
             mRequestablePermissions =
                     new HashSet<>(
                             requestablePermissions == null
-                                    ? new ArrayList<String>()
+                                    ? new ArrayList<>()
                                     : requestablePermissions);
             mPolicyRevokedPermissions =
                     new HashSet<>(
                             policyRevokedPermissions == null
-                                    ? new ArrayList<String>()
+                                    ? new ArrayList<>()
                                     : policyRevokedPermissions);
         }
 
@@ -344,7 +343,7 @@
         final String locationUrl = mTestServer.getURL(GEOLOCATION_PAGE);
         final PermissionInfo geolocationSettings =
                 ThreadUtils.runOnUiThreadBlocking(
-                        new Callable<PermissionInfo>() {
+                        new Callable<>() {
                             @Override
                             public PermissionInfo call() {
                                 return new PermissionInfo(
@@ -386,7 +385,7 @@
 
             final WebContents webContents =
                     ThreadUtils.runOnUiThreadBlocking(
-                            new Callable<WebContents>() {
+                            new Callable<>() {
                                 @Override
                                 public WebContents call() {
                                     return mActivityTestRule
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/profiles/ProfileTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/profiles/ProfileTest.java
index f4fe3494..af0213c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/profiles/ProfileTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/profiles/ProfileTest.java
@@ -337,14 +337,6 @@
                                         profileId, /* createIfNeeded= */ true));
         Assert.assertNotNull(profile4);
         Assert.assertSame(profile2, profile4);
-
-        // Ask for an existing profile with getOrCreateOffTheRecordProfile() and expect getting the
-        // existing profile.
-        Profile profile5 =
-                ThreadUtils.runOnUiThreadBlocking(
-                        () -> mRegularProfile.getOrCreateOffTheRecordProfile(profileId));
-        Assert.assertNotNull(profile5);
-        Assert.assertSame(profile2, profile5);
     }
 
     /** Tests createIfNeeded parameter of getPrimaryOtrProfile. */
@@ -380,13 +372,5 @@
                         () -> mRegularProfile.getPrimaryOtrProfile(/* createIfNeeded= */ true));
         Assert.assertNotNull(profile4);
         Assert.assertSame(profile2, profile4);
-
-        // Ask for an existing profile with getOrCreatePrimaryOtrProfile() and expect getting the
-        // existing profile.
-        Profile profile5 =
-                ThreadUtils.runOnUiThreadBlocking(
-                        () -> mRegularProfile.getOrCreatePrimaryOtrProfile());
-        Assert.assertNotNull(profile5);
-        Assert.assertSame(profile2, profile5);
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceTest.java
index afa853a6..2c5a9e0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceTest.java
@@ -89,7 +89,7 @@
 
         Assert.assertTrue(
                 ThreadUtils.runOnUiThreadBlocking(
-                        new Callable<Boolean>() {
+                        new Callable<>() {
                             @Override
                             public Boolean call() {
                                 return mTemplateUrlService.isLoaded();
@@ -111,7 +111,7 @@
             throws ExecutionException {
         GURL result =
                 ThreadUtils.runOnUiThreadBlocking(
-                        new Callable<GURL>() {
+                        new Callable<>() {
                             @Override
                             public GURL call() {
                                 return mTemplateUrlService.getUrlForContextualSearchQuery(
@@ -137,7 +137,7 @@
             throws ExecutionException {
         String result =
                 ThreadUtils.runOnUiThreadBlocking(
-                        new Callable<String>() {
+                        new Callable<>() {
                             @Override
                             public String call() {
                                 return mTemplateUrlService.getUrlForSearchQuery(
@@ -162,7 +162,7 @@
 
         Assert.assertTrue(
                 ThreadUtils.runOnUiThreadBlocking(
-                        new Callable<Boolean>() {
+                        new Callable<>() {
                             @Override
                             public Boolean call() {
                                 return mTemplateUrlService.isLoaded();
@@ -362,7 +362,7 @@
 
         Assert.assertTrue(
                 ThreadUtils.runOnUiThreadBlocking(
-                        new Callable<Boolean>() {
+                        new Callable<>() {
                             @Override
                             public Boolean call() {
                                 return mTemplateUrlService.isLoaded();
@@ -372,9 +372,9 @@
         validateSearchQuery("cat", null, null);
         Map<String, String> params = new HashMap();
         params.put("xyz", "a");
-        validateSearchQuery("cat", new ArrayList<String>(Arrays.asList("xyz=a")), params);
+        validateSearchQuery("cat", new ArrayList<>(Arrays.asList("xyz=a")), params);
         params.put("abc", "b");
-        validateSearchQuery("cat", new ArrayList<String>(Arrays.asList("xyz=a", "abc=b")), params);
+        validateSearchQuery("cat", new ArrayList<>(Arrays.asList("xyz=a", "abc=b")), params);
     }
 
     private boolean setPlayAPISearchEngine(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/share/ShareUrlTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/share/ShareUrlTest.java
index 88d0cff..ed81aee 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/share/ShareUrlTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/share/ShareUrlTest.java
@@ -50,7 +50,7 @@
 
     @Before
     public void setup() {
-        Mockito.doReturn(new WeakReference<Activity>(mActivity)).when(mWindow).getActivity();
+        Mockito.doReturn(new WeakReference<>(mActivity)).when(mWindow).getActivity();
     }
 
     private void assertCorrectUrl(final String originalUrl, final String sharedUrl) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorTest.java
index 23bf301..b7e886d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorTest.java
@@ -390,7 +390,7 @@
     }
 
     private static Matcher<View> withTopMargin(final int expected) {
-        return new TypeSafeMatcher<View>() {
+        return new TypeSafeMatcher<>() {
             private int mActual;
 
             @Override
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/AutofillTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/AutofillTest.java
index 862c6bd83..c4dd88f1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/AutofillTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/AutofillTest.java
@@ -197,7 +197,7 @@
     private List<Autofill> getClientAutofillProfiles() throws JSONException {
         List<Pair<String, JSONObject>> entities =
                 SyncTestUtil.getLocalData(mSyncTestRule.getTargetContext(), AUTOFILL_TYPE);
-        List<Autofill> autofills = new ArrayList<Autofill>(entities.size());
+        List<Autofill> autofills = new ArrayList<>(entities.size());
         for (Pair<String, JSONObject> entity : entities) {
             String id = entity.first;
             JSONObject profile = entity.second;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/BookmarksTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/BookmarksTest.java
index ea355a9..10aaa9b5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/BookmarksTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/BookmarksTest.java
@@ -492,7 +492,7 @@
             List<Pair<String, JSONObject>> rawBookmarks =
                     SyncTestUtil.getLocalData(
                             mSyncTestRule.getTargetContext(), BOOKMARKS_TYPE_STRING);
-            List<Bookmark> bookmarks = new ArrayList<Bookmark>(rawBookmarks.size());
+            List<Bookmark> bookmarks = new ArrayList<>(rawBookmarks.size());
             for (Pair<String, JSONObject> rawBookmark : rawBookmarks) {
                 String id = rawBookmark.first;
                 JSONObject json = rawBookmark.second;
@@ -518,7 +518,7 @@
                     mSyncTestRule
                             .getFakeServerHelper()
                             .getSyncEntitiesByDataType(DataType.BOOKMARKS);
-            List<Bookmark> bookmarks = new ArrayList<Bookmark>(entities.size());
+            List<Bookmark> bookmarks = new ArrayList<>(entities.size());
             for (SyncEntity entity : entities) {
                 String id = entity.getIdString();
                 String parentId = entity.getParentIdString();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeServerHelper.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeServerHelper.java
index 0a1efca..d520dc3 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeServerHelper.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeServerHelper.java
@@ -113,7 +113,7 @@
                         () ->
                                 FakeServerHelperJni.get()
                                         .getSyncEntitiesByDataType(mNativeFakeServer, dataType));
-        List<SyncEntity> entities = new ArrayList<SyncEntity>(serializedEntities.length);
+        List<SyncEntity> entities = new ArrayList<>(serializedEntities.length);
         for (byte[] serializedEntity : serializedEntities) {
             entities.add(SyncEntity.parseFrom(serializedEntity));
         }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabUmaTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabUmaTest.java
index 173f1e5..f32e7b9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabUmaTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabUmaTest.java
@@ -29,8 +29,6 @@
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.TabbedModeTabDelegateFactory;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.magic_stack.ModuleRegistry;
-import org.chromium.chrome.browser.share.ShareDelegate;
 import org.chromium.chrome.browser.ui.RootUiCoordinator;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
@@ -80,7 +78,7 @@
         return new TabbedModeTabDelegateFactory(
                 sActivityTestRule.getActivity(),
                 visibilityDelegate,
-                new ObservableSupplierImpl<ShareDelegate>(),
+                new ObservableSupplierImpl<>(),
                 null,
                 CallbackUtils.emptyRunnable(),
                 rootUiCoordinator.getBottomSheetController(),
@@ -101,7 +99,7 @@
                 null,
                 null,
                 rootUiCoordinator.getToolbarManager().getTabStripHeightSupplier(),
-                new OneshotSupplierImpl<ModuleRegistry>(),
+                new OneshotSupplierImpl<>(),
                 new ObservableSupplierImpl<>(),
                 cta.getStartupMetricsTracker());
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTestUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTestUtils.java
index 31995ca11..fff14081 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTestUtils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTestUtils.java
@@ -148,7 +148,7 @@
             GURL url,
             @ShoppingServiceResponse int expectedResponse) {
         doAnswer(
-                        new Answer<Void>() {
+                        new Answer<>() {
                             @Override
                             public Void answer(InvocationOnMock invocation) {
                                 ProductInfoCallback callback =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaverImplTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaverImplTest.java
index faba2446..a98c905 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaverImplTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaverImplTest.java
@@ -568,7 +568,7 @@
 
         Assert.assertEquals(
                 "Entry " + i + " group count mismatch.",
-                new HashSet<String>(groupTitles.values()).size(),
+                new HashSet<>(groupTitles.values()).size(),
                 recentBulk.getTabGroupIdToTitleMap().size());
         for (int j = 0; j < expectedTabs.size(); j++) {
             Assert.assertEquals(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/RestoreMigrateTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/RestoreMigrateTest.java
index c0544a69..d794f73b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/RestoreMigrateTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/RestoreMigrateTest.java
@@ -63,7 +63,7 @@
     private void writeStateFile(final TabModelSelector selector, int index) throws IOException {
         TabModelSelectorMetadata data =
                 ThreadUtils.runOnUiThreadBlocking(
-                        new Callable<TabModelSelectorMetadata>() {
+                        new Callable<>() {
                             @Override
                             public TabModelSelectorMetadata call() throws Exception {
                                 return TabPersistentStore.saveTabModelSelectorMetadata(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java
index 7d6bd942..b5e27ca 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java
@@ -174,7 +174,7 @@
             mMockTabContentManager = mock(TabContentManager.class);
             mTabPersistentStore =
                     ThreadUtils.runOnUiThreadBlocking(
-                            new Callable<TabPersistentStore>() {
+                            new Callable<>() {
                                 @Override
                                 public TabPersistentStore call() {
                                     TabPersistencePolicy persistencePolicy =
@@ -196,7 +196,7 @@
             NextTabPolicySupplier nextTabPolicySupplier = () -> NextTabPolicy.HIERARCHICAL;
 
             Callable<TabModelImpl> callable =
-                    new Callable<TabModelImpl>() {
+                    new Callable<>() {
                         @Override
                         public TabModelImpl call() {
                             TabRemover tabRemover =
@@ -1397,7 +1397,7 @@
      */
     private static boolean restoredFromDisk(Tab tab) throws ExecutionException {
         return ThreadUtils.runOnUiThreadBlocking(
-                new Callable<Boolean>() {
+                new Callable<>() {
                     @Override
                     public Boolean call() {
                         if (tab.getUserDataHost().getUserData(MockTabAttributes.class) == null) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhoneTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhoneTest.java
index c81e005..ceafb3b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhoneTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhoneTest.java
@@ -86,7 +86,6 @@
 import org.chromium.chrome.browser.toolbar.optional_button.ButtonDataImpl;
 import org.chromium.chrome.browser.toolbar.optional_button.OptionalButtonCoordinator;
 import org.chromium.chrome.browser.toolbar.top.ToolbarPhone.VisualState;
-import org.chromium.chrome.browser.ui.appmenu.AppMenuCoordinator;
 import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
 import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
@@ -226,7 +225,7 @@
                     // Has to be created on the main thread.
                     MenuButtonCoordinator realMenuButtonCoordinator =
                             new MenuButtonCoordinator(
-                                    new OneshotSupplierImpl<AppMenuCoordinator>(),
+                                    new OneshotSupplierImpl<>(),
                                     new TestControlsVisibilityDelegate(),
                                     mActivityTestRule.getActivity().getWindowAndroid(),
                                     mFocusFunction,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/undo_tab_close_snackbar/UndoBarControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/undo_tab_close_snackbar/UndoBarControllerTest.java
index 14403b5..67c2043 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/undo_tab_close_snackbar/UndoBarControllerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/undo_tab_close_snackbar/UndoBarControllerTest.java
@@ -793,7 +793,7 @@
 
     private Snackbar getCurrentSnackbar() throws ExecutionException {
         return ThreadUtils.runOnUiThreadBlocking(
-                new Callable<Snackbar>() {
+                new Callable<>() {
                     @Override
                     public Snackbar call() {
                         return mSnackbarManager.getCurrentSnackbarForTesting();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java
index 72fc19b..63a317f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java
@@ -465,7 +465,7 @@
                                         WebContentsUtils.getFocusedFrame(webContents)));
         Assert.assertTrue("Did not get a focused frame", rfh != null);
         final CountDownLatch latch = new CountDownLatch(1);
-        final AtomicReference<String> result = new AtomicReference<String>();
+        final AtomicReference<String> result = new AtomicReference<>();
         // The JS execution needs to be started on the UI thread to avoid hitting a DCHECK.
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/ArTestRuleUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/ArTestRuleUtils.java
index 15751e5..56e39c8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/ArTestRuleUtils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/ArTestRuleUtils.java
@@ -85,11 +85,11 @@
      *     an ArTestRule for a supported ChromeActivity.
      */
     public static ArrayList<ParameterSet> generateDefaultTestRuleParameters() {
-        ArrayList<ParameterSet> parameters = new ArrayList<ParameterSet>();
+        ArrayList<ParameterSet> parameters = new ArrayList<>();
         parameters.add(
                 new ParameterSet()
                         .value(
-                                new Callable<ChromeTabbedActivityArTestRule>() {
+                                new Callable<>() {
                                     @Override
                                     public ChromeTabbedActivityArTestRule call() {
                                         return new ChromeTabbedActivityArTestRule();
@@ -99,7 +99,7 @@
         parameters.add(
                 new ParameterSet()
                         .value(
-                                new Callable<CustomTabActivityArTestRule>() {
+                                new Callable<>() {
                                     @Override
                                     public CustomTabActivityArTestRule call() {
                                         return new CustomTabActivityArTestRule();
@@ -109,7 +109,7 @@
         parameters.add(
                 new ParameterSet()
                         .value(
-                                new Callable<WebappActivityArTestRule>() {
+                                new Callable<>() {
                                     @Override
                                     public WebappActivityArTestRule call() {
                                         return new WebappActivityArTestRule();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrCardboardTestRuleUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrCardboardTestRuleUtils.java
index 03cad8c..5ea538d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrCardboardTestRuleUtils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrCardboardTestRuleUtils.java
@@ -52,11 +52,11 @@
      *     a VrTestRule for a supported ChromeActivity.
      */
     public static ArrayList<ParameterSet> generateDefaultTestRuleParameters() {
-        ArrayList<ParameterSet> parameters = new ArrayList<ParameterSet>();
+        ArrayList<ParameterSet> parameters = new ArrayList<>();
         parameters.add(
                 new ParameterSet()
                         .value(
-                                new Callable<ChromeTabbedActivityVrCardboardTestRule>() {
+                                new Callable<>() {
                                     @Override
                                     public ChromeTabbedActivityVrCardboardTestRule call() {
                                         return new ChromeTabbedActivityVrCardboardTestRule();
@@ -67,7 +67,7 @@
         parameters.add(
                 new ParameterSet()
                         .value(
-                                new Callable<CustomTabActivityVrCardboardTestRule>() {
+                                new Callable<>() {
                                     @Override
                                     public CustomTabActivityVrCardboardTestRule call() {
                                         return new CustomTabActivityVrCardboardTestRule();
@@ -78,7 +78,7 @@
         parameters.add(
                 new ParameterSet()
                         .value(
-                                new Callable<WebappActivityVrCardboardTestRule>() {
+                                new Callable<>() {
                                     @Override
                                     public WebappActivityVrCardboardTestRule call() {
                                         return new WebappActivityVrCardboardTestRule();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/XrTestRuleUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/XrTestRuleUtils.java
index 800fac1..f378199e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/XrTestRuleUtils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/XrTestRuleUtils.java
@@ -37,11 +37,11 @@
      *     an XrTestRule for a supported ChromeActivity.
      */
     public static ArrayList<ParameterSet> generateDefaultTestRuleParameters() {
-        ArrayList<ParameterSet> parameters = new ArrayList<ParameterSet>();
+        ArrayList<ParameterSet> parameters = new ArrayList<>();
         parameters.add(
                 new ParameterSet()
                         .value(
-                                new Callable<ChromeTabbedActivityXrTestRule>() {
+                                new Callable<>() {
                                     @Override
                                     public ChromeTabbedActivityXrTestRule call() {
                                         return new ChromeTabbedActivityXrTestRule();
@@ -52,7 +52,7 @@
         parameters.add(
                 new ParameterSet()
                         .value(
-                                new Callable<CustomTabActivityXrTestRule>() {
+                                new Callable<>() {
                                     @Override
                                     public CustomTabActivityXrTestRule call() {
                                         return new CustomTabActivityXrTestRule();
@@ -63,7 +63,7 @@
         parameters.add(
                 new ParameterSet()
                         .value(
-                                new Callable<WebappActivityXrTestRule>() {
+                                new Callable<>() {
                                     @Override
                                     public WebappActivityXrTestRule call() {
                                         return new WebappActivityXrTestRule();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
index 55453d5..b4d524d0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
@@ -513,7 +513,7 @@
         creationData.startUrl =
                 mTestServer.getURL("/chrome/test/data/banners/manifest_test_page.html");
 
-        List<Integer> expectedUpdateReasons = new ArrayList<Integer>();
+        List<Integer> expectedUpdateReasons = new ArrayList<>();
         creationData.name += "!";
         creationData.shortName += "!";
         creationData.backgroundColor -= 1;
@@ -554,7 +554,7 @@
         creationData.startUrl =
                 mTestServer.getURL("/chrome/test/data/banners/manifest_test_page.html");
 
-        List<Integer> expectedUpdateReasons = new ArrayList<Integer>();
+        List<Integer> expectedUpdateReasons = new ArrayList<>();
 
         boolean expectIconChange = false;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappDisplayModeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappDisplayModeTest.java
index 419e1a8..7b5f539 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappDisplayModeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappDisplayModeTest.java
@@ -5,10 +5,8 @@
 package org.chromium.chrome.browser.webapps;
 
 import android.graphics.Color;
-import android.net.Uri;
 import android.os.Build.VERSION_CODES;
 import android.view.View;
-import android.widget.TextView;
 
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
@@ -27,9 +25,7 @@
 import org.chromium.blink.mojom.DisplayMode;
 import org.chromium.chrome.browser.browserservices.intents.WebappConstants;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.omnibox.UrlBar;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.R;
 import org.chromium.chrome.test.util.browser.webapps.WebappTestPage;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.test.util.JavaScriptUtils;
@@ -43,10 +39,6 @@
 @Batch(Batch.PER_CLASS)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 public class WebappDisplayModeTest {
-    private static final String WEB_APP_PAGE_TITLE = "Web app banner test page";
-
-    private static final String WEB_APP_PATH = "/chrome/test/data/banners/manifest_test_page.html";
-
     @Rule public final WebappActivityTestRule mActivityTestRule = new WebappActivityTestRule();
 
     @Test
@@ -102,21 +94,9 @@
         WebappActivity activity = startActivity(DisplayMode.MINIMAL_UI, "");
 
         Assert.assertFalse(isFullscreen(activity));
-        Assert.assertTrue(activity.getToolbarManager().getToolbarLayoutForTesting().isShown());
+        Assert.assertFalse(activity.getToolbarManager().getToolbarLayoutForTesting().isShown());
 
         Assert.assertEquals(Color.CYAN, activity.getToolbarManager().getPrimaryColor());
-        Assert.assertEquals(
-                "Web App title should be displayed on the title bar",
-                WEB_APP_PAGE_TITLE,
-                ((TextView) activity.findViewById(R.id.title_bar)).getText());
-        Assert.assertEquals(
-                "URL Bar should display URL authority",
-                Uri.parse(mActivityTestRule.getTestServer().getURL(WEB_APP_PATH)).getAuthority(),
-                ((UrlBar) activity.findViewById(R.id.url_bar)).getText().toString());
-        Assert.assertEquals(
-                "CCT Close button should not be visible",
-                View.GONE,
-                activity.findViewById(R.id.close_button).getVisibility());
     }
 
     private String getJavascriptResult(WebContents webContents, String js) {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/IntentHandlerRobolectricTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/IntentHandlerRobolectricTest.java
index 257b5113..17cd71c 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/IntentHandlerRobolectricTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/IntentHandlerRobolectricTest.java
@@ -194,7 +194,7 @@
     private ShadowKeyguardManager mShadowKeyguardManager;
 
     private void processUrls(String[] urls, boolean isValid) {
-        List<String> failedTests = new ArrayList<String>();
+        List<String> failedTests = new ArrayList<>();
 
         for (String url : urls) {
             mIntent.setData(Uri.parse(url));
@@ -302,7 +302,7 @@
     @SmallTest
     @Feature({"Android-AppBase"})
     public void testRejectedGoogleChromeSchemeUrls() {
-        List<String> failedTests = new ArrayList<String>();
+        List<String> failedTests = new ArrayList<>();
 
         for (String url : REJECTED_GOOGLECHROME_URLS) {
             mIntent.setData(Uri.parse(url));
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 959c837f..d5e481a 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
@@ -441,7 +441,7 @@
 
         BookmarkId bookmarkId = mock(BookmarkId.class);
         doReturn(bookmarkId).when(mBookmarkModel).getUserBookmarkIdForTab(any());
-        doReturn(new ArrayList<BookmarkId>())
+        doReturn(new ArrayList<>())
                 .when(mBookmarkModel)
                 .getBookmarksOfType(eq(PowerBookmarkType.SHOPPING));
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillLocalCardEditorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillLocalCardEditorTest.java
index 5345c171..c30974d 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillLocalCardEditorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillLocalCardEditorTest.java
@@ -208,7 +208,7 @@
         // Mock a card recognition logic
         when(mMockPersonalDataManagerJni.getBasicCardIssuerNetwork(anyString(), anyBoolean()))
                 .thenAnswer(
-                        new Answer<String>() {
+                        new Answer<>() {
                             @Override
                             public String answer(InvocationOnMock invocation) throws Throwable {
                                 String cardNumber = invocation.getArgument(0);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillLocalIbanEditorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillLocalIbanEditorTest.java
index cd86dfe..f182f66ea 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillLocalIbanEditorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillLocalIbanEditorTest.java
@@ -103,7 +103,7 @@
         // Mock isValidIban to return true for specific test IBANs.
         when(mMockPersonalDataManager.isValidIban(anyString()))
                 .thenAnswer(
-                        new Answer<Boolean>() {
+                        new Answer<>() {
                             @Override
                             public Boolean answer(InvocationOnMock invocation) {
                                 List<String> validIbans =
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/background_task_scheduler/NativeBackgroundTaskTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/background_task_scheduler/NativeBackgroundTaskTest.java
index dc20dd3..92a4388 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/background_task_scheduler/NativeBackgroundTaskTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/background_task_scheduler/NativeBackgroundTaskTest.java
@@ -266,7 +266,7 @@
         switch (setup) {
             case SUCCESS:
                 doAnswer(
-                                new Answer<Void>() {
+                                new Answer<>() {
                                     @Override
                                     public Void answer(InvocationOnMock invocation) {
                                         mBrowserParts.getValue().finishNativeInitialization();
@@ -278,7 +278,7 @@
                 break;
             case FAILURE:
                 doAnswer(
-                                new Answer<Void>() {
+                                new Answer<>() {
                                     @Override
                                     public Void answer(InvocationOnMock invocation) {
                                         mBrowserParts.getValue().onStartupFailure(null);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/backup/ChromeBackupAgentTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/backup/ChromeBackupAgentTest.java
index 0c166c38..70a78ab 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/backup/ChromeBackupAgentTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/backup/ChromeBackupAgentTest.java
@@ -533,7 +533,7 @@
 
         when(backupData.getKey())
                 .thenAnswer(
-                        new Answer<String>() {
+                        new Answer<>() {
                             private int mPos;
 
                             @Override
@@ -544,7 +544,7 @@
 
         when(backupData.getDataSize())
                 .thenAnswer(
-                        new Answer<Integer>() {
+                        new Answer<>() {
                             private int mPos;
 
                             @Override
@@ -555,7 +555,7 @@
 
         when(backupData.readEntityData(any(byte[].class), anyInt(), anyInt()))
                 .thenAnswer(
-                        new Answer<Integer>() {
+                        new Answer<>() {
                             private int mPos;
 
                             @Override
@@ -570,7 +570,7 @@
 
         when(backupData.readNextHeader())
                 .thenAnswer(
-                        new Answer<Boolean>() {
+                        new Answer<>() {
                             private int mPos;
 
                             @Override
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
index f7b22dd..1864ac3 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
@@ -13,7 +13,6 @@
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
-import android.content.pm.ResolveInfo;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.RemoteException;
@@ -160,7 +159,7 @@
                 mClient.createLaunchIntentForTwa(
                         RuntimeEnvironment.application,
                         "mailto:miranda@example.com",
-                        new ArrayList<ResolveInfo>()));
+                        new ArrayList<>()));
     }
 
     private static Token createDummyToken() {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/permissiondelegation/NotificationChannelPreserverTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/permissiondelegation/NotificationChannelPreserverTest.java
index 96ffe36384..7ba048f 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/permissiondelegation/NotificationChannelPreserverTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/permissiondelegation/NotificationChannelPreserverTest.java
@@ -133,7 +133,7 @@
 
     private void setChannelStatus(boolean enabled) {
         doAnswer(
-                        new Answer<Void>() {
+                        new Answer<>() {
                             @Override
                             public Void answer(InvocationOnMock invocation) throws Throwable {
                                 String channelIdArg = invocation.getArgument(0);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityBrowserControlsVisibilityManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityBrowserControlsVisibilityManagerTest.java
index 5b22af1..d9f280e 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityBrowserControlsVisibilityManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityBrowserControlsVisibilityManagerTest.java
@@ -15,14 +15,12 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Rect;
-import android.os.Build;
 import android.view.ContextThemeWrapper;
 
 import androidx.browser.customtabs.CustomTabsIntent;
@@ -43,7 +41,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.blink.mojom.DisplayMode;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.cc.input.BrowserControlsState;
@@ -55,11 +52,9 @@
 import org.chromium.chrome.browser.customtabs.content.TabObserverRegistrar;
 import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarCoordinator;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.util.browser.webapps.WebApkIntentDataProviderBuilder;
 import org.chromium.components.browser_ui.desktop_windowing.AppHeaderState;
-import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateManager;
 import org.chromium.components.security_state.ConnectionSecurityLevel;
 import org.chromium.components.security_state.SecurityStateModel;
 import org.chromium.components.security_state.SecurityStateModelJni;
@@ -78,7 +73,6 @@
     @Mock SecurityStateModel.Natives mSecurityStateMocks;
     @Mock public CustomTabToolbarCoordinator mToolbarCoordinator;
     @Mock public CloseButtonVisibilityManager mCloseButtonVisibilityManager;
-    @Mock DesktopWindowStateManager mDesktopWindowStateManager;
 
     @Mock TrustedWebActivityBrowserControlsVisibilityManager mController;
 
@@ -108,86 +102,12 @@
         assertFalse(getLastCloseButtonVisibility());
     }
 
-    /** Browser controls should be shown for WebAPKs with 'minimal-ui' display mode. */
+    /** Browser controls should not be shown for WebAPKs with 'minimal-ui' display mode. */
     @Test
     public void testMinimalUiDisplayMode() {
         mController = buildController(buildWebApkIntentDataProvider(DisplayMode.MINIMAL_UI));
         mController.updateIsInAppMode(true);
-        assertEquals(BrowserControlsState.BOTH, getLastBrowserControlsState());
-        assertFalse(getLastCloseButtonVisibility());
-    }
-
-    @Test
-    public void testMinimalUiInitInDesktopWindowing_HideBrowserControls() {
-        setupDesktopWindowing(/* isInDesktopWindow= */ true);
-        mController = buildController(buildWebApkIntentDataProvider(DisplayMode.MINIMAL_UI));
-        mController.updateIsInAppMode(true);
         assertEquals(BrowserControlsState.HIDDEN, getLastBrowserControlsState());
-        assertTrue(
-                "Close button should be visible for future layout", getLastCloseButtonVisibility());
-    }
-
-    @Test
-    public void testMinimalUiEnterDesktopWindowing_HideBrowserControls() {
-        setupDesktopWindowing(/* isInDesktopWindow= */ false);
-        mController = buildController(buildWebApkIntentDataProvider(DisplayMode.MINIMAL_UI));
-        mController.updateIsInAppMode(true);
-        setupDesktopWindowing(/* isInDesktopWindow= */ true);
-
-        mController.onDesktopWindowingModeChanged(mAppHeaderState.isInDesktopWindow());
-        assertEquals(BrowserControlsState.HIDDEN, getLastBrowserControlsState());
-        assertTrue(
-                "Close button should be visible for future layout", getLastCloseButtonVisibility());
-    }
-
-    @Test
-    public void testMinimalUiEnterDesktopWindowingNotInAppMode_NoVisibilityChanges() {
-        setupDesktopWindowing(/* isInDesktopWindow= */ false);
-        mController = buildController(buildWebApkIntentDataProvider(DisplayMode.MINIMAL_UI));
-        mController.updateIsInAppMode(false);
-        setupDesktopWindowing(/* isInDesktopWindow= */ true);
-
-        mController.onDesktopWindowingModeChanged(mAppHeaderState.isInDesktopWindow());
-        verifyNoInteractions(mToolbarCoordinator);
-    }
-
-    @Test
-    @Config(sdk = Build.VERSION_CODES.VANILLA_ICE_CREAM)
-    @EnableFeatures({ChromeFeatureList.ANDROID_MINIMAL_UI_LARGE_SCREEN})
-    public void testMinimalUiExitDesktopWindowingInAppMode_ShowBrowserControls() {
-        setupDesktopWindowing(/* isInDesktopWindow= */ true);
-        mController = buildController(buildWebApkIntentDataProvider(DisplayMode.MINIMAL_UI));
-        mController.updateIsInAppMode(true);
-        setupDesktopWindowing(/* isInDesktopWindow= */ false);
-
-        mController.onDesktopWindowingModeChanged(mAppHeaderState.isInDesktopWindow());
-        assertEquals(
-                "Browser controls should be shown",
-                BrowserControlsState.BOTH,
-                getLastBrowserControlsState());
-        assertFalse("Close button should be hidden in minimal ui", getLastCloseButtonVisibility());
-    }
-
-    @Test
-    @Config(sdk = Build.VERSION_CODES.VANILLA_ICE_CREAM)
-    @EnableFeatures({ChromeFeatureList.ANDROID_MINIMAL_UI_LARGE_SCREEN})
-    public void testMinUiExitDwAndEnterAppMode_KeepBrowserControlsHidden() {
-        // navigate out of scope in DW
-        mController = buildController(buildWebApkIntentDataProvider(DisplayMode.MINIMAL_UI));
-        setupDesktopWindowing(/* isInDesktopWindow= */ true);
-        mController.updateIsInAppMode(false);
-        assertEquals(
-                "Browser controls should be visible",
-                BrowserControlsState.BOTH,
-                getLastBrowserControlsState());
-
-        // exit DW and navigate back into the web app scope
-        setupDesktopWindowing(/* isInDesktopWindow= */ false);
-        mController.updateIsInAppMode(true);
-        assertEquals(
-                "Should keep browser controls visible",
-                BrowserControlsState.BOTH,
-                getLastBrowserControlsState());
     }
 
     /**
@@ -201,17 +121,6 @@
         assertEquals(BrowserControlsState.HIDDEN, getLastBrowserControlsState());
     }
 
-    @Test
-    public void testStandaloneEnterDesktopWindowingInAppMode_NoVisibilityChanges() {
-        setupDesktopWindowing(/* isInDesktopWindow= */ false);
-        mController = buildController(buildWebApkIntentDataProvider(DisplayMode.STANDALONE));
-        mController.updateIsInAppMode(true);
-        setupDesktopWindowing(/* isInDesktopWindow= */ true);
-
-        mController.onDesktopWindowingModeChanged(mAppHeaderState.isInDesktopWindow());
-        assertEquals(BrowserControlsState.HIDDEN, getLastBrowserControlsState());
-    }
-
     /**
      * Browser controls should be shown for WebAPKs with 'standalone' display mode when outside of
      * WebAPK's scope.
@@ -268,43 +177,6 @@
                 "Close button should be visible for future layout", getLastCloseButtonVisibility());
     }
 
-    @Test
-    @EnableFeatures({ChromeFeatureList.ANDROID_MINIMAL_UI_LARGE_SCREEN})
-    public void testTwaMinimalUiEnterDesktopWindowing_KeepBrowserControlsHidden() {
-        setupDesktopWindowing(/* isInDesktopWindow= */ false);
-        var intent = buildTwaIntent();
-        intent.putExtra(
-                TrustedWebActivityIntentBuilder.EXTRA_DISPLAY_MODE,
-                new TrustedWebActivityDisplayMode.MinimalUiMode().toBundle());
-        mController = buildController(buildCustomTabIntentProvider(intent));
-        mController.updateIsInAppMode(true);
-        setupDesktopWindowing(/* isInDesktopWindow= */ true);
-
-        mController.onDesktopWindowingModeChanged(mAppHeaderState.isInDesktopWindow());
-        assertEquals(
-                "Browser controls should be hidden",
-                BrowserControlsState.HIDDEN,
-                getLastBrowserControlsState());
-        assertTrue(
-                "Close button should be visible for future layout", getLastCloseButtonVisibility());
-    }
-
-    @Test
-    @EnableFeatures({ChromeFeatureList.ANDROID_MINIMAL_UI_LARGE_SCREEN})
-    public void testTwaMinimalUiEnterDesktopWindowingNotInAppMode_DoNotUpdateAnything() {
-        setupDesktopWindowing(/* isInDesktopWindow= */ false);
-        var intent = buildTwaIntent();
-        intent.putExtra(
-                TrustedWebActivityIntentBuilder.EXTRA_DISPLAY_MODE,
-                new TrustedWebActivityDisplayMode.MinimalUiMode().toBundle());
-        mController = buildController(buildCustomTabIntentProvider(intent));
-        mController.updateIsInAppMode(false);
-        setupDesktopWindowing(/* isInDesktopWindow= */ true);
-
-        mController.onDesktopWindowingModeChanged(mAppHeaderState.isInDesktopWindow());
-        verifyNoInteractions(mToolbarCoordinator);
-    }
-
     private void setTabSecurityLevel(int securityLevel) {
         doReturn(securityLevel).when(mController).getSecurityLevel(any());
     }
@@ -337,7 +209,6 @@
                         mTabProvider,
                         mToolbarCoordinator,
                         mCloseButtonVisibilityManager,
-                        mDesktopWindowStateManager,
                         intentDataProvider));
     }
 
@@ -358,10 +229,4 @@
                 .setVisibility(lastCloseButtonVisiblity.capture());
         return lastCloseButtonVisiblity.getValue();
     }
-
-    private void setupDesktopWindowing(final boolean isInDesktopWindow) {
-        mAppHeaderState =
-                new AppHeaderState(APP_WINDOW_RECT, WIDEST_UNOCCLUDED_RECT, isInDesktopWindow);
-        when(mDesktopWindowStateManager.getAppHeaderState()).thenReturn(mAppHeaderState);
-    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TwaVerifierTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TwaVerifierTest.java
index f303628..3e44ac9 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TwaVerifierTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TwaVerifierTest.java
@@ -58,7 +58,7 @@
     public void setUp() {
 
         when(mIntentDataProvider.getUrlToLoad()).thenReturn(INITIAL_URL);
-        HashSet<Origin> trustedOrigins = new HashSet<Origin>();
+        HashSet<Origin> trustedOrigins = new HashSet<>();
         Collections.addAll(
                 trustedOrigins, Origin.create(INITIAL_URL), Origin.create(ADDITIONAL_ORIGIN));
         when(mIntentDataProvider.getAllTrustedWebActivityOrigins()).thenReturn(trustedOrigins);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java
index f989256a..2477c5f4 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java
@@ -814,26 +814,7 @@
     public void testTabStripHeightTransition_Hide() {
         mStripLayoutHelperManager.setTabStripTreeProviderForTesting(mTabStripTreeProvider);
 
-        // Call without tab strip transition.
         float yOffset = 10;
-        mStripLayoutHelperManager.getUpdatedSceneOverlayTree(
-                new RectF(), new RectF(), mRenderHost.getResourceManager(), yOffset);
-        verify(mTabStripTreeProvider)
-                .pushAndUpdateStrip(
-                        any(),
-                        any(),
-                        any(),
-                        any(),
-                        any(),
-                        /* yOffset= */ eq(yOffset),
-                        anyInt(),
-                        anyInt(),
-                        eq(mToolbarPrimaryColor),
-                        /* scrimOpacity= */ eq(0f),
-                        anyFloat(),
-                        anyFloat(),
-                        anyFloat());
-
         // With tab strip transition, the yOffset will be forced to be 0.
         mTabStripHeightSupplier.set(0);
         mStripLayoutHelperManager.onHeightChanged(0, /* applyScrimOverlay= */ true);
@@ -1040,8 +1021,8 @@
                         anyFloat(),
                         anyFloat());
 
-        // When transition finished while tabs strip showing, yOffset will be forwarded to cc
-        // correctly.
+        // When transition finished while tabs strip showing, yOffset will be applied by viz, so
+        // the layer should be offset to 0.
         mStripLayoutHelperManager.onHeightTransitionFinished();
         mStripLayoutHelperManager.getUpdatedSceneOverlayTree(
                 new RectF(), new RectF(), mRenderHost.getResourceManager(), yOffset);
@@ -1052,7 +1033,7 @@
                         any(),
                         any(),
                         any(),
-                        /* yOffset= */ eq(yOffset),
+                        /* yOffset= */ eq(0f),
                         anyInt(),
                         anyInt(),
                         eq(scrimColor),
@@ -1444,6 +1425,55 @@
                         & mStripLayoutHelperManager.getStripVisibilityState());
     }
 
+    @Test
+    public void testVisibilityConstraintAndOffsetOverride() {
+        mStripLayoutHelperManager.setTabStripTreeProviderForTesting(mTabStripTreeProvider);
+        doReturn(false).when(mBrowserControlStateProvider).isVisibilityForced();
+
+        float yOffset = 10;
+        mStripLayoutHelperManager.getUpdatedSceneOverlayTree(
+                new RectF(), new RectF(), mRenderHost.getResourceManager(), yOffset);
+
+        // When visibility isn't forced, and when we're not in a height transition, the offset
+        // should always be 0, to position the controls at their fully visible positions.
+        verify(mTabStripTreeProvider)
+                .pushAndUpdateStrip(
+                        any(),
+                        any(),
+                        any(),
+                        any(),
+                        any(),
+                        /* yOffset= */ eq(0f),
+                        anyInt(),
+                        anyInt(),
+                        anyInt(),
+                        anyFloat(),
+                        anyFloat(),
+                        anyFloat(),
+                        anyFloat());
+
+        doReturn(true).when(mBrowserControlStateProvider).isVisibilityForced();
+        mStripLayoutHelperManager.getUpdatedSceneOverlayTree(
+                new RectF(), new RectF(), mRenderHost.getResourceManager(), yOffset);
+
+        // When visibility is forced, use the provided offset.
+        verify(mTabStripTreeProvider)
+                .pushAndUpdateStrip(
+                        any(),
+                        any(),
+                        any(),
+                        any(),
+                        any(),
+                        /* yOffset= */ eq(yOffset),
+                        anyInt(),
+                        anyInt(),
+                        anyInt(),
+                        anyFloat(),
+                        anyFloat(),
+                        anyFloat(),
+                        anyFloat());
+    }
+
     private void resizeDesktopWindowAndTriggerFadeTransition(boolean showStrip) {
         // Rerun initialization after setting the FF.
         ToolbarFeatures.setIsTabStripLayoutOptimizationEnabledForTesting(true);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinatorTest.java
index 65fca986..9e271a24 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinatorTest.java
@@ -626,7 +626,7 @@
                         /* additionalNavigationParams= */ null);
 
         final WindowAndroid windowAndroid = Mockito.mock(WindowAndroid.class);
-        doReturn(new WeakReference<Activity>(mActivity)).when(windowAndroid).getActivity();
+        doReturn(new WeakReference<>(mActivity)).when(windowAndroid).getActivity();
 
         List<Pair<Integer, ModelList>> rawItems = new ArrayList<>();
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTranslationImplTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTranslationImplTest.java
index 96233d8..06bb1d75 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTranslationImplTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTranslationImplTest.java
@@ -37,7 +37,7 @@
     private static final ArrayList<String> ENGLISH_AND_SPANISH;
 
     static {
-        ArrayList<String> langs = new ArrayList<String>();
+        ArrayList<String> langs = new ArrayList<>();
         langs.add(ENGLISH);
         langs.add(SPANISH);
         ENGLISH_AND_SPANISH = langs;
@@ -46,7 +46,7 @@
     private static final ArrayList<String> ENGLISH_LIST;
 
     static {
-        ArrayList<String> langs = new ArrayList<String>();
+        ArrayList<String> langs = new ArrayList<>();
         langs.add(ENGLISH);
         ENGLISH_LIST = langs;
     }
@@ -66,9 +66,7 @@
     @Test
     @Feature("TranslateUtilities")
     public void testNeedsTranslationEmptyFluentLanguages() {
-        doReturn(new ArrayList<String>())
-                .when(mTranslateBridgeWrapperMock)
-                .getNeverTranslateLanguages();
+        doReturn(new ArrayList<>()).when(mTranslateBridgeWrapperMock).getNeverTranslateLanguages();
         assertThat(mImpl.needsTranslation(ENGLISH), is(true));
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/cookies/CanonicalCookieTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/cookies/CanonicalCookieTest.java
index 3af0627c..087689c 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/cookies/CanonicalCookieTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/cookies/CanonicalCookieTest.java
@@ -78,7 +78,7 @@
 
     @Test
     public void testSaveRestoreEmptyList() throws Exception {
-        doSaveRestoreCookiesListTest(new ArrayList<CanonicalCookie>());
+        doSaveRestoreCookiesListTest(new ArrayList<>());
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegateUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegateUnitTest.java
index fc137e7..9c608683 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegateUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegateUnitTest.java
@@ -153,7 +153,7 @@
                         mBookmarkModelSupplier,
                         mVerifier,
                         CustomTabsUiType.AUTH_TAB,
-                        /* menuEntries= */ new ArrayList<String>(),
+                        /* menuEntries= */ new ArrayList<>(),
                         /* isOpenedByChrome= */ true,
                         /* showShare= */ true,
                         /* showStar= */ true,
@@ -189,7 +189,7 @@
                         mBookmarkModelSupplier,
                         mVerifier,
                         CustomTabsUiType.AUTH_TAB,
-                        /* menuEntries= */ new ArrayList<String>(),
+                        /* menuEntries= */ new ArrayList<>(),
                         /* isOpenedByChrome= */ true,
                         /* showShare= */ true,
                         /* showStar= */ true,
@@ -220,7 +220,7 @@
                         mBookmarkModelSupplier,
                         mVerifier,
                         CustomTabsUiType.AUTH_TAB,
-                        /* menuEntries= */ new ArrayList<String>(),
+                        /* menuEntries= */ new ArrayList<>(),
                         /* isOpenedByChrome= */ true,
                         /* showShare= */ true,
                         /* showStar= */ true,
@@ -259,7 +259,7 @@
                         mBookmarkModelSupplier,
                         mVerifier,
                         CustomTabsUiType.POPUP,
-                        /* menuEntries= */ new ArrayList<String>(),
+                        /* menuEntries= */ new ArrayList<>(),
                         /* isOpenedByChrome= */ true,
                         /* showShare= */ true,
                         /* showStar= */ true,
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabFileUtilsUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabFileUtilsUnitTest.java
index f0c491d..d3126bb 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabFileUtilsUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabFileUtilsUnitTest.java
@@ -40,7 +40,7 @@
     public void testDeletableMetadataSelection_NoFiles() {
         List<File> deletableFiles =
                 CustomTabFileUtils.getFilesForDeletion(
-                        System.currentTimeMillis(), new ArrayList<File>());
+                        System.currentTimeMillis(), new ArrayList<>());
         assertThat(deletableFiles, Matchers.emptyIterableOf(File.class));
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/ButtonVisibilityRuleUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/ButtonVisibilityRuleUnitTest.java
index 27fa0a1b..e96196a 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/ButtonVisibilityRuleUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/ButtonVisibilityRuleUnitTest.java
@@ -4,8 +4,10 @@
 
 package org.chromium.chrome.browser.customtabs.features.toolbar;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -18,11 +20,14 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.browserservices.intents.CustomButtonParams.ButtonType;
+import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider.CustomTabsButtonState;
 import org.chromium.chrome.browser.customtabs.features.toolbar.ButtonVisibilityRule.ButtonId;
 
 /** Tests for button visibility rule in CustomTab Toolbar. */
@@ -117,6 +122,64 @@
     }
 
     @Test
+    public void minimizeButtonGetsHigherPriorityThanDefaultDevButton_minimizeOverCustom1() {
+        // Close, menu, minimize.
+        // custom1 = share/default deprioritized over minimize.
+        int toolbarWidth = 48 * 3 + 68;
+        ButtonVisibilityRule buttonVisibilityRule =
+                new ButtonVisibilityRule(68, /* activated= */ true);
+        buttonVisibilityRule.setToolbarWidth(toolbarWidth);
+        buttonVisibilityRule.setCustomButtonState(
+                CustomTabsButtonState.BUTTON_STATE_DEFAULT,
+                CustomTabsButtonState.BUTTON_STATE_DEFAULT);
+        buttonVisibilityRule.addButton(ButtonId.CLOSE, mCloseButton, true);
+        buttonVisibilityRule.addButton(ButtonId.MENU, mMenuButton, true);
+        buttonVisibilityRule.addButtonForCustomAction(
+                ButtonId.CUSTOM_1, mCustom1Button, true, ButtonType.CCT_SHARE_BUTTON);
+        buttonVisibilityRule.addButton(ButtonId.MINIMIZE, mMinimizeButton, true);
+        buttonVisibilityRule.addButton(ButtonId.MTB, mOptionalButton, true);
+
+        ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
+        verify(mCustom1Button, atLeastOnce()).setVisibility(captor.capture());
+        assertEquals(View.GONE, (long) captor.getValue());
+
+        // Verify the last visibility set to the minimize button is VISIBLE.
+        captor = ArgumentCaptor.forClass(Integer.class);
+        verify(mMinimizeButton, atLeastOnce()).setVisibility(captor.capture());
+        assertEquals(View.VISIBLE, (long) captor.getValue());
+    }
+
+    @Test
+    public void minimizeButtonGetsHigherPriorityThanDefaultDevButton_minimizeAndCustom2() {
+        // Close, menu, minimize, custom2
+        // custom1 = open-in-browser/default deprioritized over minimize and custom2(share).
+        int toolbarWidth = 48 * 4 + 68;
+        ButtonVisibilityRule buttonVisibilityRule =
+                new ButtonVisibilityRule(68, /* activated= */ true);
+        buttonVisibilityRule.setToolbarWidth(toolbarWidth);
+        buttonVisibilityRule.setCustomButtonState(
+                CustomTabsButtonState.BUTTON_STATE_DEFAULT,
+                CustomTabsButtonState.BUTTON_STATE_DEFAULT);
+        buttonVisibilityRule.addButton(ButtonId.CLOSE, mCloseButton, true);
+        buttonVisibilityRule.addButton(ButtonId.MENU, mMenuButton, true);
+        buttonVisibilityRule.addButtonForCustomAction(
+                ButtonId.CUSTOM_1, mCustom1Button, true, ButtonType.CCT_OPEN_IN_BROWSER_BUTTON);
+        buttonVisibilityRule.addButtonForCustomAction(
+                ButtonId.CUSTOM_2, mCustom2Button, true, ButtonType.CCT_SHARE_BUTTON);
+        buttonVisibilityRule.addButton(ButtonId.MINIMIZE, mMinimizeButton, true);
+        buttonVisibilityRule.addButton(ButtonId.MTB, mOptionalButton, true);
+
+        ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
+        verify(mCustom1Button, atLeastOnce()).setVisibility(captor.capture());
+        assertEquals(View.GONE, (long) captor.getValue());
+
+        // Verify the last visibility set to the minimize button is VISIBLE.
+        captor = ArgumentCaptor.forClass(Integer.class);
+        verify(mMinimizeButton, atLeastOnce()).setVisibility(captor.capture());
+        assertEquals(View.VISIBLE, (long) captor.getValue());
+    }
+
+    @Test
     public void disabledOperation() {
         int toolbarWidth =
                 48 * 3 + 68; // close, menu, custom1 + url. No space for minimize, optional
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/download/OfflineContentAvailabilityStatusProviderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/download/OfflineContentAvailabilityStatusProviderTest.java
index 33a3567..e9f779d 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/download/OfflineContentAvailabilityStatusProviderTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/download/OfflineContentAvailabilityStatusProviderTest.java
@@ -63,14 +63,13 @@
                 provider.isSuggestedContentAvailable());
 
         // Add some non-prefetch items.
-        provider.onItemsAdded(
-                new ArrayList<OfflineItem>(Arrays.asList(mTransientItem, mPersistentItem)));
+        provider.onItemsAdded(new ArrayList<>(Arrays.asList(mTransientItem, mPersistentItem)));
         assertFalse(
                 "Added non-prefetch content should not affect prefetch content availability.",
                 provider.isSuggestedContentAvailable());
 
         // Add a prefetch item.
-        provider.onItemsAdded(new ArrayList<OfflineItem>(Arrays.asList(mPrefetchItem)));
+        provider.onItemsAdded(new ArrayList<>(Arrays.asList(mPrefetchItem)));
         assertTrue(
                 "Prefetch content should be available after adding prefetch content.",
                 provider.isSuggestedContentAvailable());
@@ -97,19 +96,19 @@
                 provider.isPersistentContentAvailable());
 
         // Add a transient item.
-        provider.onItemsAdded(new ArrayList<OfflineItem>(Arrays.asList(mTransientItem)));
+        provider.onItemsAdded(new ArrayList<>(Arrays.asList(mTransientItem)));
         assertFalse(
                 "Added transient content should not affect persistent content availability.",
                 provider.isSuggestedContentAvailable());
 
         // Add a persistent item.
-        provider.onItemsAdded(new ArrayList<OfflineItem>(Arrays.asList(mPersistentItem)));
+        provider.onItemsAdded(new ArrayList<>(Arrays.asList(mPersistentItem)));
         assertTrue(
                 "Persistent content should be available after adding persistent content.",
                 provider.isPersistentContentAvailable());
 
         // Add a persistent prefetch item.
-        provider.onItemsAdded(new ArrayList<OfflineItem>(Arrays.asList(mPrefetchItem)));
+        provider.onItemsAdded(new ArrayList<>(Arrays.asList(mPrefetchItem)));
         assertTrue(
                 "Persistent content should still be available after adding persistent prefetch "
                         + "content.",
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListenerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListenerUnitTest.java
index 7083ed76..d521b1ca 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListenerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListenerUnitTest.java
@@ -71,7 +71,7 @@
     @Before
     public void setup() {
         mContext = ContextUtils.getApplicationContext();
-        mLayoutStateProviderSupplierImpl = new OneshotSupplierImpl<LayoutStateProvider>();
+        mLayoutStateProviderSupplierImpl = new OneshotSupplierImpl<>();
         mLayoutStateProviderSupplierImpl.set(mLayoutStateProvider);
         mChromeTabbedOnDragListener =
                 new ChromeTabbedOnDragListener(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinatorTest.java
index 2983d0fc..86e35b1 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinatorTest.java
@@ -143,7 +143,7 @@
     }
 
     private static class TestTabModel extends EmptyTabModel {
-        public final ArrayList<TabModelObserver> mObservers = new ArrayList<TabModelObserver>();
+        public final ArrayList<TabModelObserver> mObservers = new ArrayList<>();
 
         @Override
         public void addObserver(TabModelObserver observer) {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceMediatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceMediatorTest.java
index 9e3a7e2..e6b8e80 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceMediatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceMediatorTest.java
@@ -6,6 +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.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -410,6 +412,72 @@
     }
 
     @Test
+    @EnableFeatures(ChromeFeatureList.FEED_HEADER_REMOVAL)
+    public void testshowOrHideFeed_GseOnAndThenOffAndThenOn_feedHeaderRemovalEnabled() {
+        when(mPrefService.getBoolean(Pref.ENABLE_SNIPPETS)).thenReturn(true);
+        when(mPrefService.getBoolean(Pref.ARTICLES_LIST_VISIBLE)).thenReturn(true);
+        when(mPrefService.getBoolean(Pref.ENABLE_SNIPPETS_BY_DSE)).thenReturn(true);
+        when(mUrlService.isDefaultSearchEngineGoogle()).thenReturn(true);
+
+        // When FEED_HEADER_REMOVAL is enabled, sectionHeaderModel is passed as NULL.
+        mFeedSurfaceMediator =
+                createMediator(
+                        FeedSurfaceCoordinator.StreamTabId.FOR_YOU, /* sectionHeaderModel= */ null);
+        mFeedSurfaceMediator.updateContent();
+        mFeedSurfaceMediator.showOrHideFeed();
+
+        assertNotNull(mFeedSurfaceMediator.getCurrentStreamForTesting());
+
+        // Turn GSE off.
+        when(mPrefService.getBoolean(Pref.ENABLE_SNIPPETS_BY_DSE)).thenReturn(false);
+        when(mUrlService.isDefaultSearchEngineGoogle()).thenReturn(false);
+        mFeedSurfaceMediator.updateContent();
+        mFeedSurfaceMediator.showOrHideFeed();
+
+        assertNull(mFeedSurfaceMediator.getCurrentStreamForTesting());
+
+        // Turn GSE on.
+        when(mPrefService.getBoolean(Pref.ENABLE_SNIPPETS_BY_DSE)).thenReturn(true);
+        when(mUrlService.isDefaultSearchEngineGoogle()).thenReturn(true);
+        mFeedSurfaceMediator.updateContent();
+        mFeedSurfaceMediator.showOrHideFeed();
+
+        assertNotNull(mFeedSurfaceMediator.getCurrentStreamForTesting());
+    }
+
+    @Test
+    @DisableFeatures(ChromeFeatureList.FEED_HEADER_REMOVAL)
+    public void testshowOrHideFeed_GseOnAndThenOffAndThenOn_feedHeaderRemovalDisabled() {
+        when(mPrefService.getBoolean(Pref.ENABLE_SNIPPETS)).thenReturn(true);
+        when(mPrefService.getBoolean(Pref.ARTICLES_LIST_VISIBLE)).thenReturn(true);
+        when(mPrefService.getBoolean(Pref.ENABLE_SNIPPETS_BY_DSE)).thenReturn(true);
+        when(mUrlService.isDefaultSearchEngineGoogle()).thenReturn(true);
+
+        PropertyModel model = SectionHeaderListProperties.create(TOOLBAR_HEIGHT);
+        mFeedSurfaceMediator = createMediator(FeedSurfaceCoordinator.StreamTabId.FOR_YOU, model);
+        mFeedSurfaceMediator.updateContent();
+        mFeedSurfaceMediator.showOrHideFeed();
+
+        assertNotNull(mFeedSurfaceMediator.getCurrentStreamForTesting());
+
+        // Turn GSE off.
+        when(mPrefService.getBoolean(Pref.ENABLE_SNIPPETS_BY_DSE)).thenReturn(false);
+        when(mUrlService.isDefaultSearchEngineGoogle()).thenReturn(false);
+        mFeedSurfaceMediator.updateContent();
+        mFeedSurfaceMediator.showOrHideFeed();
+
+        assertNull(mFeedSurfaceMediator.getCurrentStreamForTesting());
+
+        // Turn GSE on.
+        when(mPrefService.getBoolean(Pref.ENABLE_SNIPPETS_BY_DSE)).thenReturn(true);
+        when(mUrlService.isDefaultSearchEngineGoogle()).thenReturn(true);
+        mFeedSurfaceMediator.updateContent();
+        mFeedSurfaceMediator.showOrHideFeed();
+
+        assertNotNull(mFeedSurfaceMediator.getCurrentStreamForTesting());
+    }
+
+    @Test
     public void testshowOrHideFeed_signedInGseOn() {
         when(mPrefService.getBoolean(Pref.ENABLE_SNIPPETS)).thenReturn(true);
         when(mPrefService.getBoolean(Pref.ENABLE_SNIPPETS_BY_DSE)).thenReturn(true);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerCompatUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerCompatUnitTest.java
index 95b8b5446..9d14e623 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerCompatUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerCompatUnitTest.java
@@ -135,7 +135,7 @@
         mHost = new UserDataHost();
         doReturn(mHost).when(mTab).getUserDataHost();
 
-        mAreControlsHidden = new ObservableSupplierImpl<Boolean>();
+        mAreControlsHidden = new ObservableSupplierImpl<>();
         mMultiWindowModeStateDispatcher = new MultiWindowModeStateDispatcherImpl(mActivity);
         mFullscreenHtmlApiHandlerCompat =
                 new FullscreenHtmlApiHandlerCompat(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerLegacyUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerLegacyUnitTest.java
index 338ca00..d55c49fa 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerLegacyUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerLegacyUnitTest.java
@@ -69,7 +69,7 @@
         mHost = new UserDataHost();
         doReturn(mHost).when(mTab).getUserDataHost();
 
-        mAreControlsHidden = new ObservableSupplierImpl<Boolean>();
+        mAreControlsHidden = new ObservableSupplierImpl<>();
         mFullscreenHtmlApiHandlerLegacy =
                 new FullscreenHtmlApiHandlerLegacy(
                         mActivity, mAreControlsHidden, false, mMultiWindowModeStateDispatcher) {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/metrics/VariationsSessionTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/metrics/VariationsSessionTest.java
index 414f26f..fca2f4e 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/metrics/VariationsSessionTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/metrics/VariationsSessionTest.java
@@ -64,7 +64,7 @@
     @Test
     public void testGetRestrictModeValue() {
         mSession.getRestrictModeValue(
-                new Callback<String>() {
+                new Callback<>() {
                     @Override
                     public void onResult(String restrictMode) {}
                 });
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeUnitTest.java
index 761acb7..b29c661 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeUnitTest.java
@@ -131,7 +131,7 @@
         Callback<List<OfflinePageItem>> callback = createMultipleItemCallback(itemCount);
         mBridge.getAllPages(callback);
 
-        List<OfflinePageItem> itemList = new ArrayList<OfflinePageItem>();
+        List<OfflinePageItem> itemList = new ArrayList<>();
         verify(callback, times(1)).onResult(itemList);
     }
 
@@ -145,7 +145,7 @@
         Callback<List<OfflinePageItem>> callback = createMultipleItemCallback(itemCount);
         mBridge.getAllPages(callback);
 
-        List<OfflinePageItem> itemList = new ArrayList<OfflinePageItem>();
+        List<OfflinePageItem> itemList = new ArrayList<>();
         itemList.add(TEST_OFFLINE_PAGE_ITEM);
         itemList.add(TEST_OFFLINE_PAGE_ITEM);
         verify(callback, times(1)).onResult(itemList);
@@ -162,7 +162,7 @@
         List<ClientId> list = new ArrayList<>();
         mBridge.getPagesByClientIds(list, callback);
 
-        List<OfflinePageItem> itemList = new ArrayList<OfflinePageItem>();
+        List<OfflinePageItem> itemList = new ArrayList<>();
         verify(callback, times(1)).onResult(itemList);
     }
 
@@ -180,7 +180,7 @@
         list.add(secondClientId);
         mBridge.getPagesByClientIds(list, callback);
 
-        List<OfflinePageItem> itemList = new ArrayList<OfflinePageItem>();
+        List<OfflinePageItem> itemList = new ArrayList<>();
         itemList.add(TEST_OFFLINE_PAGE_ITEM);
         itemList.add(TEST_OFFLINE_PAGE_ITEM);
         verify(callback, times(1)).onResult(itemList);
@@ -275,7 +275,7 @@
 
     private Callback<Integer> createDeletePageCallback() {
         return spy(
-                new Callback<Integer>() {
+                new Callback<>() {
                     @Override
                     public void onResult(Integer result) {}
                 });
@@ -283,7 +283,7 @@
 
     private void answerNativeGetAllPages(final int itemCount) {
         Answer<Void> answer =
-                new Answer<Void>() {
+                new Answer<>() {
                     @Override
                     public Void answer(InvocationOnMock invocation) {
                         List<OfflinePageItem> result = mResultArgument.getValue();
@@ -307,7 +307,7 @@
 
     private void answerGetPagesByClientIds(final int itemCount) {
         Answer<Void> answer =
-                new Answer<Void>() {
+                new Answer<>() {
                     @Override
                     public Void answer(InvocationOnMock invocation) {
                         List<OfflinePageItem> result = mResultArgument.getValue();
@@ -340,7 +340,7 @@
 
     private void answerDeletePagesByOfflineIds(final int itemCount) {
         Answer<Void> answer =
-                new Answer<Void>() {
+                new Answer<>() {
                     @Override
                     public Void answer(InvocationOnMock invocation) {
                         long[] offlineIds = mOfflineIdsArgument.getValue();
@@ -367,7 +367,7 @@
 
     private void answerDeletePagesByClientIds(final int itemCount) {
         Answer<Void> answer =
-                new Answer<Void>() {
+                new Answer<>() {
                     @Override
                     public Void answer(InvocationOnMock invocation) {
                         String[] namespaces = mNamespacesArgument.getValue();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java
index 696348d6..1c18370 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java
@@ -61,8 +61,8 @@
     }
 
     private static class MockOmahaDelegate extends OmahaDelegate {
-        private final List<Integer> mPostResults = new ArrayList<Integer>();
-        private final List<Boolean> mGenerateAndPostRequestResults = new ArrayList<Boolean>();
+        private final List<Integer> mPostResults = new ArrayList<>();
+        private final List<Boolean> mGenerateAndPostRequestResults = new ArrayList<>();
 
         private final boolean mIsOnTablet;
         private final boolean mIsInForeground;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/page_info/PermissionParamsListBuilderUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/page_info/PermissionParamsListBuilderUnitTest.java
index 8fca190..8f5027b 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/page_info/PermissionParamsListBuilderUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/page_info/PermissionParamsListBuilderUnitTest.java
@@ -92,7 +92,7 @@
     }
 
     private static class FakePermissionDelegate implements AndroidPermissionDelegate {
-        private static final List<String> sBlockedPermissions = new ArrayList<String>();
+        private static final List<String> sBlockedPermissions = new ArrayList<>();
 
         private static void blockPermission(String permission) {
             sBlockedPermissions.add(permission);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/quickactionsearchwidget/QuickActionSearchWidgetProviderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/quickactionsearchwidget/QuickActionSearchWidgetProviderTest.java
index 762bece..4b397ee8 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/quickactionsearchwidget/QuickActionSearchWidgetProviderTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/quickactionsearchwidget/QuickActionSearchWidgetProviderTest.java
@@ -184,7 +184,7 @@
         updateReportedWidgetSizes(mOptionsWidgetA, new SizeF(80, 80), new SizeF(400, 40));
         // Lastly, set the empty array of sizes.
         mOptionsWidgetA.putParcelableArrayList(
-                AppWidgetManager.OPTION_APPWIDGET_SIZES, new ArrayList<SizeF>());
+                AppWidgetManager.OPTION_APPWIDGET_SIZES, new ArrayList<>());
         mWidgetProvider.getRemoteViews(mContext, mPreferences, mOptionsWidgetA);
 
         // There are 2 fake widgets that we work with, so expect both being evaluated
@@ -203,7 +203,7 @@
         // Lastly, set a different array of sizes and confirm it is used instead.
         mOptionsWidgetA.putParcelableArrayList(
                 AppWidgetManager.OPTION_APPWIDGET_SIZES,
-                new ArrayList<SizeF>(Arrays.asList(new SizeF(50, 50))));
+                new ArrayList<>(Arrays.asList(new SizeF(50, 50))));
         mWidgetProvider.getRemoteViews(mContext, mPreferences, mOptionsWidgetA);
 
         // Only one call is expected here, because we declare only one size in our list.
@@ -220,7 +220,7 @@
         // supported.
         mOptionsWidgetA.putParcelableArrayList(
                 AppWidgetManager.OPTION_APPWIDGET_SIZES,
-                new ArrayList<SizeF>(Arrays.asList(new SizeF(50, 50))));
+                new ArrayList<>(Arrays.asList(new SizeF(50, 50))));
         mWidgetProvider.getRemoteViews(mContext, mPreferences, mOptionsWidgetA);
 
         // There are 2 fake widgets that we work with, so expect both being evaluated
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/UrlSimilarityScorerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/UrlSimilarityScorerUnitTest.java
index a869ae1..d974d12a 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/UrlSimilarityScorerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/UrlSimilarityScorerUnitTest.java
@@ -344,7 +344,7 @@
     public void testFindTabWithMostSimilarUrl() {
         int bad = TabList.INVALID_TAB_INDEX;
 
-        ArrayList<GURL> candidateUrls = new ArrayList<GURL>();
+        ArrayList<GURL> candidateUrls = new ArrayList<>();
         candidateUrls.add(new GURL("https://not-example.com"));
         candidateUrls.add(new GURL("https://www.example.com/path?query#ref"));
         candidateUrls.add(new GURL("https://www.example.com/?query#ref"));
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabUnitTest.java
index 5ea5797..2a62660d 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabUnitTest.java
@@ -340,7 +340,7 @@
         mTab.onProvideAutofillVirtualStructure(mock(ViewStructure.class), 0);
         verify(mAutofillProvider, never()).onProvideAutoFillVirtualStructure(any(), anyInt());
 
-        mTab.autofill(new SparseArray<AutofillValue>());
+        mTab.autofill(new SparseArray<>());
         verify(mAutofillProvider, never()).autofill(any());
     }
 
@@ -355,7 +355,7 @@
         mTab.onProvideAutofillVirtualStructure(mock(ViewStructure.class), 0);
         verify(mAutofillProvider, never()).onProvideAutoFillVirtualStructure(any(), anyInt());
 
-        mTab.autofill(new SparseArray<AutofillValue>());
+        mTab.autofill(new SparseArray<>());
         verify(mAutofillProvider, never()).autofill(any());
     }
 
@@ -374,7 +374,7 @@
                 .onProvideAutoFillVirtualStructure(
                         structure, View.AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS);
 
-        SparseArray<AutofillValue> values = new SparseArray<AutofillValue>();
+        SparseArray<AutofillValue> values = new SparseArray<>();
         mTab.autofill(values);
         verify(mAutofillProvider).autofill(values);
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabModelObserverUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabModelObserverUnitTest.java
index e5e3a59..5bcd06d6 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabModelObserverUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabModelObserverUnitTest.java
@@ -338,7 +338,7 @@
         Token tabGroupId = new Token(3L, 4L);
         createGroup(tabGroupId, title, color, new MockTab[] {mockTab0});
         when(mTabGroupModelFilter.getLazyAllTabGroupIds(any(), anyBoolean()))
-                .thenReturn(LazyOneshotSupplier.fromValue(new HashSet<Token>()));
+                .thenReturn(LazyOneshotSupplier.fromValue(new HashSet<>()));
 
         mSavedTabGroup.collaborationId = COLLABORATION_ID;
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/PendingTabClosureManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/PendingTabClosureManagerTest.java
index 55303cb..07d9fca 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/PendingTabClosureManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/PendingTabClosureManagerTest.java
@@ -40,13 +40,13 @@
     private PendingTabClosureManager mPendingTabClosureManager;
 
     private static class FakeTabModel extends EmptyTabModel {
-        private LinkedList<Tab> mTabs = new LinkedList<Tab>();
+        private LinkedList<Tab> mTabs = new LinkedList<>();
         private int mIndex = TabModel.INVALID_TAB_INDEX;
 
         public FakeTabModel() {}
 
         public void setTabs(Tab[] tabs) {
-            mTabs = new LinkedList<Tab>(Arrays.asList(tabs));
+            mTabs = new LinkedList<>(Arrays.asList(tabs));
         }
 
         public void clear() {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/UndoTabModelUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/UndoTabModelUnitTest.java
index 012a86ab..86c7ed6 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/UndoTabModelUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/UndoTabModelUnitTest.java
@@ -1660,7 +1660,7 @@
         checkState(model, fullList, tab4, sEmptyList, fullList, tab4);
         assertFalse(model.supportsPendingClosures());
 
-        final ArrayList<Tab> lastClosedTabs = new ArrayList<Tab>();
+        final ArrayList<Tab> lastClosedTabs = new ArrayList<>();
         model.addObserver(
                 new TabModelObserver() {
                     @Override
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ui/IncognitoRestoreAppLaunchDrawBlockerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ui/IncognitoRestoreAppLaunchDrawBlockerUnitTest.java
index 6b63401..6747220 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ui/IncognitoRestoreAppLaunchDrawBlockerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ui/IncognitoRestoreAppLaunchDrawBlockerUnitTest.java
@@ -67,7 +67,7 @@
     private final ObservableSupplierImpl<TabModelSelector> mTabModelSelectorObservableSupplier =
             new ObservableSupplierImpl<>();
     private final Supplier<Intent> mIntentSupplier =
-            new Supplier<Intent>() {
+            new Supplier<>() {
                 @Nullable
                 @Override
                 public Intent get() {
@@ -75,7 +75,7 @@
                 }
             };
     private final Supplier<Boolean> mShouldIgnoreIntentSupplier =
-            new Supplier<Boolean>() {
+            new Supplier<>() {
                 @Nullable
                 @Override
                 public Boolean get() {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/usage_stats/PageViewObserverTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/usage_stats/PageViewObserverTest.java
index 1fdae82..1d37efd6 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/usage_stats/PageViewObserverTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/usage_stats/PageViewObserverTest.java
@@ -530,7 +530,7 @@
     }
 
     private ArgumentMatcher<WebsiteEvent> isStartEvent(String fqdn) {
-        return new ArgumentMatcher<WebsiteEvent>() {
+        return new ArgumentMatcher<>() {
             @Override
             public boolean matches(WebsiteEvent event) {
                 return event.getType() == WebsiteEvent.EventType.START
@@ -545,7 +545,7 @@
     }
 
     private ArgumentMatcher<WebsiteEvent> isStopEvent(String fqdn) {
-        return new ArgumentMatcher<WebsiteEvent>() {
+        return new ArgumentMatcher<>() {
             @Override
             public boolean matches(WebsiteEvent event) {
                 return event.getType() == WebsiteEvent.EventType.STOP
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappDataStorageTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappDataStorageTest.java
index fecb6a2..79c0d75 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappDataStorageTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappDataStorageTest.java
@@ -104,7 +104,7 @@
                 .apply();
         WebappDataStorage.open("test")
                 .getSplashScreenImage(
-                        new WebappDataStorage.FetchCallback<Bitmap>() {
+                        new WebappDataStorage.FetchCallback<>() {
                             @Override
                             public void onDataRetrieved(Bitmap actual) {
                                 mCallbackCalled = true;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappRegistryTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappRegistryTest.java
index 1271470..80a646d0 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappRegistryTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappRegistryTest.java
@@ -924,7 +924,7 @@
                 webApkRegistry.getWebApkSpecificsImpl(setWebappInfoForTesting);
         assertEquals(2, webApkSpecificsList.size());
 
-        Set<String> visitedScopes = new HashSet<String>();
+        Set<String> visitedScopes = new HashSet<>();
         for (WebApkSpecifics webApkSpecifics : webApkSpecificsList) {
             BrowserServicesIntentDataProvider intentDataProvider =
                     expectedIntentDataProviders.get(webApkSpecifics.getScope());
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webauthn/AuthenticatorIncognitoConfirmationBottomsheetTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webauthn/AuthenticatorIncognitoConfirmationBottomsheetTest.java
index 26670fc..a54acd3 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webauthn/AuthenticatorIncognitoConfirmationBottomsheetTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webauthn/AuthenticatorIncognitoConfirmationBottomsheetTest.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.webauthn;
 
-import android.content.Context;
 import android.widget.Button;
 
 import org.junit.After;
@@ -72,7 +71,7 @@
     public void setUp() {
         WindowAndroid windowAndroid = Mockito.mock(WindowAndroid.class);
         setWindowAndroid(windowAndroid, mWebContents);
-        Mockito.doReturn(new WeakReference<Context>(RuntimeEnvironment.application))
+        Mockito.doReturn(new WeakReference<>(RuntimeEnvironment.application))
                 .when(windowAndroid)
                 .getContext();
 
diff --git a/chrome/android/webapk/shell_apk/current_version/current_version.gni b/chrome/android/webapk/shell_apk/current_version/current_version.gni
index 09f24b9..83b789a 100644
--- a/chrome/android/webapk/shell_apk/current_version/current_version.gni
+++ b/chrome/android/webapk/shell_apk/current_version/current_version.gni
@@ -12,4 +12,4 @@
 # //chrome/android/webapk/shell_apk:webapk is changed. This includes
 # Java files, Android resource files and AndroidManifest.xml. Does not affect
 # Chrome.apk
-current_shell_apk_version = 184
+current_shell_apk_version = 185
diff --git a/chrome/android/webapk/shell_apk/javatests/src/org/chromium/webapk/shell_apk/DexLoaderTest.java b/chrome/android/webapk/shell_apk/javatests/src/org/chromium/webapk/shell_apk/DexLoaderTest.java
index ca20aff0..74d296b 100644
--- a/chrome/android/webapk/shell_apk/javatests/src/org/chromium/webapk/shell_apk/DexLoaderTest.java
+++ b/chrome/android/webapk/shell_apk/javatests/src/org/chromium/webapk/shell_apk/DexLoaderTest.java
@@ -48,8 +48,8 @@
 
     /** Monitors read files and modified files in the directory passed to the constructor. */
     private static class FileMonitor extends FileObserver {
-        public final ArrayList<String> mReadPaths = new ArrayList<String>();
-        public final ArrayList<String> mModifiedPaths = new ArrayList<String>();
+        public final ArrayList<String> mReadPaths = new ArrayList<>();
+        public final ArrayList<String> mModifiedPaths = new ArrayList<>();
 
         public FileMonitor(File directory) {
             super(directory.getPath());
diff --git a/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/HostBrowserUtilsTest.java b/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/HostBrowserUtilsTest.java
index bac5786..b453097 100644
--- a/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/HostBrowserUtilsTest.java
+++ b/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/HostBrowserUtilsTest.java
@@ -293,7 +293,7 @@
 
     @SafeVarargs
     private static String[] mergeStringArrays(String[]... toMerge) {
-        List<String> out = new ArrayList<String>();
+        List<String> out = new ArrayList<>();
         for (String[] toMergeArray : toMerge) {
             out.addAll(Arrays.asList(toMergeArray));
         }
diff --git a/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/TestBrowserInstaller.java b/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/TestBrowserInstaller.java
index 4f63cdd..59babff 100644
--- a/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/TestBrowserInstaller.java
+++ b/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/TestBrowserInstaller.java
@@ -24,7 +24,7 @@
 public class TestBrowserInstaller {
     public static final String COMPONENT_CLASS = "com.component.class.name";
 
-    Set<String> mInstalledBrowsers = new HashSet<String>();
+    Set<String> mInstalledBrowsers = new HashSet<>();
 
     /** Changes the installed browsers to the passed-in list. */
     public void setInstalledModernBrowsers(String defaultBrowserPackage, String[] newPackages) {
diff --git a/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/h2o/LaunchTest.java b/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/h2o/LaunchTest.java
index 671e7cea..76e6a7c9 100644
--- a/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/h2o/LaunchTest.java
+++ b/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/h2o/LaunchTest.java
@@ -775,7 +775,7 @@
     @SuppressWarnings("unchecked")
     private ArrayList<Intent> runActivityChain(
             Intent launchIntent, Class<? extends Activity> launchActivity, String browserPackage) {
-        ArrayList<Intent> activityIntentChain = new ArrayList<Intent>();
+        ArrayList<Intent> activityIntentChain = new ArrayList<>();
 
         // Android modifies the intent when the intent is used to launch an activity. Clone the
         // intent so as not to affect test cases which use the same intent.
diff --git a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/HostBrowserUtils.java b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/HostBrowserUtils.java
index 570d37c..2226897 100644
--- a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/HostBrowserUtils.java
+++ b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/HostBrowserUtils.java
@@ -36,7 +36,7 @@
 
     /** The package names of the browsers that support WebAPK notification delegation. */
     private static final Set<String> sBrowsersSupportingNotificationDelegation =
-            new HashSet<String>(
+            new HashSet<>(
                     Arrays.asList(
                             "com.google.android.apps.chrome",
                             "com.android.chrome",
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index 99e2ad3c..5deb4e6 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -1401,16 +1401,6 @@
         </message>
       </if>
 
-      <if expr="not is_android">
-        <!-- Desktop User Education IPH Strings -->
-        <message name="IDS_SIDE_PANEL_GENERIC_MENU_IPH" desc="The body text of the IPH describing how to find side panel entry points in the app menu.">
-          You can open bookmarks, reading mode, and more from the Chromium menu
-        </message>
-        <message name="IDS_SIDE_PANEL_GENERIC_MENU_IPH_SCREENREADER" desc="The screen reader text of the IPH describing how to find side panel entry points in the app menu. The phrase 'top right' should read as 'top left' in rtl languages.">
-          You can open bookmarks, reading mode, and more from the Chromium menu at the top right
-        </message>
-      </if>
-
       <!-- Settings API bubble -->
       <message name="IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_START_PAGES" desc="Second line in the Settings API bubble. Only shown if the secondary change by the extension was just the start pages. The triple single quotes are needed to preserve the space before and after the sentence which is needed when the language (Chrome is being translated to) uses space as word separator. Please preserve them, unless the language being translated to does not use space as word separator.">
         ''' It also controls what page is shown when you start Chromium. '''
diff --git a/chrome/app/chromium_strings_grd/IDS_SIDE_PANEL_GENERIC_MENU_IPH.png.sha1 b/chrome/app/chromium_strings_grd/IDS_SIDE_PANEL_GENERIC_MENU_IPH.png.sha1
deleted file mode 100644
index bf0c579..0000000
--- a/chrome/app/chromium_strings_grd/IDS_SIDE_PANEL_GENERIC_MENU_IPH.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-ebfe7a7afa4bd4b072dddd01f4bca081b069233c
\ No newline at end of file
diff --git a/chrome/app/chromium_strings_grd/IDS_SIDE_PANEL_GENERIC_MENU_IPH_SCREENREADER.png.sha1 b/chrome/app/chromium_strings_grd/IDS_SIDE_PANEL_GENERIC_MENU_IPH_SCREENREADER.png.sha1
deleted file mode 100644
index bf0c579..0000000
--- a/chrome/app/chromium_strings_grd/IDS_SIDE_PANEL_GENERIC_MENU_IPH_SCREENREADER.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-ebfe7a7afa4bd4b072dddd01f4bca081b069233c
\ No newline at end of file
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index a27fd50..d49816d9 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -18833,11 +18833,6 @@
           =1 {Chrome removed permissions from 1 site}
           other {Chrome removed permissions from {NUM_SITES} sites}}
         </message>
-        <message name="IDS_SETTINGS_SAFETY_HUB_UNUSED_SITE_PERMISSIONS_MENU_NOTIFICATION" desc="Title for the permissions review Safety Check module on the Magic Stack. Shown when Safety Check has identified several unused sites that have granted permissions.">
-          {NUM_SITES, plural,
-          =1 {Chrome removed permissions from 1 site}
-          other {Chrome removed permissions from {NUM_SITES} sites}}
-        </message>
         <message name="IDS_SETTINGS_SAFETY_HUB_COMPROMISED_PASSWORDS_MENU_NOTIFICATION" desc="Summary for the compromised passwords Safety Check module on the Magic Stack. Shown when Safety Check has identified insecure credentials.">
           {NUM_SITES, plural,
           =1 {Found 1 compromised password}
diff --git a/chrome/app/generated_resources_grd/IDS_SETTINGS_SAFETY_HUB_UNUSED_SITE_PERMISSIONS_MENU_NOTIFICATION.png.sha1 b/chrome/app/generated_resources_grd/IDS_SETTINGS_SAFETY_HUB_UNUSED_SITE_PERMISSIONS_MENU_NOTIFICATION.png.sha1
deleted file mode 100644
index 8e08ae4..0000000
--- a/chrome/app/generated_resources_grd/IDS_SETTINGS_SAFETY_HUB_UNUSED_SITE_PERMISSIONS_MENU_NOTIFICATION.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-07128c900c2cc7b4e536c4e268ce25ef0da82cbe
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index de1e7a9a..9beaa44 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -1394,16 +1394,6 @@
         You usually block notifications. To let this site notify you, click the notification icon on the right corner of your location bar.
       </message>
 
-      <if expr="not is_android">
-        <!-- Desktop User Education IPH Strings -->
-        <message name="IDS_SIDE_PANEL_GENERIC_MENU_IPH" desc="The body text of the IPH describing how to find side panel entry points in the Chrome menu.">
-          You can open bookmarks, reading mode, and more from the Chrome menu
-        </message>
-        <message name="IDS_SIDE_PANEL_GENERIC_MENU_IPH_SCREENREADER" desc="The screen reader text of the IPH describing how to find side panel entry points in the Chrome menu. The phrase 'top right' should read as 'top left' in rtl languages.">
-          You can open bookmarks, reading mode, and more from the Chrome menu at the top right
-        </message>
-      </if>
-
       <!-- Settings API bubble -->
       <message name="IDS_EXTENSIONS_SETTINGS_API_SECOND_LINE_START_PAGES" desc="Second line in the Settings API bubble. Only shown if the secondary change by the extension was just the start pages. The triple single quotes are needed to preserve the space before and after the sentence which is needed when the language (Chrome is being translated to) uses space as word separator. Please preserve them, unless the language being translated to does not use space as word separator.">
         ''' It also controls what page is shown when you start Chrome. '''
diff --git a/chrome/app/google_chrome_strings_grd/IDS_SIDE_PANEL_GENERIC_MENU_IPH.png.sha1 b/chrome/app/google_chrome_strings_grd/IDS_SIDE_PANEL_GENERIC_MENU_IPH.png.sha1
deleted file mode 100644
index 4d61c7ec..0000000
--- a/chrome/app/google_chrome_strings_grd/IDS_SIDE_PANEL_GENERIC_MENU_IPH.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-e8a6e251ff2f58503a89a4da839723a006880a71
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings_grd/IDS_SIDE_PANEL_GENERIC_MENU_IPH_SCREENREADER.png.sha1 b/chrome/app/google_chrome_strings_grd/IDS_SIDE_PANEL_GENERIC_MENU_IPH_SCREENREADER.png.sha1
deleted file mode 100644
index 4d61c7ec..0000000
--- a/chrome/app/google_chrome_strings_grd/IDS_SIDE_PANEL_GENERIC_MENU_IPH_SCREENREADER.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-e8a6e251ff2f58503a89a4da839723a006880a71
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 6b3d5529..bec7a9f 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -271,9 +271,6 @@
   <message name="IDS_OS_SETTINGS_FIRMWARE_UP_TO_DATE_DESCRIPTION" desc="Sublabel shown when no firmware updates are available.">
     Firmware is up to date
   </message>
-  <message name="IDS_OS_SETTINGS_FIRMWARE_DISABLED_DESCRIPTION" desc="Sublabel shown when firmware updates are disabled.">
-    Firmware updates are disabled by your administrator.
-  </message>
   <message name="IDS_OS_SETTINGS_FIRMWARE_UPDATE_AVAILABLE_DESCRIPTION" desc="Sublabel shown when firmware updates are available.">
     Update available
   </message>
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_FIRMWARE_DISABLED_DESCRIPTION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_FIRMWARE_DISABLED_DESCRIPTION.png.sha1
deleted file mode 100644
index 5381230..0000000
--- a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_FIRMWARE_DISABLED_DESCRIPTION.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-a0a3c173a22b590dd7bae9abfd6eea915212552a
\ No newline at end of file
diff --git a/chrome/app/password_manager_ui_strings.grdp b/chrome/app/password_manager_ui_strings.grdp
index e4ec9cb..12101e1 100644
--- a/chrome/app/password_manager_ui_strings.grdp
+++ b/chrome/app/password_manager_ui_strings.grdp
@@ -977,9 +977,6 @@
   <message name="IDS_PASSWORD_MANAGER_UI_PASSWORD_CHANGE_FAILED_BODY" desc="This is the description of the dialog displayed after failed password change attempt.">
     Your password wasn't updated, but you should still be able to access the site with your old password
   </message>
-  <message name="IDS_PASSWORD_MANAGER_UI_PASSWORD_CHANGE_FAILED_ACCEPT_BUTTON" desc="A button label. When clicked, Chrome will navigate to the site’s change password page. From there, the user should be able to change their password themselves, as they normally would without any help from Google Password Manager. When writing this label, keep the button label “Change it for me” in mind. This feature flow attempts to “Change it for me” for the user. If that doesn’t work, the feature flow offers a path to the user to do it themselves: “[you] Change it on the site”.">
-    Change it on the site
-  </message>
   <message name="IDS_PASSWORD_MANAGER_UI_PASSWORD_CHANGE_FAILED_ACTION" desc="Text on the button displayed on a dialog after failed password change attempt.">
     Check your password
   </message>
diff --git a/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_PASSWORD_CHANGE_FAILED_ACCEPT_BUTTON.png.sha1 b/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_PASSWORD_CHANGE_FAILED_ACCEPT_BUTTON.png.sha1
deleted file mode 100644
index f5a3137..0000000
--- a/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_PASSWORD_CHANGE_FAILED_ACCEPT_BUTTON.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-cc416625733e4d067e9f83462ffce3d003f3d6c8
\ No newline at end of file
diff --git a/chrome/app/resources/locale_settings_linux.grd b/chrome/app/resources/locale_settings_linux.grd
index 67cf33f3..7f2a01b89 100644
--- a/chrome/app/resources/locale_settings_linux.grd
+++ b/chrome/app/resources/locale_settings_linux.grd
@@ -190,6 +190,12 @@
         Latin Modern Math
       </message>
 
+      <!-- The default value for |WebPreference::fixed_font_family_map| for
+           Japanese script. -->
+      <message name="IDS_FIXED_FONT_FAMILY_JAPANESE" use_name_for_id="true">
+        Noto Sans Mono CJK JP
+      </message>
+
       <!-- The default value for |WebPreference::standard_font_family_map| for
            Japanese script. -->
       <message name="IDS_STANDARD_FONT_FAMILY_JAPANESE" use_name_for_id="true">
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 32750a49..471552e 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -1613,18 +1613,6 @@
   <message name="IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_ALLOW_AGAIN_LABEL" desc="'Unused Site Permissions' shows unused websites for which permissions have been revoked. This label is for the action to undo the revokation and allow the site permissions again.">
     Allow again
   </message>
-  <message name="IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_REMOVED_ONE_PERMISSION_LABEL" desc="'Unused Site Permissions' shows the revoked permissions of unused websites. This text describes which permission was removed and is shown if there is exactly one permission.">
-    Removed <ph name="PERMISSION">$1<ex>location</ex></ph>
-  </message>
-  <message name="IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_REMOVED_TWO_PERMISSIONS_LABEL" desc="'Unused Site Permissions' shows the revoked permissions of unused websites. This text describes which permissions were removed and is shown if there are exactly two permissions.">
-    Removed <ph name="PERMISSION_1">$1<ex>location</ex></ph>, <ph name="PERMISSION_2">$2<ex>notifications</ex></ph>
-  </message>
-  <message name="IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_REMOVED_THREE_PERMISSIONS_LABEL" desc="'Unused Site Permissions' shows the revoked permissions of unused websites. This text describes which permissions were removed and is shown if there are exactly three permissions.">
-    Removed <ph name="PERMISSION_1">$1<ex>location</ex></ph>, <ph name="PERMISSION_2">$2<ex>notifications</ex></ph>, <ph name="PERMISSION_3">$3<ex>camera</ex></ph>
-  </message>
-  <message name="IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_REMOVED_FOUR_OR_MORE_PERMISSIONS_LABEL" desc="'Unused Site Permissions' shows the revoked permissions of unused websites. This text describes which permissions were removed and is shown if there are four or more permissions. The number indicates the count of permisions that are not shown explicitly. The number is always two or larger.">
-    Removed <ph name="PERMISSION_1">$1<ex>location</ex></ph>, <ph name="PERMISSION_2">$2<ex>notifications</ex></ph>, and <ph name="COUNT">$3<ex>2</ex></ph> more
-  </message>
   <message name="IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_PRIMARY_LABEL" desc="'Unused Site Permissions' shows unused websites for which permissions have been revoked. This label is the header of the module in the site settings page. The number represents the number of unused sites.">
     {NUM_SITES, plural,
      =1 {Permissions removed from 1 site}
@@ -4252,11 +4240,6 @@
      =1 {You can stop this site from sending future notifications.}
      other {You can stop these sites from sending future notifications.}}
   </message>
-  <message name="IDS_SETTINGS_SAFETY_HUB_UNUSED_SITE_PERMISSIONS_MENU_NOTIFICATION" desc="The unused site permissions module of Safety Hub revokes permissions of sites that the user hasn't visited in a while. This label is for the notification in Chrome's three-dot menu indicating for how many sites the permission was revoked.">
-    {NUM_SITES, plural,
-      =1 {Removed permissions for 1 unused site}
-      other {Removed permissions for {NUM_SITES} unused sites}}
-  </message>
   <message name="IDS_SETTINGS_SAFETY_HUB_REVOKED_PERMISSIONS_MENU_NOTIFICATION" desc="The revoked permissions module of Safety Hub revokes permissions of sites that the user hasn't visited in a while or that send abusive notifications. This label is for the notification in Chrome's three-dot menu indicating for how many sites the permission was revoked.">
     {NUM_SITES, plural,
       =1 {Removed permissions for 1 site}
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_REMOVED_FOUR_OR_MORE_PERMISSIONS_LABEL.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_REMOVED_FOUR_OR_MORE_PERMISSIONS_LABEL.png.sha1
deleted file mode 100644
index 3b72051..0000000
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_REMOVED_FOUR_OR_MORE_PERMISSIONS_LABEL.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-1e850b432270e539dd12a4f1de39048cd98020b9
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_REMOVED_ONE_PERMISSION_LABEL.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_REMOVED_ONE_PERMISSION_LABEL.png.sha1
deleted file mode 100644
index 0e6ed56..0000000
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_REMOVED_ONE_PERMISSION_LABEL.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-36662e310263a3bdb2eed4d136fcfc4d565aa289
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_REMOVED_THREE_PERMISSIONS_LABEL.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_REMOVED_THREE_PERMISSIONS_LABEL.png.sha1
deleted file mode 100644
index 8e1272d..0000000
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_REMOVED_THREE_PERMISSIONS_LABEL.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-e56c8924a959b465fced57789a8e060a5267ebd7
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_REMOVED_TWO_PERMISSIONS_LABEL.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_REMOVED_TWO_PERMISSIONS_LABEL.png.sha1
deleted file mode 100644
index b1af8fe..0000000
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_REMOVED_TWO_PERMISSIONS_LABEL.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-0dc08bc9877481edb0ecf8c925a4397ba376634f
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SAFETY_HUB_UNUSED_SITE_PERMISSIONS_MENU_NOTIFICATION.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SAFETY_HUB_UNUSED_SITE_PERMISSIONS_MENU_NOTIFICATION.png.sha1
deleted file mode 100644
index 8bf689d..0000000
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SAFETY_HUB_UNUSED_SITE_PERMISSIONS_MENU_NOTIFICATION.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-0b8ab021e13a7016eae046c52ae29306f98450cf
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 11230ff..17cc19a 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -549,6 +549,8 @@
     "first_party_sets/first_party_sets_policy_service_factory.h",
     "flag_descriptions.cc",
     "flag_descriptions.h",
+    "font_family_cache.cc",
+    "font_family_cache.h",
     "gcm/gcm_product_util.cc",
     "gcm/gcm_product_util.h",
     "gcm/gcm_profile_service_factory.cc",
@@ -3814,8 +3816,6 @@
       "first_run/first_run.h",
       "first_run/first_run_dialog.h",
       "first_run/first_run_internal.h",
-      "font_family_cache.cc",
-      "font_family_cache.h",
       "headless/chrome_browser_main_extra_parts_headless.cc",
       "headless/chrome_browser_main_extra_parts_headless.h",
       "hid/chrome_hid_delegate.cc",
@@ -4339,6 +4339,7 @@
       "//chrome/browser/accessibility/tree_fixing:prefs",
       "//chrome/browser/accessibility/tree_fixing:service",
       "//chrome/browser/actor",
+      "//chrome/browser/actor:impl",
       "//chrome/browser/apps/app_service",
       "//chrome/browser/apps/link_capturing",
       "//chrome/browser/contextual_cueing",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 501e19a..4774897 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -8428,6 +8428,12 @@
      kOsCrOS,
      FEATURE_VALUE_TYPE(features::kAccessibilityMagnifierFollowsChromeVox)},
 
+    {"enable-accessibility-manifest-v3-accessibility-common",
+     flag_descriptions::kAccessibilityManifestV3AccessibilityCommonName,
+     flag_descriptions::kAccessibilityManifestV3AccessibilityCommonDescription,
+     kOsCrOS,
+     FEATURE_VALUE_TYPE(features::kAccessibilityManifestV3AccessibilityCommon)},
+
     {"enable-accessibility-manifest-v3-braille-ime",
      flag_descriptions::kAccessibilityManifestV3BrailleImeName,
      flag_descriptions::kAccessibilityManifestV3BrailleImeDescription, kOsCrOS,
@@ -8444,11 +8450,15 @@
      kOsCrOS,
      FEATURE_VALUE_TYPE(features::kAccessibilityManifestV3EnhancedNetworkTts)},
 
-    {"enable-accessibility-manifest-v3-accessibility-common",
-     flag_descriptions::kAccessibilityManifestV3AccessibilityCommonName,
-     flag_descriptions::kAccessibilityManifestV3AccessibilityCommonDescription,
-     kOsCrOS,
-     FEATURE_VALUE_TYPE(features::kAccessibilityManifestV3AccessibilityCommon)},
+    {"enable-accessibility-manifest-v3-espeakng",
+     flag_descriptions::kAccessibilityManifestV3EspeakNGName,
+     flag_descriptions::kAccessibilityManifestV3EspeakNGDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(::features::kAccessibilityManifestV3EspeakNGTts)},
+
+    {"enable-accessibility-manifest-v3-google-tts",
+     flag_descriptions::kAccessibilityManifestV3GoogleTtsName,
+     flag_descriptions::kAccessibilityManifestV3GoogleTtsDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(::features::kAccessibilityManifestV3GoogleTts)},
 
     {"enable-accessibility-manifest-v3-select-to-speak",
      flag_descriptions::kAccessibilityManifestV3SelectToSpeakName,
@@ -12634,13 +12644,6 @@
          autofill::features::
              kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollment)},
 
-#if BUILDFLAG(IS_CHROMEOS)
-    {"espeakng-manifest-v3",
-     flag_descriptions::kAccessibilityManifestV3EspeakNGName,
-     flag_descriptions::kAccessibilityManifestV3EspeakNGDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(::features::kAccessibilityManifestV3EspeakNGTts)},
-#endif
-
 #if BUILDFLAG(IS_ANDROID)
     {"mvc-update-view-when-model-changed",
      flag_descriptions::kMvcUpdateViewWhenModelChangedName,
diff --git a/chrome/browser/actor/BUILD.gn b/chrome/browser/actor/BUILD.gn
index 72b430d..a1de7e7 100644
--- a/chrome/browser/actor/BUILD.gn
+++ b/chrome/browser/actor/BUILD.gn
@@ -17,8 +17,23 @@
     "actor_keyed_service_factory.h",
     "actor_task.h",
     "aggregated_journal.h",
+    "tools/observation_delay_controller.h",
+    "tools/observation_delay_type.h",
+    "tools/tool_controller.h",
   ]
+  public_deps = [
+    ":types",
+    "//base",
+    "//chrome/browser/profiles",
+    "//chrome/common",
+    "//components/optimization_guide/content/browser",
+    "//components/optimization_guide/proto:optimization_guide_proto",
+    "//components/tabs:public",
+    "//content/public/browser",
+  ]
+}
 
+source_set("impl") {
   sources = [
     "actor_coordinator.cc",
     "actor_features.cc",
@@ -36,8 +51,6 @@
     "tools/navigate_tool.cc",
     "tools/navigate_tool.h",
     "tools/observation_delay_controller.cc",
-    "tools/observation_delay_controller.h",
-    "tools/observation_delay_type.h",
     "tools/page_tool.cc",
     "tools/page_tool.h",
     "tools/tool.cc",
@@ -45,18 +58,14 @@
     "tools/tool_callbacks.cc",
     "tools/tool_callbacks.h",
     "tools/tool_controller.cc",
-    "tools/tool_controller.h",
     "tools/wait_tool.cc",
     "tools/wait_tool.h",
   ]
-  public_deps = [
-    ":types",
-    "//base",
-    "//chrome/browser:browser_public_dependencies",
-    "//components/optimization_guide/content/browser",
-    "//components/optimization_guide/proto:optimization_guide_proto",
-  ]
+
+  public_deps = [ "//chrome/browser:browser_public_dependencies" ]
+
   deps = [
+    ":actor",
     "//chrome/browser:browser_process",
     "//chrome/browser/optimization_guide:optimization_guide",
     "//chrome/browser/profiles:profile",
@@ -104,6 +113,7 @@
   ]
   deps = [
     ":actor",
+    ":impl",
     ":test_support",
     "//base/test:test_support",
     "//chrome/browser/optimization_guide:test_support",
@@ -122,6 +132,7 @@
   sources = [ "site_policy_browsertest.cc" ]
   deps = [
     ":actor",
+    ":impl",
     ":test_support",
     "//base/test:test_support",
     "//chrome/browser/optimization_guide:test_support",
diff --git a/chrome/browser/actor/actor_coordinator.cc b/chrome/browser/actor/actor_coordinator.cc
index 99fef91..dc1cab2 100644
--- a/chrome/browser/actor/actor_coordinator.cc
+++ b/chrome/browser/actor/actor_coordinator.cc
@@ -25,7 +25,6 @@
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/common/actor.mojom.h"
 #include "chrome/common/actor/action_result.h"
-#include "chrome/common/actor/actor_logging.h"
 #include "chrome/common/chrome_features.h"
 #include "components/optimization_guide/content/browser/page_content_proto_provider.h"
 #include "components/optimization_guide/proto/features/actions_data.pb.h"
@@ -83,6 +82,8 @@
     : profile_(profile),
       journal_(ActorKeyedService::Get(profile)->GetJournal().GetSafeRef()) {
   CHECK(profile_);
+  // Idempotent. Enables the action blocklist if it isn't already enabled.
+  InitActionBlocklist(profile_.get());
 }
 
 ActorCoordinator::ActorCoordinator(Profile* profile, tabs::TabInterface* tab)
@@ -91,6 +92,8 @@
       tab_scoped_actions_deprecated_(true),
       tab_(tab->GetWeakPtr()) {
   CHECK(profile_);
+  // Idempotent. Enables the action blocklist if it isn't already enabled.
+  InitActionBlocklist(profile_.get());
 }
 
 ActorCoordinator::~ActorCoordinator() {
@@ -133,8 +136,10 @@
   CHECK(base::FeatureList::IsEnabled(features::kGlicActor));
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  TaskId task_id(action.task_id());
   if (tab_scoped_actions_deprecated_ && !tab_) {
-    ACTOR_LOG() << "Unable to perform action: tab has been destroyed";
+    journal_->Log(LastCommittedURLOfCurrentTask(), task_id, "Act Failed",
+                  "Unable to perform action: tab has been destroyed");
     PostTaskForActCallback(std::move(callback),
                            MakeResult(mojom::ActionResultCode::kTabWentAway));
     return;
@@ -142,8 +147,9 @@
 
   // NOTE: Improve this API by queuing the action instead.
   if (actions_) {
-    ACTOR_LOG()
-        << "Unable to perform action: task already has action in progress";
+    journal_->Log(
+        LastCommittedURLOfCurrentTask(), task_id, "Act Failed",
+        "Unable to perform action: task already has action in progress");
     PostTaskForActCallback(std::move(callback),
                            MakeResult(mojom::ActionResultCode::kError,
                                       "Task already has action in progress"));
@@ -154,7 +160,7 @@
   action_index_ = 0;
 
   MayActOnTab(*tab_, base::BindOnce(&ActorCoordinator::OnMayActOnTabResponse,
-                                    GetWeakPtr(), TaskId(action.task_id()),
+                                    GetWeakPtr(), task_id,
                                     tab_->GetContents()
                                         ->GetPrimaryMainFrame()
                                         ->GetLastCommittedOrigin()));
@@ -172,8 +178,9 @@
   }
 
   if (tab_scoped_actions_deprecated_ && !tab_.get()) {
-    ACTOR_LOG()
-        << "Unable to perform action: Tab closed while checking site policy";
+    journal_->Log(
+        LastCommittedURLOfCurrentTask(), task_id, "Act Failed",
+        "Unable to perform action: Tab closed while checking site policy");
     CompleteActions(MakeResult(mojom::ActionResultCode::kTabWentAway,
                                "Tab closed while checking site policy"));
     return;
@@ -185,7 +192,8 @@
     // A cross-origin navigation occurred before we got permission. The result
     // is no longer applicable. For now just fail.
     // TODO(mcnee): Handle this gracefully.
-    NOTIMPLEMENTED() << "Acting after cross-origin navigation occurred";
+    journal_->Log(LastCommittedURLOfCurrentTask(), task_id, "Act Failed",
+                  "Acting after cross-origin navigation occurred");
     CompleteActions(
         MakeResult(mojom::ActionResultCode::kCrossOriginNavigation,
                    "Acting after cross-origin navigation occurred"));
@@ -193,6 +201,8 @@
   }
 
   if (!may_act) {
+    journal_->Log(LastCommittedURLOfCurrentTask(), task_id, "Act Failed",
+                  "URL blocked for actions");
     CompleteActions(MakeResult(mojom::ActionResultCode::kUrlBlocked,
                                "URL blocked for actions"));
     return;
@@ -226,6 +236,8 @@
   // TODO(https://crbug.com/411462297): tabs should not be required for all
   // actions.
   if (!tab_) {
+    journal_->Log(LastCommittedURLOfCurrentTask(), task_id, "Act Failed",
+                  "The tab is no longer present");
     CompleteActions(MakeResult(mojom::ActionResultCode::kTabWentAway,
                                "The tab is no longer present."));
     return;
@@ -234,6 +246,8 @@
   RenderFrameHost* target_frame = FindTargetFrame(*tab_->GetContents(), action);
 
   if (!target_frame) {
+    journal_->Log(LastCommittedURLOfCurrentTask(), task_id, "Act Failed",
+                  "The target frame is no longer present in the tab.");
     CompleteActions(
         MakeResult(mojom::ActionResultCode::kFrameWentAway,
                    "The target frame is no longer present in the tab."));
@@ -266,7 +280,9 @@
   }
 
   if (!IsOk(*result)) {
-    ACTOR_LOG() << "Action Failed: " << ToDebugString(*result);
+    journal_->Log(LastCommittedURLOfCurrentTask(),
+                  TaskId(actions_->proto.task_id()), "Act Failed",
+                  ToDebugString(*result));
   }
 
   PostTaskForActCallback(std::move(actions_->callback), std::move(result));
@@ -288,4 +304,11 @@
   return weak_ptr_factory_.GetWeakPtr();
 }
 
+const GURL& ActorCoordinator::LastCommittedURLOfCurrentTask() {
+  if (!tab_) {
+    return GURL::EmptyGURL();
+  }
+  return tab_->GetContents()->GetLastCommittedURL();
+}
+
 }  // namespace actor
diff --git a/chrome/browser/actor/actor_coordinator.h b/chrome/browser/actor/actor_coordinator.h
index 3b95afd..4e026350 100644
--- a/chrome/browser/actor/actor_coordinator.h
+++ b/chrome/browser/actor/actor_coordinator.h
@@ -22,6 +22,7 @@
 #include "components/tabs/public/tab_interface.h"
 #include "content/public/browser/web_contents_observer.h"
 
+class GURL;
 class Profile;
 
 namespace mojo_base {
@@ -112,6 +113,8 @@
   // Fires the callback and clears `actions`.
   void CompleteActions(mojom::ActionResultPtr result);
 
+  const GURL& LastCommittedURLOfCurrentTask();
+
   static std::optional<base::TimeDelta> action_observation_delay_for_testing_;
 
   raw_ptr<Profile> profile_;
diff --git a/chrome/browser/actor/actor_coordinator_unittest.cc b/chrome/browser/actor/actor_coordinator_unittest.cc
index 45ee01f..51144dd 100644
--- a/chrome/browser/actor/actor_coordinator_unittest.cc
+++ b/chrome/browser/actor/actor_coordinator_unittest.cc
@@ -98,6 +98,10 @@
 
     ChromeRenderViewHostTestHarness::SetUp();
 
+    // TODO(crbug.com/409564704): Mock the delay so that tests can run at
+    // reasonable speed. Remove once there is a more permanent approach.
+    OverrideActionObservationDelay(base::TimeDelta());
+
     AssociateTabInterface();
   }
 
diff --git a/chrome/browser/actor/actor_keyed_service.cc b/chrome/browser/actor/actor_keyed_service.cc
index d7eb72a43..1e3a0cac 100644
--- a/chrome/browser/actor/actor_keyed_service.cc
+++ b/chrome/browser/actor/actor_keyed_service.cc
@@ -6,7 +6,24 @@
 
 #include <utility>
 
+#include "base/task/single_thread_task_runner.h"
+#include "chrome/browser/actor/actor_coordinator.h"
 #include "chrome/browser/actor/actor_keyed_service_factory.h"
+#include "chrome/browser/actor/actor_task.h"
+#include "chrome/browser/actor/task_id.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+
+namespace {
+
+// TODO(crbug.com/411462297): This is a short term hack. This code will be
+// deleted soon once StartTask stops creating new tabs implicitly. This adds a
+// 1-second delay to wait for about:blank to load. This can be replaced by ~100
+// lines of complex code that tries to precisely wait for navigation commit, but
+// that would be overkill.
+constexpr base::TimeDelta kDelayForNewTab = base::Seconds(1);
+
+}  // namespace
 
 namespace actor {
 
@@ -19,12 +36,88 @@
   return ActorKeyedServiceFactory::GetActorKeyedService(context);
 }
 
-void ActorKeyedService::AddTask(std::unique_ptr<ActorTask> task) {
-  tasks_.push_back(std::move(task));
+TaskId ActorKeyedService::AddTask(std::unique_ptr<ActorTask> task) {
+  TaskId task_id = next_task_id_.GenerateNextId();
+  tasks_[task_id] = std::move(task);
+  return task_id;
 }
 
-const std::vector<std::unique_ptr<ActorTask>>& ActorKeyedService::GetTasks() {
+const std::map<TaskId, std::unique_ptr<ActorTask>>&
+ActorKeyedService::GetTasks() {
   return tasks_;
 }
 
+void ActorKeyedService::StartTask(
+    optimization_guide::proto::BrowserStartTask task,
+    base::OnceCallback<void(optimization_guide::proto::BrowserStartTaskResult)>
+        callback) {
+  // TODO(crbug.com/411462297): This is a short term hack. This code will be
+  // deleted soon once tab_id is removed.
+  tabs::TabHandle handle(task.tab_id());
+  if (!task.tab_id()) {
+    // Get the most recently active browser for this profile.
+    Browser* browser = chrome::FindBrowserWithProfile(profile_.get());
+    // If no browser exists create one.
+    if (!browser) {
+      browser = Browser::Create(
+          Browser::CreateParams(profile_.get(), /*user_gesture=*/false));
+    }
+    // Create a new tab.
+    browser->OpenGURL(GURL(url::kAboutBlankURL),
+                      WindowOpenDisposition::NEW_FOREGROUND_TAB);
+    handle = browser->GetActiveTabInterface()->GetHandle();
+    base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&ActorKeyedService::FinishStartTask,
+                       weak_ptr_factory_.GetWeakPtr(), handle, std::move(task),
+                       std::move(callback)),
+        kDelayForNewTab);
+    return;
+  }
+
+  FinishStartTask(handle, std::move(task), std::move(callback));
+}
+
+void ActorKeyedService::FinishStartTask(
+    tabs::TabHandle handle,
+    optimization_guide::proto::BrowserStartTask task,
+    base::OnceCallback<void(optimization_guide::proto::BrowserStartTaskResult)>
+        callback) {
+  tabs::TabInterface* tab = handle.Get();
+  std::unique_ptr<actor::ActorCoordinator> actor_coordinator;
+  if (tab) {
+    actor_coordinator =
+        std::make_unique<actor::ActorCoordinator>(profile_.get(), tab);
+  } else {
+    actor_coordinator =
+        std::make_unique<actor::ActorCoordinator>(profile_.get());
+  }
+
+  auto actor_task =
+      std::make_unique<actor::ActorTask>(std::move(actor_coordinator));
+  actor::TaskId task_id = AddTask(std::move(actor_task));
+
+  optimization_guide::proto::BrowserStartTaskResult result;
+  result.set_task_id(task_id.value());
+  result.set_tab_id(handle.raw_value());
+  result.set_status(optimization_guide::proto::BrowserStartTaskResult::SUCCESS);
+  base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), std::move(result)));
+}
+
+void ActorKeyedService::StopTask(TaskId task_id) {
+  auto task = tasks_.find(task_id);
+  if (task != tasks_.end()) {
+    task->second->Stop();
+  }
+}
+
+ActorTask* ActorKeyedService::GetTask(TaskId task_id) {
+  auto task = tasks_.find(task_id);
+  if (task != tasks_.end()) {
+    return task->second.get();
+  }
+  return nullptr;
+}
+
 }  // namespace actor
diff --git a/chrome/browser/actor/actor_keyed_service.h b/chrome/browser/actor/actor_keyed_service.h
index 6ab615b..2ef5475 100644
--- a/chrome/browser/actor/actor_keyed_service.h
+++ b/chrome/browser/actor/actor_keyed_service.h
@@ -9,10 +9,16 @@
 #include <vector>
 
 #include "base/compiler_specific.h"
+#include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "chrome/browser/actor/actor_task.h"
 #include "chrome/browser/actor/aggregated_journal.h"
+#include "chrome/browser/actor/task_id.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "components/optimization_guide/proto/features/actions_data.pb.h"
+#include "components/optimization_guide/proto/features/model_prototyping.pb.h"
+#include "components/tabs/public/tab_interface.h"
 
 class Profile;
 
@@ -34,25 +40,52 @@
   // Convenience method, may return nullptr.
   static ActorKeyedService* Get(content::BrowserContext* context);
 
-  // Starts tracking a task.
-  void AddTask(std::unique_ptr<ActorTask> task);
+  // Starts tracking an existing task. Returns the new task ID.
+  TaskId AddTask(std::unique_ptr<ActorTask> task);
 
   // In the future we may want to return a more limited or const-version of
   // ActorTasks. The purpose of this method is to get information about tasks,
   // not to modify them.
-  const std::vector<std::unique_ptr<ActorTask>>& GetTasks();
+  const std::map<TaskId, std::unique_ptr<ActorTask>>& GetTasks();
+
+  // Starts a new task using the ActorCoordinator execution engine and fires
+  // `callback` when the task is ready. Implicitly calls AddTask.
+  void StartTask(
+      optimization_guide::proto::BrowserStartTask task,
+      base::OnceCallback<
+          void(optimization_guide::proto::BrowserStartTaskResult)> callback);
+
+  // Stops a task by its ID.
+  void StopTask(TaskId task_id);
+
+  // Returns the task with the given ID. Returns nullptr if the task does not
+  // exist.
+  ActorTask* GetTask(TaskId task_id);
 
   // The associated journal for the associated profile.
   AggregatedJournal& GetJournal() LIFETIME_BOUND { return journal_; }
 
  private:
+  // Start task is currently asynchronous.
+  // TODO(crbug.com/411462297): This is a short term hack. Eventually StartTask
+  // will become synchronous.
+  void FinishStartTask(
+      tabs::TabHandle handle,
+      optimization_guide::proto::BrowserStartTask task,
+      base::OnceCallback<
+          void(optimization_guide::proto::BrowserStartTaskResult)> callback);
+
   // In the future we may want to divide this between active and inactive tasks.
-  std::vector<std::unique_ptr<ActorTask>> tasks_;
+  std::map<TaskId, std::unique_ptr<ActorTask>> tasks_;
+
+  TaskId::Generator next_task_id_;
 
   AggregatedJournal journal_;
 
   // Owns this.
   raw_ptr<Profile> profile_;
+
+  base::WeakPtrFactory<ActorKeyedService> weak_ptr_factory_{this};
 };
 
 }  // namespace actor
diff --git a/chrome/browser/actor/actor_keyed_service_unittest.cc b/chrome/browser/actor/actor_keyed_service_unittest.cc
index a5814184..463faa6 100644
--- a/chrome/browser/actor/actor_keyed_service_unittest.cc
+++ b/chrome/browser/actor/actor_keyed_service_unittest.cc
@@ -42,12 +42,12 @@
   raw_ptr<TestingProfile> profile_;
 };
 
-// Adds a task to ActorKeyedService, and then checks that fetching the
-// ActorKeyedService from a profile shows that task.
+// Adds a task to ActorKeyedService
 TEST_F(ActorKeyedServiceTest, AddTask) {
-  ActorKeyedService::Get(profile())->AddTask(std::make_unique<ActorTask>());
-  ASSERT_EQ(ActorKeyedService::Get(profile())->GetTasks().size(), 1u);
-  EXPECT_EQ(ActorKeyedService::Get(profile())->GetTasks()[0]->GetState(),
+  auto* actor_service = ActorKeyedService::Get(profile());
+  actor_service->AddTask(std::make_unique<ActorTask>());
+  ASSERT_EQ(actor_service->GetTasks().size(), 1u);
+  EXPECT_EQ(actor_service->GetTasks().begin()->second->GetState(),
             ActorTask::State::kCreated);
 }
 
diff --git a/chrome/browser/actor/actor_task.cc b/chrome/browser/actor/actor_task.cc
index 4f94f928..ec6af7b7 100644
--- a/chrome/browser/actor/actor_task.cc
+++ b/chrome/browser/actor/actor_task.cc
@@ -46,6 +46,13 @@
   state_ = state;
 }
 
+void ActorTask::Stop() {
+  if (actor_coordinator_) {
+    actor_coordinator_->StopTask();
+  }
+  SetState(State::kFinished);
+}
+
 bool ActorTask::IsPaused() const {
   return GetState() == State::kPausedByClient;
 }
diff --git a/chrome/browser/actor/actor_task.h b/chrome/browser/actor/actor_task.h
index 01fd8c41..0b4f5d43 100644
--- a/chrome/browser/actor/actor_task.h
+++ b/chrome/browser/actor/actor_task.h
@@ -35,6 +35,9 @@
   State GetState() const;
   void SetState(State state);
 
+  // Sets State to kFinished and cancels any pending actions.
+  void Stop();
+
   bool IsPaused() const;
 
   ActorCoordinator* GetActorCoordinator() const;
diff --git a/chrome/browser/actor/aggregated_journal.cc b/chrome/browser/actor/aggregated_journal.cc
index d18f98a..780b937 100644
--- a/chrome/browser/actor/aggregated_journal.cc
+++ b/chrome/browser/actor/aggregated_journal.cc
@@ -7,6 +7,7 @@
 #include "base/memory/safe_ref.h"
 #include "base/rand_util.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/common/actor/actor_logging.h"
 #include "chrome/common/chrome_render_frame.mojom.h"
 #include "content/public/browser/render_frame_host_receiver_set.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -96,6 +97,7 @@
 void AggregatedJournal::PendingAsyncEntry::EndEntry(std::string_view details) {
   CHECK(!terminated_);
   terminated_ = true;
+  ACTOR_LOG() << "End " << event_name_ << ": " << details;
   journal_->AddEndEvent(pass_key_, task_id_, trace_id_, event_name_, details);
 }
 
@@ -108,6 +110,8 @@
                                            TaskId task_id,
                                            std::string_view event_name,
                                            std::string_view details) {
+  ACTOR_LOG() << "Begin " << event_name << ": " << details;
+
   uint64_t trace_id = next_trace_id_++;
   AddEntry(std::make_unique<Entry>(
       url, mojom::JournalEntry::New(mojom::JournalEntryType::kBegin,
@@ -119,6 +123,18 @@
       task_id, trace_id, event_name));
 }
 
+void AggregatedJournal::Log(const GURL& url,
+                            TaskId task_id,
+                            std::string_view event_name,
+                            std::string_view details) {
+  ACTOR_LOG() << event_name << ": " << details;
+  AddEntry(std::make_unique<Entry>(
+      url.possibly_invalid_spec(),
+      mojom::JournalEntry::New(mojom::JournalEntryType::kInstant,
+                               task_id.GetUnsafeValue(), 0, base::Time::Now(),
+                               std::string(event_name), std::string(details))));
+}
+
 void AggregatedJournal::EnsureJournalBound(content::RenderFrameHost& rfh) {
   auto* web_contents = content::WebContents::FromRenderFrameHost(&rfh);
   CHECK(web_contents);
diff --git a/chrome/browser/actor/aggregated_journal.h b/chrome/browser/actor/aggregated_journal.h
index 0419635..1bd4631 100644
--- a/chrome/browser/actor/aggregated_journal.h
+++ b/chrome/browser/actor/aggregated_journal.h
@@ -19,6 +19,7 @@
 #include "chrome/browser/actor/task_id.h"
 #include "chrome/common/actor.mojom.h"
 #include "content/public/browser/render_frame_host.h"
+#include "url/gurl.h"
 
 namespace actor {
 
@@ -82,6 +83,12 @@
       std::string_view event_name,
       std::string_view details);
 
+  // Log an instant event.
+  void Log(const GURL& url,
+           TaskId task_id,
+           std::string_view event_name,
+           std::string_view details);
+
   void EnsureJournalBound(content::RenderFrameHost& rfh);
   void AppendJournalEntries(content::RenderFrameHost* rfh,
                             std::vector<mojom::JournalEntryPtr> entries);
diff --git a/chrome/browser/actor/tools/tool_controller.cc b/chrome/browser/actor/tools/tool_controller.cc
index f0371f1..ebc1f9b3 100644
--- a/chrome/browser/actor/tools/tool_controller.cc
+++ b/chrome/browser/actor/tools/tool_controller.cc
@@ -20,7 +20,6 @@
 #include "chrome/browser/actor/tools/wait_tool.h"
 #include "chrome/common/actor.mojom.h"
 #include "chrome/common/actor/action_result.h"
-#include "chrome/common/actor/actor_logging.h"
 #include "chrome/common/chrome_features.h"
 #include "components/optimization_guide/proto/features/actions_data.pb.h"
 #include "content/public/browser/weak_document_ptr.h"
@@ -111,8 +110,6 @@
   auto journal_event = journal.CreatePendingAsyncEntry(
       target_frame.GetLastCommittedURL().possibly_invalid_spec(), task_id,
       created_tool->JournalEvent(), created_tool->DebugString());
-
-  ACTOR_LOG() << "Starting Tool Use: " << created_tool->DebugString();
   active_state_.emplace(std::move(created_tool), std::move(result_callback),
                         target_frame.GetWeakDocumentPtr(),
                         std::move(journal_event));
@@ -156,8 +153,6 @@
 void ToolController::CompleteToolRequest(mojom::ActionResultPtr result) {
   CHECK(active_state_);
   active_state_->journal_entry->EndEntry(ToDebugString(*result));
-  ACTOR_LOG() << "Completed Tool Invoke[" << ToDebugString(*result)
-              << "]: " << active_state_->tool->DebugString();
   PostResponseTask(std::move(active_state_->completion_callback),
                    std::move(result));
   observation_delayer_.reset();
diff --git a/chrome/browser/ai/ai_data_keyed_service.cc b/chrome/browser/ai/ai_data_keyed_service.cc
index 6c2fc0d1..03b4514 100644
--- a/chrome/browser/ai/ai_data_keyed_service.cc
+++ b/chrome/browser/ai/ai_data_keyed_service.cc
@@ -75,6 +75,7 @@
 #endif
 
 #if !BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/actor/actor_keyed_service.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_group_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -694,12 +695,11 @@
   return options;
 }
 
-#endif  // BUILDFLAG(ENABLE_GLIC)
-
 void RunLater(base::OnceClosure task) {
   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE,
                                                               std::move(task));
 }
+#endif  // BUILDFLAG(ENABLE_GLIC)
 
 // Feature to add allow listed extensions remotely for data collection.
 BASE_FEATURE(kAllowlistedAiDataExtensions,
@@ -856,140 +856,37 @@
   return base::Contains(*kStableChannelAllowlistedIds, extension_id);
 }
 
-void AiDataKeyedService::StartTask(
-    optimization_guide::proto::BrowserStartTask task,
-    base::OnceCallback<void(optimization_guide::proto::BrowserStartTaskResult)>
-        callback) {
-#if !BUILDFLAG(ENABLE_GLIC)
-  RunLater(base::BindOnce(std::move(callback),
-                          optimization_guide::proto::BrowserStartTaskResult()));
-  return;
-#else
-  if (actor_coordinator_) {
-    VLOG(1) << "Start Task failed: Actor coordinator already exists and only "
-               "one allowed at a time.";
-    optimization_guide::proto::BrowserStartTaskResult result;
-    result.set_status(
-        optimization_guide::proto::BrowserStartTaskResult::OVER_TASK_LIMIT);
-    RunLater(base::BindOnce(std::move(callback), std::move(result)));
-    return;
-  }
-
-  Profile* profile = Profile::FromBrowserContext(browser_context_);
-
-  // Make a new tab and navigate before starting the task.
-  // TODO(crbug.com/411462297): This is a short term hack. This code will be
-  // deleted soon once tab_id is removed.
-  tabs::TabHandle handle(task.tab_id());
-  if (!task.tab_id()) {
-    // Get the most recently active browser for this profile.
-    Browser* browser = chrome::FindBrowserWithProfile(profile);
-    // If no browser exists create one.
-    if (!browser) {
-      browser = Browser::Create(
-          Browser::CreateParams(profile, /*user_gesture=*/false));
-    }
-    // Create a new tab.
-    browser->OpenGURL(GURL(url::kAboutBlankURL),
-                      WindowOpenDisposition::NEW_FOREGROUND_TAB);
-    handle = browser->GetActiveTabInterface()->GetHandle();
-  }
-
-  // This is a short term hack. This code will be deleted soon once tab_id is
-  // removed.
-  tabs::TabInterface* tab = handle.Get();
-  if (!tab) {
-    VLOG(1) << "Start Task failed: Unable to find tab";
-    optimization_guide::proto::BrowserStartTaskResult result;
-    // We can theoretically make an appropriate error code here but this whole
-    // flow should be going away soon.
-    result.set_status(
-        optimization_guide::proto::BrowserStartTaskResult::OVER_TASK_LIMIT);
-    RunLater(base::BindOnce(std::move(callback), std::move(result)));
-    return;
-  }
-
-  tab_ = tab->GetWeakPtr();
-  actor_coordinator_ = std::make_unique<actor::ActorCoordinator>(profile, tab);
-
-  // The GlicKeyedService is normally what sets up the profile for Glic, but
-  // GlicKeyedService is not compabible with system profiles, so we need to
-  // manually register the profile here to make sure that OptimizationGuide is
-  // properly set up.
-  // Note that this function is idempotent, so it is fine to call it multiple
-  // times.
-  actor::ActorCoordinator::RegisterWithProfile(profile);
-
-  optimization_guide::proto::BrowserStartTaskResult result;
-  result.set_task_id(task_id_);
-  result.set_tab_id(handle.raw_value());
-  result.set_status(optimization_guide::proto::BrowserStartTaskResult::SUCCESS);
-  RunLater(base::BindOnce(std::move(callback), std::move(result)));
-#endif  // BUILDFLAG(ENABLE_GLIC)
-}
-
-void AiDataKeyedService::StopTask(int64_t task_id,
-                                  base::OnceCallback<void(bool)> callback) {
-#if !BUILDFLAG(ENABLE_GLIC)
-  RunLater(base::BindOnce(std::move(callback), false));
-  return;
-#else
-  if (task_id != task_id_ || !actor_coordinator_ || !tab_) {
-    VLOG(1)
-        << "Stop Task failed: Task id or tab id does not match current task.";
-    RunLater(base::BindOnce(std::move(callback), false));
-    return;
-  }
-
-  actor_coordinator_.reset();
-  tab_.reset();
-  task_id_ += 1;
-  RunLater(base::BindOnce(std::move(callback), true));
-#endif  // BUILDFLAG(ENABLE_GLIC)
-}
-
+#if BUILDFLAG(ENABLE_GLIC)
 void AiDataKeyedService::ExecuteAction(
     optimization_guide::proto::BrowserAction action,
     base::OnceCallback<void(optimization_guide::proto::BrowserActionResult)>
         callback) {
-#if !BUILDFLAG(ENABLE_GLIC)
-  RunLater(base::BindOnce(std::move(callback),
-                          optimization_guide::proto::BrowserActionResult()));
-  return;
-#else
-  if (task_id_ != action.task_id()) {
-    VLOG(1) << "Execute Action failed: Task id does not match "
-               "current task.";
+  auto* actor_service = actor::ActorKeyedService::Get(browser_context_);
+  auto* task = actor_service->GetTask(actor::TaskId(action.task_id()));
+  if (!task) {
+    VLOG(1) << "Execute Action failed: Task not found.";
     optimization_guide::proto::BrowserActionResult result;
     result.set_action_result(0);
     RunLater(base::BindOnce(std::move(callback), std::move(result)));
     return;
   }
-  actor_coordinator_->Act(std::move(action),
-                          base::BindOnce(&AiDataKeyedService::OnActionFinished,
-                                         weak_factory_.GetWeakPtr(),
-                                         std::move(callback), task_id_));
-#endif  // BUILDFLAG(ENABLE_GLIC)
+  task->GetActorCoordinator()->Act(
+      std::move(action), base::BindOnce(&AiDataKeyedService::OnActionFinished,
+                                        weak_factory_.GetWeakPtr(),
+                                        std::move(callback), action.task_id()));
 }
 
-bool AiDataKeyedService::IsActorCoordinatorActingOnTab(
-    const content::WebContents* tab) const {
-#if BUILDFLAG(ENABLE_GLIC)
-  return actor_coordinator_ && actor_coordinator_->HasTaskForTab(tab);
-#else
-  return false;
-#endif
-}
-
-#if BUILDFLAG(ENABLE_GLIC)
 void AiDataKeyedService::OnActionFinished(
     base::OnceCallback<void(optimization_guide::proto::BrowserActionResult)>
         callback,
     int task_id,
     actor::mojom::ActionResultPtr action_result) {
-  if (!tab_ || task_id_ != task_id) {
-    VLOG(1) << "Execute Action failed: Task id does not match "
-               "current task.";
+  auto* actor_service = actor::ActorKeyedService::Get(browser_context_);
+  auto* task = actor_service->GetTask(actor::TaskId(task_id));
+  CHECK(task);
+  tabs::TabInterface* tab = task->GetActorCoordinator()->GetTabOfCurrentTask();
+  if (!tab) {
+    VLOG(1) << "Execute Action failed: Tab not found.";
     optimization_guide::proto::BrowserActionResult result;
     result.set_action_result(0);
     RunLater(base::BindOnce(std::move(callback), std::move(result)));
@@ -997,23 +894,24 @@
   }
   // TODO(https://crbug.com/398271171): Remove when the actor coordinator
   // handles getting a new observation.
+  int32_t tab_id = tab->GetHandle().raw_value();
   glic::FetchPageContext(
-      tab_.get(), DefaultOptions(), /*include_actionable_data=*/true,
+      tab, DefaultOptions(), /*include_actionable_data=*/true,
       base::BindOnce(&AiDataKeyedService::ConvertToBrowserActionResult,
-                     weak_factory_.GetWeakPtr(), std::move(callback), task_id_,
-                     std::move(action_result)));
+                     weak_factory_.GetWeakPtr(), std::move(callback), task_id,
+                     tab_id, std::move(action_result)));
 }
 
 void AiDataKeyedService::ConvertToBrowserActionResult(
     base::OnceCallback<void(optimization_guide::proto::BrowserActionResult)>
         callback,
     int task_id,
+    int32_t tab_id,
     actor::mojom::ActionResultPtr action_result,
     glic::mojom::GetContextResultPtr context_result) {
   optimization_guide::proto::BrowserActionResult browser_action_result;
-  if (task_id != task_id_ || context_result->is_error_reason()) {
-    VLOG(1) << "Execute Action failed: Tab id or task id does not match "
-               "current task.";
+  if (context_result->is_error_reason()) {
+    VLOG(1) << "Execute Action failed: Error fetching context.";
     browser_action_result.set_action_result(0);
     RunLater(
         base::BindOnce(std::move(callback), std::move(browser_action_result)));
@@ -1040,8 +938,7 @@
         context_result->get_tab_context()->viewport_screenshot->mime_type);
   }
   browser_action_result.set_task_id(task_id);
-  browser_action_result.set_tab_id(tab_ ? tab_->GetHandle().raw_value()
-                                        : tabs::TabHandle::Null().raw_value());
+  browser_action_result.set_tab_id(tab_id);
   browser_action_result.set_action_result(actor::IsOk(*action_result) ? 1 : 0);
   RunLater(
       base::BindOnce(std::move(callback), std::move(browser_action_result)));
diff --git a/chrome/browser/ai/ai_data_keyed_service.h b/chrome/browser/ai/ai_data_keyed_service.h
index 7ff6b92..568bfe6 100644
--- a/chrome/browser/ai/ai_data_keyed_service.h
+++ b/chrome/browser/ai/ai_data_keyed_service.h
@@ -72,25 +72,13 @@
                               AiDataSpecifier specifier,
                               AiDataCallback callback);
 
-  // Starts an actor task.
-  void StartTask(
-      optimization_guide::proto::BrowserStartTask task,
-      base::OnceCallback<
-          void(optimization_guide::proto::BrowserStartTaskResult)> callback);
-
-  // Stops an actor task.
-  void StopTask(int64_t task_id, base::OnceCallback<void(bool)> callback);
-
   // Executes an actor action. The first action in a task must be navigate.
+#if BUILDFLAG(ENABLE_GLIC)
   void ExecuteAction(
       optimization_guide::proto::BrowserAction action,
       base::OnceCallback<void(optimization_guide::proto::BrowserActionResult)>
           callback);
-
-  // Returns true if the associated ActorCoordinator is active on the given
-  // `tab`. This can be used by callers to customize certain behaviour that
-  // might interfere with the ActorCoordinator.
-  bool IsActorCoordinatorActingOnTab(const content::WebContents* tab) const;
+#endif
 
   static const base::Feature& GetAllowlistedAiDataExtensionsFeatureForTesting();
   static const base::Feature&
@@ -98,18 +86,11 @@
 
  private:
 #if BUILDFLAG(ENABLE_GLIC)
-  // Called when the actor coordinator has created a new tab for the task.
-  void TaskCreated(
-      base::OnceCallback<void(optimization_guide::proto::BrowserActionResult)>
-          callback,
-      optimization_guide::proto::BrowserAction action,
-      int task_id,
-      base::WeakPtr<tabs::TabInterface>);
-  // Converts the result of a page context fetch to a BrowserActionResult.
   void ConvertToBrowserActionResult(
       base::OnceCallback<void(optimization_guide::proto::BrowserActionResult)>
           callback,
       int task_id,
+      int32_t tab_id,
       actor::mojom::ActionResultPtr action_result,
       glic::mojom::GetContextResultPtr result);
   // Called when the actor coordinator has finished an action which required
@@ -119,18 +100,6 @@
           callback,
       int task_id,
       actor::mojom::ActionResultPtr action_result);
-  // The actor coordinator which manages task and action routing.
-  std::unique_ptr<actor::ActorCoordinator> actor_coordinator_;
-
-  // Whether the task still needs a navigate action to be executed as the
-  // first action in the task.
-  bool task_needs_navigate_ = false;
-
-  // The tab that the task is running on.
-  base::WeakPtr<tabs::TabInterface> tab_;
-
-  // The task id of the current task.
-  int task_id_ = 1;
 #endif
 
   // A `KeyedService` should never outlive the `BrowserContext`.
diff --git a/chrome/browser/ai/ai_data_keyed_service_browsertest.cc b/chrome/browser/ai/ai_data_keyed_service_browsertest.cc
index 9966123..1a2fb56 100644
--- a/chrome/browser/ai/ai_data_keyed_service_browsertest.cc
+++ b/chrome/browser/ai/ai_data_keyed_service_browsertest.cc
@@ -13,6 +13,7 @@
 #include "base/test/bind.h"
 #include "base/test/test_future.h"
 #include "build/build_config.h"
+#include "chrome/browser/actor/actor_keyed_service.h"
 #include "chrome/browser/ai/ai_data_keyed_service_factory.h"
 #include "chrome/browser/history_embeddings/history_embeddings_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -96,6 +97,10 @@
         browser()->profile());
   }
 
+  actor::ActorKeyedService& actor_service() {
+    return *actor::ActorKeyedService::Get(browser()->profile());
+  }
+
   content::WebContents* web_contents() {
     return browser()->tab_strip_model()->GetActiveWebContents();
   }
@@ -540,17 +545,10 @@
         EXPECT_EQ(task.tab_id(), tab_id);
         run_loop->Quit();
       };
-  ai_data_service().StartTask(std::move(task_request),
-                              base::BindLambdaForTesting(start_task_callback));
+  actor_service().StartTask(std::move(task_request),
+                            base::BindLambdaForTesting(start_task_callback));
   run_loop->Run();
-  run_loop = std::make_unique<base::RunLoop>();
-  auto stop_task_callback = [&run_loop](bool success) {
-    EXPECT_TRUE(success);
-    run_loop->Quit();
-  };
-  ai_data_service().StopTask(
-      id, base::BindLambdaForTesting(std::move(stop_task_callback)));
-  run_loop->Run();
+  actor_service().StopTask(actor::TaskId(id));
 
   id++;
   run_loop = std::make_unique<base::RunLoop>();
@@ -562,9 +560,8 @@
         run_loop->Quit();
       };
   task_request.set_tab_id(tab_id);
-  ai_data_service().StartTask(
-      std::move(task_request),
-      base::BindLambdaForTesting(start_task_callback_2));
+  actor_service().StartTask(std::move(task_request),
+                            base::BindLambdaForTesting(start_task_callback_2));
   run_loop->Run();
 }
 
@@ -582,8 +579,8 @@
         EXPECT_EQ(task.tab_id(), tab_id);
         run_loop->Quit();
       };
-  ai_data_service().StartTask(std::move(task_request),
-                              base::BindLambdaForTesting(start_task_callback));
+  actor_service().StartTask(std::move(task_request),
+                            base::BindLambdaForTesting(start_task_callback));
   run_loop->Run();
 
   run_loop = std::make_unique<base::RunLoop>();
@@ -606,14 +603,7 @@
   run_loop->Run();
   EXPECT_EQ(web_contents()->GetURL(), GURL("https://www.google.com"));
 
-  run_loop = std::make_unique<base::RunLoop>();
-  auto stop_task_callback = [&run_loop](bool success) {
-    EXPECT_TRUE(success);
-    run_loop->Quit();
-  };
-  ai_data_service().StopTask(
-      id, base::BindLambdaForTesting(std::move(stop_task_callback)));
-  run_loop->Run();
+  actor_service().StopTask(actor::TaskId(id));
 
   id++;
   run_loop = std::make_unique<base::RunLoop>();
@@ -625,9 +615,8 @@
         run_loop->Quit();
       };
   task_request.set_tab_id(tab_id);
-  ai_data_service().StartTask(
-      std::move(task_request),
-      base::BindLambdaForTesting(start_task_callback_2));
+  actor_service().StartTask(std::move(task_request),
+                            base::BindLambdaForTesting(start_task_callback_2));
   run_loop->Run();
 }
 
@@ -640,8 +629,8 @@
   int tab_id = browser()->GetActiveTabInterface()->GetHandle().raw_value();
   optimization_guide::proto::BrowserStartTask task_request;
   task_request.set_tab_id(tab_id);
-  ai_data_service().StartTask(std::move(task_request),
-                              start_task_result.GetCallback());
+  actor_service().StartTask(std::move(task_request),
+                            start_task_result.GetCallback());
   auto& task = start_task_result.Get();
   EXPECT_EQ(task.task_id(), id);
   EXPECT_EQ(task.tab_id(), tab_id);
@@ -685,7 +674,6 @@
   EXPECT_EQ(click_response.tab_id(), id);
   frame_nav_observer.Wait();
 }
-
 #endif  // BUILDFLAG(ENABLE_GLIC)
 
 }  // namespace
diff --git a/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java b/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java
index 55b2125e..90ea8ad 100644
--- a/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java
+++ b/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java
@@ -9,6 +9,7 @@
 import static androidx.browser.customtabs.CustomTabsIntent.ACTIVITY_SIDE_SHEET_ROUNDED_CORNERS_POSITION_NONE;
 import static androidx.browser.customtabs.CustomTabsIntent.CLOSE_BUTTON_POSITION_DEFAULT;
 import static androidx.browser.customtabs.CustomTabsIntent.OPEN_IN_BROWSER_STATE_OFF;
+import static androidx.browser.customtabs.CustomTabsIntent.SHARE_STATE_OFF;
 
 import android.app.PendingIntent;
 import android.content.ComponentName;
@@ -769,6 +770,13 @@
     }
 
     /**
+     * @return the developer option specified for Share action button in the custom tab toolbar.
+     */
+    public int getShareButtonState() {
+        return SHARE_STATE_OFF;
+    }
+
+    /**
      * @return the TWA startup timestamp associated with an intent in the uptimeMillis timebase, or
      *     null.
      */
diff --git a/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/CustomButtonParams.java b/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/CustomButtonParams.java
index d98b423..c5a4a288 100644
--- a/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/CustomButtonParams.java
+++ b/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/CustomButtonParams.java
@@ -17,6 +17,7 @@
 import androidx.browser.customtabs.CustomTabsIntent;
 
 import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -79,7 +80,7 @@
     /**
      * @return The {@link PendingIntent} that will be sent when user clicks the customized button.
      */
-    PendingIntent getPendingIntent();
+    @Nullable PendingIntent getPendingIntent();
 
     /**
      * @return The {@link ButtonType} of the customized button.
diff --git a/chrome/browser/apps/app_service/app_icon/app_icon_factory.cc b/chrome/browser/apps/app_service/app_icon/app_icon_factory.cc
index b4cd0a8..2744d8f 100644
--- a/chrome/browser/apps/app_service/app_icon/app_icon_factory.cc
+++ b/chrome/browser/apps/app_service/app_icon/app_icon_factory.cc
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "base/containers/span.h"
-#include "base/debug/stack_trace.h"
 #include "base/debug/task_trace.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
diff --git a/chrome/browser/apps/app_service/app_install/app_install_service_ash.cc b/chrome/browser/apps/app_service/app_install/app_install_service_ash.cc
index 217b0241..69113d9 100644
--- a/chrome/browser/apps/app_service/app_install/app_install_service_ash.cc
+++ b/chrome/browser/apps/app_service/app_install/app_install_service_ash.cc
@@ -8,7 +8,6 @@
 #include <variant>
 
 #include "ash/constants/ash_features.h"
-#include "base/debug/stack_trace.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/functional/callback_helpers.h"
diff --git a/chrome/browser/apps/app_shim/app_shim_manager_mac.cc b/chrome/browser/apps/app_shim/app_shim_manager_mac.cc
index e5f9392f..6136858 100644
--- a/chrome/browser/apps/app_shim/app_shim_manager_mac.cc
+++ b/chrome/browser/apps/app_shim/app_shim_manager_mac.cc
@@ -502,8 +502,7 @@
 
 mojo::Remote<mac_notifications::mojom::MacNotificationProvider>
 AppShimManager::LaunchNotificationProvider(const webapps::AppId& app_id) {
-  CHECK(
-      base::FeatureList::IsEnabled(features::kAppShimNotificationAttribution));
+  CHECK(web_app::UseNotificationAttributionForWebAppShims());
 
   mojo::Remote<mac_notifications::mojom::MacNotificationProvider> remote;
   auto bind_provider = base::BindOnce(
@@ -543,8 +542,7 @@
 void AppShimManager::ShowNotificationPermissionRequest(
     const webapps::AppId& app_id,
     RequestNotificationPermissionCallback callback) {
-  CHECK(
-      base::FeatureList::IsEnabled(features::kAppShimNotificationAttribution));
+  CHECK(web_app::UseNotificationAttributionForWebAppShims());
 
   if (notification_permission_result_for_testing_.has_value()) {
     std::move(callback).Run(*notification_permission_result_for_testing_);
@@ -767,7 +765,7 @@
 
   auto notification_action_handler = bootstrap->TakeNotificationActionHandler();
   std::optional<mojo::ReceiverId> notification_action_receiver_id;
-  if (base::FeatureList::IsEnabled(features::kAppShimNotificationAttribution) &&
+  if (web_app::UseNotificationAttributionForWebAppShims() &&
       notification_action_handler) {
     notification_action_receiver_id =
         notification_action_handler_receivers_.Add(
@@ -790,8 +788,7 @@
       break;
     }
     case chrome::mojom::AppShimLaunchType::kNotificationAction:
-      if (base::FeatureList::IsEnabled(
-              features::kAppShimNotificationAttribution) &&
+      if (web_app::UseNotificationAttributionForWebAppShims() &&
           notification_action_receiver_id.has_value()) {
         // Wait for the notification action to be handled before finishing up
         // the connection process to ensure Chrome and the App Shim stay alive
diff --git a/chrome/browser/apps/app_shim/app_shim_manager_mac_unittest.cc b/chrome/browser/apps/app_shim/app_shim_manager_mac_unittest.cc
index f5b74d1..4743b025 100644
--- a/chrome/browser/apps/app_shim/app_shim_manager_mac_unittest.cc
+++ b/chrome/browser/apps/app_shim/app_shim_manager_mac_unittest.cc
@@ -1234,7 +1234,9 @@
   };
 
   scoped_feature_list_.InitWithFeatures(
-      {features::kAppShimNotificationAttribution}, {});
+      {features::kAppShimNotificationAttribution,
+       features::kUseAdHocSigningForWebAppShims},
+      {});
 
   // Use SetAppCanCreateHost to simulate the case where there isn't already a
   // loaded profile.
@@ -2008,7 +2010,9 @@
 
 TEST_F(AppShimManagerTest, LaunchNotificationProviderWithAppRunning) {
   scoped_feature_list_.InitWithFeatures(
-      {features::kAppShimNotificationAttribution}, {});
+      {features::kAppShimNotificationAttribution,
+       features::kUseAdHocSigningForWebAppShims},
+      {});
 
   // This app is installed for profile A throughout this test.
   AppShimRegistry::Get()->OnAppInstalledForProfile(kTestAppIdA,
@@ -2035,7 +2039,9 @@
 
 TEST_F(AppShimManagerTest, LaunchNotificationProviderWithoutAppRunning) {
   scoped_feature_list_.InitWithFeatures(
-      {features::kAppShimNotificationAttribution}, {});
+      {features::kAppShimNotificationAttribution,
+       features::kUseAdHocSigningForWebAppShims},
+      {});
 
   // This app is installed for profile A throughout this test.
   AppShimRegistry::Get()->OnAppInstalledForProfile(kTestAppIdA,
@@ -2060,7 +2066,9 @@
 
 TEST_F(AppShimManagerTest, LaunchNotificationProviderWithAppNotInstalled) {
   scoped_feature_list_.InitWithFeatures(
-      {features::kAppShimNotificationAttribution}, {});
+      {features::kAppShimNotificationAttribution,
+       features::kUseAdHocSigningForWebAppShims},
+      {});
 
   EXPECT_CALL(*manager_, ProfileForBackgroundShimLaunch(kTestAppIdA))
       .WillOnce(Return(nullptr));
@@ -2088,7 +2096,9 @@
 
 TEST_F(AppShimManagerTest, RequestNotificationPermissionWithAppRunning) {
   scoped_feature_list_.InitWithFeatures(
-      {features::kAppShimNotificationAttribution}, {});
+      {features::kAppShimNotificationAttribution,
+       features::kUseAdHocSigningForWebAppShims},
+      {});
 
   // This app is installed for profile A throughout this test.
   AppShimRegistry::Get()->OnAppInstalledForProfile(kTestAppIdA,
@@ -2120,7 +2130,9 @@
 
 TEST_F(AppShimManagerTest, RequestNotificationPermissionWithoutAppRunning) {
   scoped_feature_list_.InitWithFeatures(
-      {features::kAppShimNotificationAttribution}, {});
+      {features::kAppShimNotificationAttribution,
+       features::kUseAdHocSigningForWebAppShims},
+      {});
 
   // This app is installed for profile A throughout this test.
   AppShimRegistry::Get()->OnAppInstalledForProfile(kTestAppIdA,
@@ -2152,7 +2164,9 @@
 TEST_F(AppShimManagerTest,
        RequestNotificationPermissionWithoutAppRunningAndBrowserClosing) {
   scoped_feature_list_.InitWithFeatures(
-      {features::kAppShimNotificationAttribution}, {});
+      {features::kAppShimNotificationAttribution,
+       features::kUseAdHocSigningForWebAppShims},
+      {});
 
   // This app is installed for profile A throughout this test.
   AppShimRegistry::Get()->OnAppInstalledForProfile(kTestAppIdA,
@@ -2197,7 +2211,9 @@
 TEST_F(AppShimManagerTest,
        AppShimFailToConnectForNotificationPermissionAfterBrowserClosed) {
   scoped_feature_list_.InitWithFeatures(
-      {features::kAppShimNotificationAttribution}, {});
+      {features::kAppShimNotificationAttribution,
+       features::kUseAdHocSigningForWebAppShims},
+      {});
 
   // This app is installed for profile A throughout this test.
   AppShimRegistry::Get()->OnAppInstalledForProfile(kTestAppIdA,
@@ -2241,7 +2257,9 @@
 TEST_F(AppShimManagerTest,
        RequestNotificationPermissionWithAppShimFailingToLaunch) {
   scoped_feature_list_.InitWithFeatures(
-      {features::kAppShimNotificationAttribution}, {});
+      {features::kAppShimNotificationAttribution,
+       features::kUseAdHocSigningForWebAppShims},
+      {});
 
   // This app is installed for profile A throughout this test.
   AppShimRegistry::Get()->OnAppInstalledForProfile(kTestAppIdA,
@@ -2274,7 +2292,9 @@
 
 TEST_F(AppShimManagerTest, RequestNotificationPermissionWithAppNotInstalled) {
   scoped_feature_list_.InitWithFeatures(
-      {features::kAppShimNotificationAttribution}, {});
+      {features::kAppShimNotificationAttribution,
+       features::kUseAdHocSigningForWebAppShims},
+      {});
 
   EXPECT_CALL(*manager_, ProfileForBackgroundShimLaunch(kTestAppIdA))
       .WillOnce(Return(nullptr));
@@ -2291,7 +2311,9 @@
 TEST_F(AppShimManagerTest, CachedNotificationPermissionStatus) {
   using PermissionStatus = mac_notifications::mojom::PermissionStatus;
   scoped_feature_list_.InitWithFeatures(
-      {features::kAppShimNotificationAttribution}, {});
+      {features::kAppShimNotificationAttribution,
+       features::kUseAdHocSigningForWebAppShims},
+      {});
 
   // Create and launch shim for app A in profile A.
   AppShimRegistry::Get()->OnAppInstalledForProfile(kTestAppIdA,
diff --git a/chrome/browser/ash/accessibility/service/autoclick_client_impl.cc b/chrome/browser/ash/accessibility/service/autoclick_client_impl.cc
index 1815176f..b345b0e 100644
--- a/chrome/browser/ash/accessibility/service/autoclick_client_impl.cc
+++ b/chrome/browser/ash/accessibility/service/autoclick_client_impl.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/ash/accessibility/service/autoclick_client_impl.h"
 
 #include "ash/accessibility/accessibility_controller.h"
-#include "base/debug/stack_trace.h"
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "services/accessibility/public/mojom/autoclick.mojom.h"
diff --git a/chrome/browser/ash/login/chrome_restart_request.cc b/chrome/browser/ash/login/chrome_restart_request.cc
index 805bcab..7b7b68c 100644
--- a/chrome/browser/ash/login/chrome_restart_request.cc
+++ b/chrome/browser/ash/login/chrome_restart_request.cc
@@ -55,6 +55,7 @@
 #include "sandbox/policy/switches.h"
 #include "third_party/blink/public/common/switches.h"
 #include "third_party/cros_system_api/switches/chrome_switches.h"
+#include "ui/accessibility/accessibility_features.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/display/display_features.h"
 #include "ui/display/display_switches.h"
@@ -245,6 +246,8 @@
 // current session.
 void DeriveFeatures(base::CommandLine* out_command_line) {
   auto kForwardFeatures = {
+      &::features::kAccessibilityManifestV3EspeakNGTts,
+      &::features::kAccessibilityManifestV3GoogleTts,
       &features::kAutoNightLight,
       &ash::features::kSeamlessRefreshRateSwitching,
       &::features::kPluginVm,
diff --git a/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_installation.cc b/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_installation.cc
index 631dfaa..b314557 100644
--- a/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_installation.cc
+++ b/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_installation.cc
@@ -8,7 +8,6 @@
 #include <vector>
 
 #include "ash/webui/system_apps/public/system_web_app_type.h"
-#include "base/debug/stack_trace.h"
 #include "base/functional/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
diff --git a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsStateProvider.java b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsStateProvider.java
index eed5c0ca..be6219a 100644
--- a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsStateProvider.java
+++ b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsStateProvider.java
@@ -195,4 +195,7 @@
      */
     @ControlsPosition
     int getControlsPosition();
+
+    /** Returns whether the visibility is controlled by the browser. */
+    boolean isVisibilityForced();
 }
diff --git a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
index 5d22fa6..c251c92 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
@@ -236,7 +236,7 @@
     EXPECT_FALSE(HasDataForType(type));
   }
 
-  // Test that storage systems like filesystem and websql, where just an access
+  // Test that storage systems like filesystem, where just an access
   // creates an empty store, are counted and deleted correctly.
   void TestEmptySiteData(const std::string& type, TimeEnum delete_begin) {
     EXPECT_EQ(0, GetSiteDataCount());
@@ -1494,11 +1494,6 @@
 const std::vector<std::string> kStorageTypes{
     "Cookie",    "LocalStorage",  "FileSystem",   "SessionStorage",
     "IndexedDb", "ServiceWorker", "CacheStorage", "MediaLicense",
-// TODO(crbug.com/333756088): WebSQL is disabled everywhere except Android
-// WebView.
-#if BUILDFLAG(IS_ANDROID)
-    "WebSql",
-#endif
 };
 
 // Test that storage doesn't leave any traces on disk.
@@ -1571,11 +1566,6 @@
 const std::vector<std::string> kSessionOnlyStorageTestTypes{
     "Cookie",    "LocalStorage",  "FileSystem",   "SessionStorage",
     "IndexedDb", "ServiceWorker", "CacheStorage", "MediaLicense",
-// TODO(crbug.com/333756088): WebSQL is disabled everywhere except Android
-// WebView.
-#if BUILDFLAG(IS_ANDROID)
-    "WebSql",
-#endif
 };
 
 // Test that storage gets deleted if marked as SessionOnly.
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 2dab73e8..131b7d6 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -4428,9 +4428,10 @@
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
   PrefService* prefs = profile->GetPrefs();
 
-// Fill font preferences. These are not registered on Android
+// Fill font preferences. These are not registered on Android unless we're built
+// with extensions (the chrome.fontSettings API can change these).
 // - http://crbug.com/308033, http://crbug.com/696364.
-#if !BUILDFLAG(IS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS)
   // Enabling the FontFamilyCache needs some KeyedService that might not be
   // available for some irregular profiles, like the System Profile.
   if (!AreKeyedServicesDisabledForProfileByDefault(profile)) {
diff --git a/chrome/browser/chromeos/enterprise/cloud_storage/one_drive_pref_observer.cc b/chrome/browser/chromeos/enterprise/cloud_storage/one_drive_pref_observer.cc
index 33ec9b8..a1df4e5 100644
--- a/chrome/browser/chromeos/enterprise/cloud_storage/one_drive_pref_observer.cc
+++ b/chrome/browser/chromeos/enterprise/cloud_storage/one_drive_pref_observer.cc
@@ -7,7 +7,6 @@
 #include "ash/constants/web_app_id_constants.h"
 #include "base/check_deref.h"
 #include "base/check_is_test.h"
-#include "base/debug/stack_trace.h"
 #include "base/functional/bind.h"
 #include "base/values.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesConfig.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesConfig.java
index 703fa30..52dde3c 100644
--- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesConfig.java
+++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesConfig.java
@@ -218,13 +218,13 @@
          * @return This builder instance for chaining.
          */
         public Builder setTabGroupColor(Context context, @TabGroupColorId int tabGroupColorId) {
+            if (SurfaceColorUpdateUtils.useNewGm3GtsTabGroupColors()) {
+                return setTabGroupColorGm3(context, tabGroupColorId);
+            }
             @ColorInt
             int tabGroupColor =
-                    SurfaceColorUpdateUtils.useNewGm3GtsTabGroupColors()
-                            ? SurfaceColorUpdateUtils.getCardViewBackgroundColor(
-                                    context, false, tabGroupColorId)
-                            : TabGroupColorPickerUtils.getTabGroupColorPickerItemColor(
-                                    context, tabGroupColorId, false);
+                    TabGroupColorPickerUtils.getTabGroupColorPickerItemColor(
+                            context, tabGroupColorId, false);
             setBorderColor(tabGroupColor);
             setBackgroundColor(tabGroupColor);
             @ColorRes
@@ -233,7 +233,6 @@
                             ? R.color.small_shared_image_tiles_text_color_dark
                             : R.color.small_shared_image_tiles_text_color_light;
             setTextColor(ContextCompat.getColor(context, textColorRes));
-
             return this;
         }
 
@@ -245,5 +244,27 @@
         public SharedImageTilesConfig build() {
             return new SharedImageTilesConfig(this);
         }
+
+        /**
+         * Sets the a new tab group color and updates all relevant colors to match.
+         *
+         * @param context The Android context.
+         * @param tabGroupColorId The color associated with the tab group.
+         * @return This builder instance for chaining.
+         */
+        private Builder setTabGroupColorGm3(Context context, @TabGroupColorId int tabGroupColorId) {
+            @ColorInt
+            int tabGroupColor =
+                    SurfaceColorUpdateUtils.getCardViewBackgroundColor(
+                            context, /* isIncognito= */ false, tabGroupColorId);
+            setBorderColor(tabGroupColor);
+            setBackgroundColor(tabGroupColor);
+            @ColorInt
+            int textColor =
+                    SurfaceColorUpdateUtils.getCardViewTextColor(
+                            context, /* isIncognito= */ false, tabGroupColorId);
+            setTextColor(textColor);
+            return this;
+        }
     }
 }
diff --git a/chrome/browser/download/android/BUILD.gn b/chrome/browser/download/android/BUILD.gn
index c1caa683..3b9acf656 100644
--- a/chrome/browser/download/android/BUILD.gn
+++ b/chrome/browser/download/android/BUILD.gn
@@ -48,6 +48,7 @@
     "java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogCoordinator.java",
     "java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogProperties.java",
     "java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogViewBinder.java",
+    "java/src/org/chromium/chrome/browser/download/dialogs/DownloadWarningBypassDialog.java",
     "java/src/org/chromium/chrome/browser/download/dialogs/InsecureDownloadDialog.java",
     "java/src/org/chromium/chrome/browser/download/dialogs/OpenDownloadCustomView.java",
     "java/src/org/chromium/chrome/browser/download/dialogs/OpenDownloadDialog.java",
diff --git a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DangerousDownloadDialog.java b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DangerousDownloadDialog.java
index f249e73..bfbd46c6 100644
--- a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DangerousDownloadDialog.java
+++ b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DangerousDownloadDialog.java
@@ -22,7 +22,9 @@
 
 /**
  * Dialog for confirming that user want to download a dangerous file, using the default model dialog
- * from ModalDialogManager.
+ * from ModalDialogManager. This dialog applies only to dangerous file types, i.e. dangerType is
+ * DownloadDangerType.DANGEROUS_FILE. Downloads with Safe Browsing warnings may trigger a different
+ * dialog (see {@link DownloadWarningBypassDialog}).
  */
 @NullMarked
 public class DangerousDownloadDialog {
diff --git a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadWarningBypassDialog.java b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadWarningBypassDialog.java
new file mode 100644
index 0000000..7647c4b
--- /dev/null
+++ b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadWarningBypassDialog.java
@@ -0,0 +1,152 @@
+// Copyright 2025 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.download.dialogs;
+
+import android.content.Context;
+
+import androidx.annotation.IntDef;
+
+import org.chromium.base.Callback;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.chrome.browser.download.R;
+import org.chromium.ui.UiUtils;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Dialog for confirming that the user wants to download a file with a Safe Browsing download
+ * warning (i.e. bypassing the warning), using the default modal dialog from ModalDialogManager. The
+ * dialog has two buttons: NEGATIVE accepts the bypass; POSITIVE opens the "Learn more" help page.
+ */
+@NullMarked
+public class DownloadWarningBypassDialog {
+    /**
+     * Events related to the download warning bypass dialog. Reported to UMA and used to communicate
+     * the result of the dialog. These values are persisted to logs. Entries should not be
+     * renumbered and numeric values should never be reused.
+     */
+    @IntDef({
+        DownloadWarningBypassDialogEvent.SHOW,
+        DownloadWarningBypassDialogEvent.VALIDATE,
+        DownloadWarningBypassDialogEvent.LEARN_MORE,
+        DownloadWarningBypassDialogEvent.DISMISS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DownloadWarningBypassDialogEvent {
+        // Logged when a dialog is shown.
+        int SHOW = 0;
+        // Following 3 buckets communicate the user's action on the dialog. Exactly 1 of these
+        // should be logged after each dialog shown.
+        int VALIDATE = 1;
+        int LEARN_MORE = 2;
+        int DISMISS = 3;
+
+        int COUNT = 4;
+    }
+
+    /**
+     * Called to show a warning bypass dialog for dangerous download with Safe Browsing warning.
+     *
+     * @param context Context for showing the dialog.
+     * @param modalDialogManager Manager for managing the modal dialog.
+     * @param fileName Name of the download file.
+     * @param callback Callback to run when reporting whether the user selected the bypass option on
+     *     the dialog.
+     */
+    public void show(
+            Context context,
+            ModalDialogManager modalDialogManager,
+            String fileName,
+            Callback<Boolean> callback) {
+        var controller =
+                new ModalDialogProperties.Controller() {
+                    @Override
+                    public void onClick(PropertyModel model, int buttonType) {
+                        handleResult(
+                                buttonType == ModalDialogProperties.ButtonType.NEGATIVE
+                                        ? DownloadWarningBypassDialogEvent.VALIDATE
+                                        : DownloadWarningBypassDialogEvent.LEARN_MORE);
+                        modalDialogManager.dismissDialog(
+                                model,
+                                buttonType == ModalDialogProperties.ButtonType.NEGATIVE
+                                        ? DialogDismissalCause.NEGATIVE_BUTTON_CLICKED
+                                        : DialogDismissalCause.POSITIVE_BUTTON_CLICKED);
+                    }
+
+                    @Override
+                    public void onDismiss(PropertyModel model, int dismissalCause) {
+                        if (dismissalCause != DialogDismissalCause.POSITIVE_BUTTON_CLICKED
+                                && dismissalCause != DialogDismissalCause.NEGATIVE_BUTTON_CLICKED) {
+                            handleResult(DownloadWarningBypassDialogEvent.DISMISS);
+                        }
+                    }
+
+                    private void handleResult(@DownloadWarningBypassDialogEvent int result) {
+                        logDialogEventHistogram(result);
+                        if (callback != null) {
+                            callback.onResult(result == DownloadWarningBypassDialogEvent.VALIDATE);
+                        }
+                        if (result == DownloadWarningBypassDialogEvent.LEARN_MORE) {
+                            openLearnMorePage();
+                        }
+                    }
+                };
+        var resources = context.getResources();
+        PropertyModel propertyModel =
+                new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
+                        .with(ModalDialogProperties.CONTROLLER, controller)
+                        .with(ModalDialogProperties.TITLE, fileName)
+                        .with(ModalDialogProperties.TITLE_MAX_LINES, 1)
+                        .with(
+                                ModalDialogProperties.TITLE_ICON,
+                                UiUtils.getTintedDrawable(
+                                        context,
+                                        R.drawable.dangerous_filled_24dp,
+                                        R.color.error_icon_color_tint_list))
+                        .with(
+                                ModalDialogProperties.MESSAGE_PARAGRAPH_1,
+                                resources.getString(R.string.download_warning_bypass_dialog_text))
+                        // The bypass action is mapped to the negative button, because bypassing the
+                        // security warning is the "discouraged" action.
+                        .with(
+                                ModalDialogProperties.NEGATIVE_BUTTON_TEXT,
+                                resources.getString(
+                                        R.string.download_warning_bypass_dialog_action_download))
+                        .with(
+                                ModalDialogProperties.POSITIVE_BUTTON_TEXT,
+                                resources.getString(
+                                        R.string.download_warning_bypass_dialog_action_learn_more))
+                        .with(
+                                ModalDialogProperties.BUTTON_STYLES,
+                                ModalDialogProperties.ButtonStyles.PRIMARY_OUTLINE_NEGATIVE_OUTLINE)
+                        .with(
+                                ModalDialogProperties.BUTTON_TAP_PROTECTION_PERIOD_MS,
+                                UiUtils.PROMPT_INPUT_PROTECTION_SHORT_DELAY_MS)
+                        .with(ModalDialogProperties.FILTER_TOUCH_FOR_SECURITY, true)
+                        .with(ModalDialogProperties.CANCEL_ON_TOUCH_OUTSIDE, true)
+                        .build();
+        modalDialogManager.showDialog(
+                propertyModel, ModalDialogManager.ModalDialogType.APP, /* showAsNext= */ true);
+        logDialogEventHistogram(DownloadWarningBypassDialogEvent.SHOW);
+    }
+
+    /** Opens the "Learn More" help page URL in a Custom Tab. */
+    void openLearnMorePage() {
+        // TODO(crbug.com/397407934): send an intent to open Learn More URL in a CCT.
+    }
+
+    void logDialogEventHistogram(@DownloadWarningBypassDialogEvent int event) {
+        RecordHistogram.recordEnumeratedHistogram(
+                "Download.Android.WarningBypassDialog.Events",
+                event,
+                DownloadWarningBypassDialogEvent.COUNT);
+    }
+}
diff --git a/chrome/browser/download/download_ui_controller.h b/chrome/browser/download/download_ui_controller.h
index 789ccf980..5b03c15 100644
--- a/chrome/browser/download/download_ui_controller.h
+++ b/chrome/browser/download/download_ui_controller.h
@@ -10,10 +10,10 @@
 
 #include "components/download/content/public/all_download_item_notifier.h"
 
-// This class handles the task of observing a single DownloadManager for
-// notifying the UI when a new download should be displayed in the UI.
-// It invokes the OnNewDownloadReady() method of hte Delegate when the
-// target path is available for a new download.
+// This class handles the task of observing a single DownloadManager to
+// notify the UI when a new download should be displayed. It invokes the
+// OnNewDownloadReady() method of the Delegate when the target path is available
+// for a new download.
 class DownloadUIController
     : public download::AllDownloadItemNotifier::Observer {
  public:
diff --git a/chrome/browser/download/internal/android/BUILD.gn b/chrome/browser/download/internal/android/BUILD.gn
index a47177e..5bf9a4a 100644
--- a/chrome/browser/download/internal/android/BUILD.gn
+++ b/chrome/browser/download/internal/android/BUILD.gn
@@ -111,6 +111,7 @@
     "//base:base_java",
     "//base:content_uri_utils_java",
     "//build/android:build_java",
+    "//chrome/android:chrome_java",
     "//chrome/browser/download/android:file_provider_java",
     "//chrome/browser/download/android:java",
     "//chrome/browser/flags:java",
diff --git a/chrome/browser/download/internal/android/DEPS b/chrome/browser/download/internal/android/DEPS
index e18091b3..25dd167 100644
--- a/chrome/browser/download/internal/android/DEPS
+++ b/chrome/browser/download/internal/android/DEPS
@@ -26,4 +26,3 @@
   "+third_party/junit",
   "+third_party/mockito",
 ]
-
diff --git a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/DownloadActivityV2Test.java b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/DownloadActivityV2Test.java
index 38e99141..f376651 100644
--- a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/DownloadActivityV2Test.java
+++ b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/DownloadActivityV2Test.java
@@ -71,6 +71,7 @@
 import org.chromium.chrome.test.AutomotiveContextWrapperTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.components.browser_ui.modaldialog.AppModalPresenter;
+import org.chromium.components.browser_ui.modaldialog.ModalDialogView;
 import org.chromium.components.browser_ui.util.date.StringUtils;
 import org.chromium.components.download.DownloadDangerType;
 import org.chromium.components.embedder_support.util.UrlConstants;
@@ -153,6 +154,8 @@
 
     @Before
     public void setUp() throws Exception {
+        ModalDialogView.disableButtonTapProtectionForTesting();
+
         UrlFormatterJni.setInstanceForTesting(mUrlFormatterJniMock);
         when(mUrlFormatterJniMock.formatUrlForSecurityDisplay(
                         any(), eq(SchemeDisplay.OMIT_HTTP_AND_HTTPS)))
@@ -426,9 +429,8 @@
                 .check(matches(isDisplayed()))
                 .perform(ViewActions.click());
 
-        // TODO(crbug.com/397407934): Menu options should reflect the actions available for a
-        // dangerous download ("Delete from history" and "Download").
-        onView(withText("Delete")).check(matches(isDisplayed()));
+        onView(withText("Delete from history")).check(matches(isDisplayed()));
+        onView(withText("Download")).check(matches(isDisplayed()));
         onView(withText("Rename")).check(doesNotExist());
         onView(withText("Share")).check(doesNotExist());
 
@@ -484,6 +486,61 @@
 
     @Test
     @MediumTest
+    public void testBypassDangerousWarning() throws Exception {
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    setUpUi(/*showDangerousItems*/ true);
+                });
+
+        String storageHeaderText = "Using 1.10 KB of";
+        onView(withText(containsString(storageHeaderText))).check(matches(isDisplayed()));
+
+        // Add a dangerous item.
+        OfflineItem dangerousItem =
+                StubbedProvider.createOfflineItem(
+                        "offline_guid_5",
+                        JUnitTestGURLs.URL_2,
+                        OfflineItemState.COMPLETE,
+                        1024,
+                        "dangerous",
+                        "/data/fake_path/Downloads/file_5",
+                        System.currentTimeMillis(),
+                        100000,
+                        OfflineItemFilter.OTHER);
+        dangerousItem.dangerType = DownloadDangerType.DANGEROUS_CONTENT;
+        dangerousItem.isDangerous = true;
+        dangerousItem.canRename = false;
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> mStubbedOfflineContentProvider.addItem(dangerousItem));
+        onView(withText("dangerous")).check(matches(isDisplayed()));
+        onView(withText(containsString("Using 1.10 KB of"))).check(matches(isDisplayed()));
+        // Open bypass dialog by clicking on the item.
+        onView(withText(containsString("Dangerous download blocked")))
+                .check(matches(isDisplayed()))
+                .perform(ViewActions.click());
+
+        // Click the bypass button.
+        onView(allOf(withId(R.id.negative_button), withText("Download anyway")))
+                .inRoot(isDialog())
+                .check(matches(isDisplayed()))
+                .perform(ViewActions.click());
+
+        // Wait for the download to be validated.
+        mStubbedOfflineContentProvider.getValidateDangerousDownloadHelper().waitForOnly();
+
+        // The UI has updated that the download is no longer dangerous, and now counts towards the
+        // storage total.
+        CriteriaHelper.pollInstrumentationThread(
+                () -> {
+                    onView(withText(containsString("Dangerous download blocked")))
+                            .check(doesNotExist());
+                });
+        onView(withText(containsString("Using 2.10 KB of"))).check(matches(isDisplayed()));
+    }
+
+    @Test
+    @MediumTest
     public void testShowListItemMenuWithRename() throws Exception {
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
diff --git a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/StubbedOfflineContentProvider.java b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/StubbedOfflineContentProvider.java
index b3df365..8b618bd 100644
--- a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/StubbedOfflineContentProvider.java
+++ b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/StubbedOfflineContentProvider.java
@@ -12,6 +12,7 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.components.download.DownloadDangerType;
 import org.chromium.components.offline_items_collection.ContentId;
 import org.chromium.components.offline_items_collection.OfflineContentProvider;
 import org.chromium.components.offline_items_collection.OfflineItem;
@@ -26,15 +27,21 @@
 public class StubbedOfflineContentProvider implements OfflineContentProvider {
     private final Handler mHandler;
     private final CallbackHelper mDeleteItemCallback;
+    private final CallbackHelper mValidateDangerousDownloadCallback;
     private final ArrayList<OfflineItem> mItems;
     private OfflineContentProvider.Observer mObserver;
 
     public StubbedOfflineContentProvider() {
         mHandler = new Handler(Looper.getMainLooper());
         mDeleteItemCallback = new CallbackHelper();
+        mValidateDangerousDownloadCallback = new CallbackHelper();
         mItems = new ArrayList<>();
     }
 
+    public CallbackHelper getValidateDangerousDownloadHelper() {
+        return mValidateDangerousDownloadCallback;
+    }
+
     public ArrayList<OfflineItem> getItems() {
         return mItems;
     }
@@ -109,7 +116,22 @@
     public void resumeDownload(ContentId id) {}
 
     @Override
-    public void validateDangerousDownload(ContentId id) {}
+    public void validateDangerousDownload(ContentId id) {
+        for (int i = 0; i < mItems.size(); i++) {
+            if (mItems.get(i).id.equals(id)) {
+                OfflineItem validatedItem = mItems.get(i).clone();
+                validatedItem.isDangerous = false;
+                validatedItem.dangerType = DownloadDangerType.USER_VALIDATED;
+                mItems.set(i, validatedItem);
+                mValidateDangerousDownloadCallback.notifyCalled();
+                break;
+            }
+        }
+        mHandler.post(
+                () -> {
+                    notifyObservers(id);
+                });
+    }
 
     @Override
     public void cancelDownload(ContentId id) {}
diff --git a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListCoordinator.java b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListCoordinator.java
index 1d44eed..ae7a5d3 100644
--- a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListCoordinator.java
+++ b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListCoordinator.java
@@ -21,6 +21,7 @@
 import org.chromium.base.supplier.Supplier;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.browser.download.dialogs.DownloadWarningBypassDialog;
 import org.chromium.chrome.browser.download.home.DownloadManagerUiConfig;
 import org.chromium.chrome.browser.download.home.FaviconProvider;
 import org.chromium.chrome.browser.download.home.StableIds;
@@ -92,6 +93,7 @@
     private final EmptyCoordinator mEmptyCoordinator;
     private final DateOrderedListMediator mMediator;
     private final DateOrderedListView mListView;
+    private final ModalDialogManager mModalDialogManager;
     private final RenameDialogManager mRenameDialogManager;
     private ViewGroup mMainView;
     private View mEmptyView;
@@ -140,6 +142,7 @@
                         decoratedModel,
                         dateOrderedListObserver,
                         this::onConfigurationChangedCallback);
+        mModalDialogManager = modalDialogManager;
         mRenameDialogManager = new RenameDialogManager(context, modalDialogManager);
         mMediator =
                 new DateOrderedListMediator(
@@ -148,6 +151,7 @@
                         this::startShareIntent,
                         deleteController,
                         this::startRename,
+                        this::startShowWarningBypassDialog,
                         selectionDelegate,
                         config,
                         dateOrderedListObserver,
@@ -336,4 +340,8 @@
     private void startRename(String name, DateOrderedListMediator.RenameCallback callback) {
         mRenameDialogManager.startRename(name, callback::tryToRename);
     }
+
+    private void startShowWarningBypassDialog(String fileName, Callback<Boolean> callback) {
+        new DownloadWarningBypassDialog().show(mContext, mModalDialogManager, fileName, callback);
+    }
 }
diff --git a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMediator.java b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMediator.java
index 38f1794..8b03191d 100644
--- a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMediator.java
+++ b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMediator.java
@@ -100,6 +100,19 @@
         void rename(String name, RenameCallback result);
     }
 
+    /** Helper interface for handling warning bypass requests by the UI. */
+    @FunctionalInterface
+    public interface WarningBypassDialogController {
+        /**
+         * Will be called whenever a dangerous {@link OfflineItem} is being requested to bypass a
+         * warning by the UI.
+         *
+         * @param fileName The filename of the warned download, to show in the dialog.
+         * @param callback The callback used to communicate whether the user chose to bypass.
+         */
+        void showWarningBypassDialog(String fileName, Callback<Boolean> callback);
+    }
+
     private final Handler mHandler = new Handler();
 
     private final OfflineContentProvider mProvider;
@@ -108,6 +121,7 @@
     private final ListItemModel mModel;
     private final DeleteController mDeleteController;
     private final RenameController mRenameController;
+    private final WarningBypassDialogController mWarningBypassDialogController;
 
     private final OfflineItemSource mSource;
     private final DateOrderedListMutator mListMutator;
@@ -155,20 +169,22 @@
     }
 
     /**
-     * Creates an instance of a DateOrderedListMediator that will push {@code provider} into
-     * {@code model}.
-     * @param provider                 The {@link OfflineContentProvider} to visually represent.
-     * @param faviconProvider          The {@link FaviconProvider} to handle favicon requests.
-     * @param deleteController         A class to manage whether or not items can be deleted.
-     * @param shareController          A class responsible for sharing downloaded item {@link
-     *                                 Intent}s.
-     * @param selectionDelegate        A class responsible for handling list item selection.
-     * @param config                   A {@link DownloadManagerUiConfig} to provide UI config
-     *                                 params.
-     * @param dateOrderedListObserver  An observer of the list and recycler view.
-     * @param model                    The {@link ListItemModel} to push {@code provider} into.
+     * Creates an instance of a DateOrderedListMediator that will push {@code provider} into {@code
+     * model}.
+     *
+     * @param provider The {@link OfflineContentProvider} to visually represent.
+     * @param faviconProvider The {@link FaviconProvider} to handle favicon requests.
+     * @param shareController A class responsible for sharing downloaded item {@link Intent}s.
+     * @param deleteController A class to manage whether or not items can be deleted.
+     * @param renameController A class to manage whether or not items can be renamed.
+     * @param warningBypassDialogController A class to manage whether or not items can trigger a
+     *     warning bypass.
+     * @param selectionDelegate A class responsible for handling list item selection.
+     * @param config A {@link DownloadManagerUiConfig} to provide UI config params.
+     * @param dateOrderedListObserver An observer of the list and recycler view.
+     * @param model The {@link ListItemModel} to push {@code provider} into.
      * @param discardableReferencePool A {@linK DiscardableReferencePool} reference to use for large
-     *                                 objects (e.g. bitmaps) in the UI.
+     *     objects (e.g. bitmaps) in the UI.
      */
     public DateOrderedListMediator(
             OfflineContentProvider provider,
@@ -176,6 +192,7 @@
             ShareController shareController,
             DeleteController deleteController,
             RenameController renameController,
+            WarningBypassDialogController warningBypassDialogController,
             SelectionDelegate<ListItem> selectionDelegate,
             DownloadManagerUiConfig config,
             DateOrderedListObserver dateOrderedListObserver,
@@ -201,6 +218,7 @@
         mModel = model;
         mDeleteController = deleteController;
         mRenameController = renameController;
+        mWarningBypassDialogController = warningBypassDialogController;
         mSelectionDelegate = selectionDelegate;
         mUiConfig = config;
 
@@ -241,6 +259,10 @@
         mModel.getProperties().set(ListProperties.CALLBACK_RENAME, this::onRenameItem);
         mModel.getProperties()
                 .set(
+                        ListProperties.CALLBACK_SHOW_WARNING_BYPASS_DIALOG,
+                        this::onShowWarningBypassDialog);
+        mModel.getProperties()
+                .set(
                         ListProperties.CALLBACK_PAGINATION_CLICK,
                         mListMutationController::loadMorePages);
         mModel.getProperties()
@@ -343,6 +365,10 @@
     }
 
     private void onOpenItem(OfflineItem item) {
+        if (UiUtils.shouldDisplayAsDangerous(item)) {
+            onShowWarningBypassDialog(item);
+            return;
+        }
         OpenParams openParams = new OpenParams(LaunchLocation.DOWNLOAD_HOME);
         openParams.openInIncognito = OtrProfileId.isOffTheRecord(mUiConfig.otrProfileId);
         mProvider.openItem(openParams, assumeNonNull(item.id));
@@ -376,8 +402,20 @@
                 });
     }
 
+    private void onShowWarningBypassDialog(OfflineItem item) {
+        if (!UiUtils.shouldDisplayAsDangerous(item)) return;
+        mWarningBypassDialogController.showWarningBypassDialog(
+                item.title,
+                (didValidate) -> {
+                    if (didValidate) {
+                        mProvider.validateDangerousDownload(assumeNonNull(item.id));
+                    }
+                });
+    }
+
     /**
      * Deletes a given list of items. If the items are not completed yet, they would be cancelled.
+     *
      * @param items The list of items to delete.
      */
     private void deleteItemsInternal(List<OfflineItem> items) {
diff --git a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/ListProperties.java b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/ListProperties.java
index 9c4d8ee..7edfd277 100644
--- a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/ListProperties.java
+++ b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/ListProperties.java
@@ -81,6 +81,13 @@
     WritableObjectPropertyKey<Callback<OfflineItem>> CALLBACK_RENAME =
             new WritableObjectPropertyKey<>();
 
+    /**
+     * The callback for when a UI action should display a {@link DownloadWarningBypassDialog} for a
+     * {@link OfflineItem} that is displayed as dangerous (has a warning from Safe Browsing).
+     */
+    WritableObjectPropertyKey<Callback<OfflineItem>> CALLBACK_SHOW_WARNING_BYPASS_DIALOG =
+            new WritableObjectPropertyKey<>();
+
     /** The provider to retrieve expensive assets for a {@link OfflineItem}. */
     WritableObjectPropertyKey<VisualsProvider> PROVIDER_VISUALS = new WritableObjectPropertyKey<>();
 
@@ -117,6 +124,7 @@
                 CALLBACK_SELECTION,
                 SELECTION_MODE_ACTIVE,
                 CALLBACK_PAGINATION_CLICK,
-                CALLBACK_GROUP_PAGINATION_CLICK
+                CALLBACK_GROUP_PAGINATION_CLICK,
+                CALLBACK_SHOW_WARNING_BYPASS_DIALOG
             };
 }
diff --git a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/ListPropertyViewBinder.java b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/ListPropertyViewBinder.java
index 2516c7c..e2e73ed 100644
--- a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/ListPropertyViewBinder.java
+++ b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/ListPropertyViewBinder.java
@@ -37,6 +37,7 @@
                 || propertyKey == ListProperties.CALLBACK_CANCEL
                 || propertyKey == ListProperties.CALLBACK_SHARE
                 || propertyKey == ListProperties.CALLBACK_REMOVE
+                || propertyKey == ListProperties.CALLBACK_SHOW_WARNING_BYPASS_DIALOG
                 || propertyKey == ListProperties.PROVIDER_VISUALS
                 || propertyKey == ListProperties.CALLBACK_SELECTION
                 || propertyKey == ListProperties.CALLBACK_RENAME
diff --git a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/holder/OfflineItemViewHolder.java b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/holder/OfflineItemViewHolder.java
index 08efa018..67116d1 100644
--- a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/holder/OfflineItemViewHolder.java
+++ b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/holder/OfflineItemViewHolder.java
@@ -51,10 +51,12 @@
     private @Nullable Runnable mShareCallback;
     private @Nullable Runnable mDeleteCallback;
     private @Nullable Runnable mRenameCallback;
+    private @Nullable Runnable mShowWarningBypassDialogCallback;
 
     // flag to hide rename list menu option for offline pages
     private boolean mCanRename;
     private boolean mCanShare;
+    private boolean mCanShowWarningBypassDialog;
 
     /** Creates a new instance of a {@link OfflineItemViewHolder}. */
     public OfflineItemViewHolder(View view) {
@@ -73,6 +75,7 @@
         OfflineItem offlineItem = ((ListItem.OfflineItemListItem) item).item;
         mCanRename = offlineItem.canRename;
         mCanShare = UiUtils.canShare(offlineItem);
+        mCanShowWarningBypassDialog = canShowWarningBypassDialog(offlineItem);
 
         // Push 'interaction' state
         bindOnClick(properties, item, offlineItem);
@@ -120,6 +123,10 @@
                 v -> {
                     if (mSelectionView != null && mSelectionView.isInSelectionMode()) {
                         properties.get(ListProperties.CALLBACK_SELECTION).onResult(item);
+                    } else if (canShowWarningBypassDialog(offlineItem)) {
+                        properties
+                                .get(ListProperties.CALLBACK_SHOW_WARNING_BYPASS_DIALOG)
+                                .onResult(offlineItem);
                     } else {
                         properties.get(ListProperties.CALLBACK_OPEN).onResult(offlineItem);
                     }
@@ -145,6 +152,14 @@
                     () -> properties.get(ListProperties.CALLBACK_RENAME).onResult(offlineItem);
         }
 
+        if (canShowWarningBypassDialog(offlineItem)) {
+            mShowWarningBypassDialogCallback =
+                    () ->
+                            properties
+                                    .get(ListProperties.CALLBACK_SHOW_WARNING_BYPASS_DIALOG)
+                                    .onResult(offlineItem);
+        }
+
         mDeleteCallback =
                 () -> properties.get(ListProperties.CALLBACK_REMOVE).onResult(offlineItem);
 
@@ -170,17 +185,28 @@
 
         if (mCanShare) listItems.add(buildMenuListItem(R.string.share, 0, 0));
         if (mCanRename) listItems.add(buildMenuListItem(R.string.rename, 0, 0));
-
-        listItems.add(buildMenuListItem(R.string.delete, 0, 0));
+        if (mCanShowWarningBypassDialog) {
+            listItems.add(
+                    buildMenuListItem(R.string.download_warning_heed_menu_action_delete, 0, 0));
+            listItems.add(
+                    buildMenuListItem(R.string.download_warning_bypass_menu_action_download, 0, 0));
+        } else {
+            listItems.add(buildMenuListItem(R.string.delete, 0, 0));
+        }
         ListMenu.Delegate delegate =
                 (model) -> {
                     int textId = model.get(ListMenuItemProperties.TITLE_ID);
                     if (textId == R.string.share) {
                         if (mShareCallback != null) mShareCallback.run();
-                    } else if (textId == R.string.delete) {
+                    } else if (textId == R.string.delete
+                            || textId == R.string.download_warning_heed_menu_action_delete) {
                         if (mDeleteCallback != null) mDeleteCallback.run();
                     } else if (textId == R.string.rename) {
                         if (mRenameCallback != null) mRenameCallback.run();
+                    } else if (textId == R.string.download_warning_bypass_menu_action_download) {
+                        if (mShowWarningBypassDialogCallback != null) {
+                            mShowWarningBypassDialogCallback.run();
+                        }
                     }
                 };
         return BrowserUiListMenuUtils.getBasicListMenu(mMore.getContext(), listItems, delegate);
@@ -208,6 +234,10 @@
                         != properties.get(ListProperties.SELECTION_MODE_ACTIVE);
     }
 
+    private boolean canShowWarningBypassDialog(OfflineItem item) {
+        return UiUtils.shouldDisplayAsDangerous(item);
+    }
+
     /**
      * A class that sets the correct image matrix on the given {@link ImageView} depending on the
      * size of the bitmap.
diff --git a/chrome/browser/enterprise/client_certificates/client_certificates_browsertest.cc b/chrome/browser/enterprise/client_certificates/client_certificates_browsertest.cc
index 84792ede..61eb92f 100644
--- a/chrome/browser/enterprise/client_certificates/client_certificates_browsertest.cc
+++ b/chrome/browser/enterprise/client_certificates/client_certificates_browsertest.cc
@@ -144,8 +144,6 @@
                                              "CN", GetIssuerCommonName()))),
         &policy_value_json));
 
-    LOG(ERROR) << "Policy value: " << policy_value_json.c_str();
-
     base::flat_map<std::string, std::optional<base::Value>> policy_values;
     policy_values.insert(
         {policy::key::kAutoSelectCertificateForUrls,
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_features.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_features.cc
index 452caf5..dfa752a 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_features.cc
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_features.cc
@@ -26,13 +26,13 @@
                    /*default_value=*/5);
 
 // Controls the new upload/download limit for content analysis.
-BASE_FEATURE(kEnableNewUploadDownloadLimit,
-             "EnableNewUploadDownloadLimit",
+BASE_FEATURE(kEnableNewUploadSizeLimit,
+             "EnableNewUploadSizeLimit",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
 BASE_FEATURE_PARAM(size_t,
                    kMaxContentAnalysisFileSizeMB,
-                   &kEnableNewUploadDownloadLimit,
+                   &kEnableNewUploadSizeLimit,
                    "max_file_size_mb",
                    /*default_value=*/50);
 
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_features.h b/chrome/browser/enterprise/connectors/analysis/content_analysis_features.h
index f1c6eb2..7bb8e1ac 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_features.h
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_features.h
@@ -20,8 +20,8 @@
 // Controls the number of content analysis requests concurrently uploaded.
 BASE_DECLARE_FEATURE_PARAM(size_t, kParallelContentAnalysisRequestCount);
 
-// Controls the new upload/download limit for content analysis.
-BASE_DECLARE_FEATURE(kEnableNewUploadDownloadLimit);
+// Controls the new upload/download and print limit for content analysis.
+BASE_DECLARE_FEATURE(kEnableNewUploadSizeLimit);
 
 // Controls the maximum file size for content analysis in MB.
 BASE_DECLARE_FEATURE_PARAM(size_t, kMaxContentAnalysisFileSizeMB);
diff --git a/chrome/browser/enterprise/connectors/analysis/page_print_request_handler.cc b/chrome/browser/enterprise/connectors/analysis/page_print_request_handler.cc
index a554a8a5..0b3e498 100644
--- a/chrome/browser/enterprise/connectors/analysis/page_print_request_handler.cc
+++ b/chrome/browser/enterprise/connectors/analysis/page_print_request_handler.cc
@@ -7,6 +7,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/no_destructor.h"
+#include "chrome/browser/enterprise/connectors/analysis/content_analysis_features.h"
 #include "chrome/browser/enterprise/connectors/analysis/page_print_analysis_request.h"
 #include "chrome/browser/enterprise/connectors/analysis/request_handler_base.h"
 #include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h"
@@ -23,9 +24,17 @@
 
 bool ShouldNotUploadLargePage(const AnalysisSettings& settings,
                               size_t page_size) {
+  size_t max_page_size_bytes =
+      safe_browsing::BinaryUploadService::kMaxUploadSizeBytes;
+  if (base::FeatureList::IsEnabled(
+          enterprise_connectors::kEnableNewUploadSizeLimit)) {
+    max_page_size_bytes =
+        1024 * 1024 *
+        enterprise_connectors::kMaxContentAnalysisFileSizeMB.Get();
+  }
+
   return settings.cloud_or_local_settings.is_cloud_analysis() &&
-         page_size > safe_browsing::BinaryUploadService::kMaxUploadSizeBytes &&
-         settings.block_large_files;
+         page_size > max_page_size_bytes && settings.block_large_files;
 }
 
 PagePrintRequestHandler::TestFactory* TestFactoryStorage() {
diff --git a/chrome/browser/enterprise/connectors/analysis/page_print_request_handler_unittest.cc b/chrome/browser/enterprise/connectors/analysis/page_print_request_handler_unittest.cc
index 099aa192..4309997 100644
--- a/chrome/browser/enterprise/connectors/analysis/page_print_request_handler_unittest.cc
+++ b/chrome/browser/enterprise/connectors/analysis/page_print_request_handler_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/run_loop.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
+#include "chrome/browser/enterprise/connectors/analysis/content_analysis_features.h"
 #include "chrome/browser/enterprise/connectors/analysis/content_analysis_info.h"
 #include "chrome/browser/enterprise/connectors/test/deep_scanning_test_utils.h"
 #include "chrome/browser/safe_browsing/cloud_content_scanning/cloud_binary_upload_service.h"
@@ -227,4 +228,89 @@
   handler->ReportWarningBypass(kJustification);
 }
 
+TEST_F(PagePrintRequestHandlerTest, TestNewLimit) {
+  TestContentAnalysisInfo info(cloud_settings());
+
+  scoped_feature_list_.Reset();
+  scoped_feature_list_.InitAndEnableFeatureWithParameters(
+      enterprise_connectors::kEnableNewUploadSizeLimit,
+      {{"max_file_size_mb", "100"}});
+
+  auto page = CreatePageRegion(kMaxSize);
+  size_t page_size_bytes = page.mapping.size();
+  auto handler = PagePrintRequestHandler::Create(
+      &info, &binary_upload_service_, profile_.get(), GURL(kUrl),
+      "printer_name", "page_content_type", std::move(page.region),
+      base::BindOnce([](RequestHandlerResult result) {
+        EXPECT_EQ(result.final_result, FinalContentAnalysisResult::FAILURE);
+        EXPECT_EQ(result.complies, false);
+        EXPECT_EQ(result.custom_rule_message.message_segments_size(), 1);
+        EXPECT_EQ(result.custom_rule_message.message_segments(0).text(),
+                  kMessage);
+        EXPECT_EQ(result.tag, "dlp");
+      }));
+
+  base::RunLoop run_loop;
+  auto validator = helper_->CreateValidator();
+  validator.SetDoneClosure(run_loop.QuitClosure());
+  validator.ExpectSensitiveDataEvent(
+      /*url*/
+      kUrl,
+      /*tab_url*/ kTabUrl,
+      /*source*/ "",
+      /*destination*/ "printer_name",
+      /*filename*/ "tab_title",
+      /*sha*/ "",
+      /*trigger*/ "PAGE_PRINT",
+      /*dlp_verdict*/
+      CreateResult(ContentAnalysisResponse::Result::TriggeredRule::BLOCK),
+      /*mimetype*/
+      []() {
+        static std::set<std::string> set = {""};
+        return &set;
+      }(),
+      /*size*/ std::nullopt,
+      /*result*/ EventResultToString(EventResult::BLOCKED),
+      /*username*/ "test-user@chromium.org",
+      /*profile_identifier*/ profile_->GetPath().AsUTF8Unsafe(),
+      /*scan_id*/ "",
+      /*content_transfer_method*/ std::nullopt,
+      /*user_justification*/ std::nullopt);
+
+  EXPECT_TRUE(handler->UploadData());
+  run_loop.Run();
+
+  // Verify that the UMA metric was recorded.
+  histogram_tester_.ExpectUniqueSample(
+      "Enterprise.FileAnalysisRequest.PrintedPageSize", page_size_bytes / 1024,
+      1);
+  histogram_tester_.ExpectTotalCount(
+      "Enterprise.FileAnalysisRequest.PrintedPageSize", 1);
+
+  validator.ExpectSensitiveDataEvent(
+      /*url*/
+      kUrl,
+      /*tab_url*/ kTabUrl,
+      /*source*/ "",
+      /*destination*/ "printer_name",
+      /*filename*/ "tab_title",
+      /*sha*/ "",
+      /*trigger*/ "PAGE_PRINT",
+      /*dlp_verdict*/
+      CreateResult(ContentAnalysisResponse::Result::TriggeredRule::BLOCK),
+      /*mimetype*/
+      []() {
+        static std::set<std::string> set = {""};
+        return &set;
+      }(),
+      /*size*/ std::nullopt,
+      /*result*/ EventResultToString(EventResult::BYPASSED),
+      /*username*/ "test-user@chromium.org",
+      /*profile_identifier*/ profile_->GetPath().AsUTF8Unsafe(),
+      /*scan_id*/ "",
+      /*content_transfer_method*/ std::nullopt,
+      /*user_justification*/ kJustification);
+  handler->ReportWarningBypass(kJustification);
+}
+
 }  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/test/ash/management_context_mixin_ash.cc b/chrome/browser/enterprise/test/ash/management_context_mixin_ash.cc
index 9e9ab82..19c5b1e 100644
--- a/chrome/browser/enterprise/test/ash/management_context_mixin_ash.cc
+++ b/chrome/browser/enterprise/test/ash/management_context_mixin_ash.cc
@@ -27,7 +27,19 @@
           host,
           management_context.is_cloud_machine_managed
               ? ash::DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED
-              : ash::DeviceStateMixin::State::OOBE_COMPLETED_CONSUMER_OWNED) {}
+              : ash::DeviceStateMixin::State::OOBE_COMPLETED_CONSUMER_OWNED) {
+  if (management_context_.is_cloud_machine_managed) {
+    management_context_.is_cloud_machine_managed = true;
+    policy::SetDMTokenForTesting(
+        policy::DMToken::CreateValidToken(kDeviceDmToken));
+
+    auto device_policy_update = RequestDevicePolicyUpdate();
+    device_policy_update->policy_data()->add_device_affiliation_ids(
+        kFakeCustomerId);
+    device_policy_update->policy_data()->set_obfuscated_customer_id(
+        kFakeCustomerId);
+  }
+}
 
 ManagementContextMixinAsh::~ManagementContextMixinAsh() = default;
 
@@ -53,16 +65,4 @@
   }
 }
 
-void ManagementContextMixinAsh::ManageCloudMachine() {
-  ManagementContextMixin::ManageCloudMachine();
-  policy::SetDMTokenForTesting(
-      policy::DMToken::CreateValidToken(kDeviceDmToken));
-
-  auto device_policy_update = RequestDevicePolicyUpdate();
-  device_policy_update->policy_data()->add_device_affiliation_ids(
-      kFakeCustomerId);
-  device_policy_update->policy_data()->set_obfuscated_customer_id(
-      kFakeCustomerId);
-}
-
 }  // namespace enterprise::test
diff --git a/chrome/browser/enterprise/test/ash/management_context_mixin_ash.h b/chrome/browser/enterprise/test/ash/management_context_mixin_ash.h
index 323cea6..fa3de9f 100644
--- a/chrome/browser/enterprise/test/ash/management_context_mixin_ash.h
+++ b/chrome/browser/enterprise/test/ash/management_context_mixin_ash.h
@@ -34,9 +34,6 @@
   // InProcessBrowserTestMixin:
   void SetUpOnMainThread() override;
 
-  // ManagementContextMixin:
-  void ManageCloudMachine() override;
-
  private:
   ash::DeviceStateMixin device_state_mixin_;
 };
diff --git a/chrome/browser/enterprise/test/browser/management_context_mixin_browser.cc b/chrome/browser/enterprise/test/browser/management_context_mixin_browser.cc
index 07d9017..bcdd906a 100644
--- a/chrome/browser/enterprise/test/browser/management_context_mixin_browser.cc
+++ b/chrome/browser/enterprise/test/browser/management_context_mixin_browser.cc
@@ -27,6 +27,12 @@
     : ManagementContextMixin(host, test_base, std::move(management_context)) {
   // Fake the OS' device ID.
   browser_dm_token_storage_.SetClientId(kBrowserClientId);
+
+  if (management_context_.is_cloud_machine_managed) {
+    management_context_.is_cloud_machine_managed = true;
+    browser_dm_token_storage_.SetEnrollmentToken(kEnrollmentToken);
+    browser_dm_token_storage_.SetDMToken(kBrowserDmToken);
+  }
 }
 
 ManagementContextMixinBrowser::~ManagementContextMixinBrowser() = default;
@@ -82,12 +88,6 @@
 }
 #endif
 
-void ManagementContextMixinBrowser::ManageCloudMachine() {
-  ManagementContextMixin::ManageCloudMachine();
-  browser_dm_token_storage_.SetEnrollmentToken(kEnrollmentToken);
-  browser_dm_token_storage_.SetDMToken(kBrowserDmToken);
-}
-
 void ManagementContextMixinBrowser::SetCloudMachinePolicies(
     base::flat_map<std::string, std::optional<base::Value>> policy_entries) {
   CHECK(management_context_.is_cloud_machine_managed);
diff --git a/chrome/browser/enterprise/test/browser/management_context_mixin_browser.h b/chrome/browser/enterprise/test/browser/management_context_mixin_browser.h
index 8ca8df7c..6274b82d 100644
--- a/chrome/browser/enterprise/test/browser/management_context_mixin_browser.h
+++ b/chrome/browser/enterprise/test/browser/management_context_mixin_browser.h
@@ -42,7 +42,6 @@
 #endif
 
   // ManagementContextMixin:
-  void ManageCloudMachine() override;
   void SetCloudMachinePolicies(
       base::flat_map<std::string, std::optional<base::Value>> policy_entries)
       override;
diff --git a/chrome/browser/enterprise/test/management_context_mixin.cc b/chrome/browser/enterprise/test/management_context_mixin.cc
index 3282170..8abdc4b 100644
--- a/chrome/browser/enterprise/test/management_context_mixin.cc
+++ b/chrome/browser/enterprise/test/management_context_mixin.cc
@@ -43,16 +43,7 @@
     ManagementContext management_context)
     : InProcessBrowserTestMixin(host),
       test_base_(test_base),
-      management_context_(std::move(management_context)) {}
-
-ManagementContextMixin::~ManagementContextMixin() = default;
-
-void ManagementContextMixin::SetUpInProcessBrowserTestFixture() {
-  InProcessBrowserTestMixin::SetUpInProcessBrowserTestFixture();
-  if (management_context_.is_cloud_machine_managed) {
-    ManageCloudMachine();
-  }
-
+      management_context_(std::move(management_context)) {
   user_policy_provider_.SetDefaultReturns(
       /*is_initialization_complete_return=*/true,
       /*is_first_policy_load_complete_return=*/true);
@@ -60,6 +51,8 @@
       &user_policy_provider_);
 }
 
+ManagementContextMixin::~ManagementContextMixin() = default;
+
 void ManagementContextMixin::ManageCloudUser() {
   // User is now managed. Derived classes are expected to have more logic.
   management_context_.is_cloud_user_managed = true;
@@ -79,11 +72,6 @@
   MergeNewChromePolicies(policy_map);
 }
 
-void ManagementContextMixin::ManageCloudMachine() {
-  // Machine is now managed. Derived classes are expected to have more logic.
-  management_context_.is_cloud_machine_managed = true;
-}
-
 std::unique_ptr<enterprise_management::PolicyData>
 ManagementContextMixin::GetBaseUserPolicyData() const {
   const auto* user_customer_id =
diff --git a/chrome/browser/enterprise/test/management_context_mixin.h b/chrome/browser/enterprise/test/management_context_mixin.h
index f6ecb90..66b4b6c 100644
--- a/chrome/browser/enterprise/test/management_context_mixin.h
+++ b/chrome/browser/enterprise/test/management_context_mixin.h
@@ -80,11 +80,6 @@
                          InProcessBrowserTest* test_base,
                          ManagementContext management_context);
 
-  // InProcessBrowserTestMixin:
-  void SetUpInProcessBrowserTestFixture() override;
-
-  virtual void ManageCloudMachine();
-
   // Returns a PolicyData object with some base value which can be used by
   // platform-specific mixin definitions to manage the current user.
   std::unique_ptr<enterprise_management::PolicyData> GetBaseUserPolicyData()
diff --git a/chrome/browser/extensions/api/BUILD.gn b/chrome/browser/extensions/api/BUILD.gn
index 5fbe9340..9f52519 100644
--- a/chrome/browser/extensions/api/BUILD.gn
+++ b/chrome/browser/extensions/api/BUILD.gn
@@ -30,6 +30,7 @@
     "//chrome/browser/extensions/api/browsing_data",
     "//chrome/browser/extensions/api/cookies",
     "//chrome/browser/extensions/api/extension_action",
+    "//chrome/browser/extensions/api/font_settings",
     "//chrome/browser/extensions/api/module",
     "//chrome/browser/extensions/api/notifications",
     "//chrome/browser/extensions/api/runtime",
@@ -51,7 +52,6 @@
       "//chrome/browser/extensions/api/enterprise_hardware_platform",
       "//chrome/browser/extensions/api/experimental_actor",
       "//chrome/browser/extensions/api/experimental_ai_data",
-      "//chrome/browser/extensions/api/font_settings",
       "//chrome/browser/extensions/api/idltest",
       "//chrome/browser/extensions/api/networking_private",
       "//chrome/browser/extensions/api/omnibox",
diff --git a/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc b/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc
index 139f8340..a7f1fde5 100644
--- a/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc
+++ b/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc
@@ -6,6 +6,7 @@
 #include "chrome/browser/extensions/api/bookmarks/bookmarks_api_watcher.h"
 #include "chrome/browser/extensions/api/cookies/cookies_api.h"
 #include "chrome/browser/extensions/api/developer_private/developer_private_api.h"
+#include "chrome/browser/extensions/api/font_settings/font_settings_api.h"
 #include "chrome/browser/extensions/api/history/history_api.h"
 #include "chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.h"
 #include "chrome/browser/extensions/commands/command_service.h"
@@ -23,7 +24,6 @@
 #include "chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h"
 #include "chrome/browser/extensions/api/braille_display_private/braille_display_private_api.h"
 #include "chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_event_router.h"
-#include "chrome/browser/extensions/api/font_settings/font_settings_api.h"
 #include "chrome/browser/extensions/api/identity/identity_api.h"
 #include "chrome/browser/extensions/api/image_writer_private/operation_manager.h"
 #include "chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate_factory.h"
@@ -84,6 +84,7 @@
   extensions::CookiesAPI::GetFactoryInstance();
   extensions::DeveloperPrivateAPI::GetFactoryInstance();
   extensions::ExtensionNotificationDisplayHelperFactory::GetInstance();
+  extensions::FontSettingsAPI::GetFactoryInstance();
   extensions::HistoryAPI::GetFactoryInstance();
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
@@ -96,7 +97,6 @@
   extensions::DocumentScanAPIHandler::GetFactoryInstance();
 #endif
   extensions::EnterpriseReportingPrivateEventRouterFactory::GetInstance();
-  extensions::FontSettingsAPI::GetFactoryInstance();
   extensions::IdentityAPI::GetFactoryInstance();
   extensions::IncognitoConnectability::EnsureFactoryBuilt();
 #if BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc b/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc
index b0668718..b613759 100644
--- a/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc
+++ b/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc
@@ -77,8 +77,6 @@
     return content::BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS;
   if (strcmp(key, extension_browsing_data_api_constants::kCacheStorageKey) == 0)
     return content::BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE;
-  if (strcmp(key, extension_browsing_data_api_constants::kWebSQLKey) == 0)
-    return content::BrowsingDataRemover::DATA_TYPE_WEB_SQL;
 
   return 0ULL;
 }
@@ -162,9 +160,6 @@
              extension_browsing_data_api_constants::kLocalStorageKey,
              delete_site_data);
   SetDetails(&selected, &permitted,
-             extension_browsing_data_api_constants::kWebSQLKey,
-             delete_site_data);
-  SetDetails(&selected, &permitted,
              extension_browsing_data_api_constants::kServiceWorkersKey,
              delete_site_data);
   SetDetails(&selected, &permitted,
@@ -530,6 +525,7 @@
 }
 
 bool BrowsingDataRemoveWebSQLFunction::GetRemovalMask(uint64_t* removal_mask) {
-  *removal_mask = content::BrowsingDataRemover::DATA_TYPE_WEB_SQL;
+  // TODO(http://crbug.com/420857719): Deprecate and remove this extension api.
+  *removal_mask = 0;
   return true;
 }
diff --git a/chrome/browser/extensions/api/browsing_data/browsing_data_api.h b/chrome/browser/extensions/api/browsing_data/browsing_data_api.h
index 706eaaf..6d38a51 100644
--- a/chrome/browser/extensions/api/browsing_data/browsing_data_api.h
+++ b/chrome/browser/extensions/api/browsing_data/browsing_data_api.h
@@ -20,6 +20,9 @@
 #include "content/public/browser/browsing_data_filter_builder.h"
 #include "content/public/browser/browsing_data_remover.h"
 #include "extensions/browser/extension_function.h"
+#include "extensions/buildflags/buildflags.h"
+
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
 
 class PrefService;
 
@@ -43,7 +46,6 @@
 inline constexpr char kPluginDataKeyDeprecated[] = "pluginData";
 inline constexpr char kServiceWorkersKey[] = "serviceWorkers";
 inline constexpr char kCacheStorageKey[] = "cacheStorage";
-inline constexpr char kWebSQLKey[] = "webSQL";
 
 // Option keys.
 inline constexpr char kExtensionsKey[] = "extension";
diff --git a/chrome/browser/extensions/api/browsing_data/browsing_data_unittest.cc b/chrome/browser/extensions/api/browsing_data/browsing_data_unittest.cc
index 686c2c1..4d03d2f 100644
--- a/chrome/browser/extensions/api/browsing_data/browsing_data_unittest.cc
+++ b/chrome/browser/extensions/api/browsing_data/browsing_data_unittest.cc
@@ -15,8 +15,6 @@
 #include "chrome/browser/extensions/api/browsing_data/browsing_data_api.h"
 #include "chrome/browser/extensions/extension_service_test_base.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/test/base/test_browser_window.h"
 #include "components/browsing_data/content/browsing_data_helper.h"
 #include "components/browsing_data/core/browsing_data_utils.h"
 #include "components/browsing_data/core/pref_names.h"
@@ -26,9 +24,12 @@
 #include "content/public/browser/browsing_data_remover.h"
 #include "content/public/test/mock_browsing_data_remover_delegate.h"
 #include "extensions/browser/api_test_utils.h"
+#include "extensions/buildflags/buildflags.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 using extensions::api_test_utils::RunFunctionAndReturnError;
 using extensions::api_test_utils::RunFunctionAndReturnSingleResult;
 
@@ -43,6 +44,7 @@
 };
 
 // TODO(http://crbug.com/1266606): appcache is a noop and should be removed.
+// TODO(http://crbug.com/420857719): webSQL is a noop and should be removed.
 const char kRemoveEverythingArguments[] =
     R"([{"since": 1000}, {
     "appcache": true, "cache": true, "cookies": true, "downloads": true,
@@ -58,21 +60,10 @@
     ExtensionServiceTestBase::SetUp();
     InitializeEmptyExtensionService();
 
-    browser_window_ = std::make_unique<TestBrowserWindow>();
-    Browser::CreateParams params(profile(), true);
-    params.type = Browser::TYPE_NORMAL;
-    params.window = browser_window_.get();
-    browser_ = std::unique_ptr<Browser>(Browser::Create(params));
-
     remover_ = profile()->GetBrowsingDataRemover();
     remover_->SetEmbedderDelegate(&delegate_);
   }
 
-  void TearDown() override {
-    browser_.reset();
-    browser_window_.reset();
-    ExtensionServiceTestBase::TearDown();
-  }
   const base::Time& GetBeginTime() {
     return remover_->GetLastUsedBeginTimeForTesting();
   }
@@ -100,7 +91,7 @@
     SCOPED_TRACE(data_types);
     EXPECT_FALSE(RunFunctionAndReturnSingleResult(
         function.get(), std::string("[{\"since\": 1},") + data_types + "]",
-        browser()->profile()));
+        profile()));
     EXPECT_EQ(expected_mask, GetRemovalMask());
     EXPECT_EQ(UNPROTECTED_WEB, GetOriginTypeMask());
   }
@@ -120,7 +111,7 @@
     EXPECT_FALSE(RunFunctionAndReturnSingleResult(
         function.get(),
         "[{\"originTypes\": " + protectedStr + "}, {\"cookies\": true}]",
-        browser()->profile()));
+        profile()));
     EXPECT_EQ(expected_mask, GetOriginTypeMask());
   }
 
@@ -129,13 +120,13 @@
     scoped_refptr<ShortcutFunction> function = new ShortcutFunction();
     SCOPED_TRACE(ShortcutFunction::static_function_name());
     EXPECT_FALSE(RunFunctionAndReturnSingleResult(
-        function.get(), std::string("[{\"since\": 1}]"), browser()->profile()));
+        function.get(), std::string("[{\"since\": 1}]"), profile()));
     EXPECT_EQ(expected_mask, GetRemovalMask());
     EXPECT_EQ(UNPROTECTED_WEB, GetOriginTypeMask());
   }
 
   void SetSinceAndVerify(browsing_data::TimePeriod since_pref) {
-    PrefService* prefs = browser()->profile()->GetPrefs();
+    PrefService* prefs = profile()->GetPrefs();
     browsing_data::ClearBrowsingDataTab tab =
         static_cast<browsing_data::ClearBrowsingDataTab>(
             prefs->GetInteger(browsing_data::prefs::kLastClearBrowsingDataTab));
@@ -146,7 +137,7 @@
         new BrowsingDataSettingsFunction();
     SCOPED_TRACE("settings");
     std::optional<base::Value> result = RunFunctionAndReturnSingleResult(
-        function.get(), std::string("[]"), browser()->profile());
+        function.get(), std::string("[]"), profile());
     EXPECT_TRUE(result->is_dict());
     ASSERT_TRUE(result->GetDict().FindDoubleByDottedPath("options.since"));
     double since = *result->GetDict().FindDoubleByDottedPath("options.since");
@@ -167,7 +158,7 @@
   void SetPrefsAndVerifySettings(uint64_t data_type_flags,
                                  uint64_t expected_origin_type_mask,
                                  uint64_t expected_removal_mask) {
-    PrefService* prefs = browser()->profile()->GetPrefs();
+    PrefService* prefs = profile()->GetPrefs();
     prefs->SetInteger(
         browsing_data::prefs::kLastClearBrowsingDataTab,
         static_cast<int>(browsing_data::ClearBrowsingDataTab::ADVANCED));
@@ -200,7 +191,7 @@
   void SetBasicPrefsAndVerifySettings(uint64_t data_type_flags,
                                       uint64_t expected_origin_type_mask,
                                       uint64_t expected_removal_mask) {
-    PrefService* prefs = browser()->profile()->GetPrefs();
+    PrefService* prefs = profile()->GetPrefs();
     prefs->SetInteger(
         browsing_data::prefs::kLastClearBrowsingDataTab,
         static_cast<int>(browsing_data::ClearBrowsingDataTab::BASIC));
@@ -223,7 +214,7 @@
         new BrowsingDataSettingsFunction();
     SCOPED_TRACE("settings");
     std::optional<base::Value> result = RunFunctionAndReturnSingleResult(
-        function.get(), std::string("[]"), browser()->profile());
+        function.get(), std::string("[]"), profile());
 
     ASSERT_TRUE(result->is_dict());
     const base::Value::Dict& result_dict = result->GetDict();
@@ -264,9 +255,7 @@
         GetAsMask(data_to_remove, "passwords",
                   chrome_browsing_data_remover::DATA_TYPE_PASSWORDS) |
         GetAsMask(data_to_remove, "serviceWorkers",
-                  content::BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS) |
-        GetAsMask(data_to_remove, "webSQL",
-                  content::BrowsingDataRemover::DATA_TYPE_WEB_SQL);
+                  content::BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS);
 
     EXPECT_EQ(expected_removal_mask, removal_mask);
   }
@@ -278,13 +267,12 @@
     std::string args = "[{\"since\": 1}," + data_types + "]";
 
     if (permitted) {
-      EXPECT_FALSE(RunFunctionAndReturnSingleResult(function.get(), args,
-                                                    browser()->profile()))
+      EXPECT_FALSE(
+          RunFunctionAndReturnSingleResult(function.get(), args, profile()))
           << " for " << args;
     } else {
-      EXPECT_EQ(
-          RunFunctionAndReturnError(function.get(), args, browser()->profile()),
-          extension_browsing_data_api_constants::kDeleteProhibitedError)
+      EXPECT_EQ(RunFunctionAndReturnError(function.get(), args, profile()),
+                extension_browsing_data_api_constants::kDeleteProhibitedError)
           << " for " << args;
     }
   }
@@ -292,9 +280,8 @@
   void CheckInvalidRemovalArgs(const std::string& args,
                                const std::string& expected_error) {
     auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
-    EXPECT_EQ(
-        RunFunctionAndReturnError(function.get(), args, browser()->profile()),
-        expected_error)
+    EXPECT_EQ(RunFunctionAndReturnError(function.get(), args, profile()),
+              expected_error)
         << " for " << args;
   }
 
@@ -307,18 +294,14 @@
     auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
     EXPECT_FALSE(RunFunctionAndReturnSingleResult(
         function.get(), "[" + options + ", {\"localStorage\": true}]",
-        browser()->profile()))
+        profile()))
         << options;
     delegate()->VerifyAndClearExpectations();
   }
 
-  Browser* browser() { return browser_.get(); }
-
   content::MockBrowsingDataRemoverDelegate* delegate() { return &delegate_; }
 
  private:
-  std::unique_ptr<TestBrowserWindow> browser_window_;
-  std::unique_ptr<Browser> browser_;
   raw_ptr<content::BrowsingDataRemover> remover_;
   content::MockBrowsingDataRemoverDelegate delegate_;
 };
@@ -326,7 +309,7 @@
 }  // namespace
 
 TEST_F(BrowsingDataApiTest, RemovalProhibited) {
-  PrefService* prefs = browser()->profile()->GetPrefs();
+  PrefService* prefs = profile()->GetPrefs();
   prefs->SetBoolean(prefs::kAllowDeletingBrowserHistory, false);
 
   CheckRemovalPermitted("{\"cache\": true}", true);
@@ -341,7 +324,6 @@
   CheckRemovalPermitted("{\"serverBoundCertificates\": true}", true);
   CheckRemovalPermitted("{\"passwords\": true}", true);
   CheckRemovalPermitted("{\"serviceWorkers\": true}", true);
-  CheckRemovalPermitted("{\"webSQL\": true}", true);
 
   // The entire removal is prohibited if any part is.
   CheckRemovalPermitted("{\"cache\": true, \"history\": true}", false);
@@ -357,7 +339,7 @@
 TEST_F(BrowsingDataApiTest, RemoveBrowsingDataAll) {
   auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
   EXPECT_FALSE(RunFunctionAndReturnSingleResult(
-      function.get(), kRemoveEverythingArguments, browser()->profile()));
+      function.get(), kRemoveEverythingArguments, profile()));
 
   EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(1.0), GetBeginTime());
   EXPECT_EQ(
@@ -429,8 +411,6 @@
   RunBrowsingDataRemoveWithKeyAndCompareRemovalMask(
       "serviceWorkers",
       content::BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS);
-  RunBrowsingDataRemoveWithKeyAndCompareRemovalMask(
-      "webSQL", content::BrowsingDataRemover::DATA_TYPE_WEB_SQL);
 }
 
 // Test an arbitrary combination of data types.
@@ -443,7 +423,7 @@
 
 // Make sure the remove() function accepts the format produced by settings().
 TEST_F(BrowsingDataApiTest, BrowsingDataRemovalInputFromSettings) {
-  PrefService* prefs = browser()->profile()->GetPrefs();
+  PrefService* prefs = profile()->GetPrefs();
   prefs->SetInteger(
       browsing_data::prefs::kLastClearBrowsingDataTab,
       static_cast<int>(browsing_data::ClearBrowsingDataTab::ADVANCED));
@@ -464,7 +444,7 @@
         new BrowsingDataSettingsFunction();
     SCOPED_TRACE("settings_json");
     std::optional<base::Value> result = RunFunctionAndReturnSingleResult(
-        settings_function.get(), std::string("[]"), browser()->profile());
+        settings_function.get(), std::string("[]"), profile());
 
     EXPECT_TRUE(result->is_dict());
     base::Value::Dict* data_to_remove =
@@ -479,7 +459,7 @@
     SCOPED_TRACE("remove_json");
     EXPECT_FALSE(RunFunctionAndReturnSingleResult(
         remove_function.get(), std::string("[{\"since\": 1},") + *json + "]",
-        browser()->profile()));
+        profile()));
     EXPECT_EQ(expected_mask, GetRemovalMask());
     EXPECT_EQ(UNPROTECTED_WEB, GetOriginTypeMask());
   }
@@ -508,8 +488,6 @@
       chrome_browsing_data_remover::DATA_TYPE_PASSWORDS);
   RunAndCompareRemovalMask<BrowsingDataRemoveServiceWorkersFunction>(
       content::BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS);
-  RunAndCompareRemovalMask<BrowsingDataRemoveWebSQLFunction>(
-      content::BrowsingDataRemover::DATA_TYPE_WEB_SQL);
 }
 
 // Test the processing of the 'delete since' preference.
@@ -638,7 +616,7 @@
       function.get(),
       R"([{"excludeOrigins": ["http://example.com"]},
                     {"cookies": true}])",
-      browser()->profile()));
+      profile()));
   delegate()->VerifyAndClearExpectations();
 }
 
@@ -665,7 +643,7 @@
       function.get(),
       R"([{"origins": ["http://www.example.com"]},
                     {"cookies": true, "localStorage": true}])",
-      browser()->profile()));
+      profile()));
   delegate()->VerifyAndClearExpectations();
 }
 
diff --git a/chrome/browser/extensions/api/experimental_actor/experimental_actor_api.cc b/chrome/browser/extensions/api/experimental_actor/experimental_actor_api.cc
index 2b87244..05f8da5f 100644
--- a/chrome/browser/extensions/api/experimental_actor/experimental_actor_api.cc
+++ b/chrome/browser/extensions/api/experimental_actor/experimental_actor_api.cc
@@ -11,6 +11,9 @@
 #include "base/check.h"
 #include "base/strings/string_split.h"
 #include "base/version_info/channel.h"
+#include "chrome/browser/actor/actor_keyed_service.h"
+#include "chrome/browser/actor/actor_keyed_service_factory.h"
+#include "chrome/browser/actor/task_id.h"
 #include "chrome/browser/ai/ai_data_keyed_service.h"
 #include "chrome/browser/ai/ai_data_keyed_service_factory.h"
 #include "chrome/browser/extensions/chrome_extension_function_details.h"
@@ -78,9 +81,8 @@
     return false;
   }
 
-  auto* ai_data_service =
-      AiDataKeyedServiceFactory::GetAiDataKeyedService(browser_context());
-  if (!ai_data_service) {
+  auto* actor_service = actor::ActorKeyedService::Get(browser_context());
+  if (!actor_service) {
     *error = "Incognito profile not supported.";
     return false;
   }
@@ -110,10 +112,9 @@
       ConvertSessionTabIdToTabHandle(task.tab_id(), browser_context());
   task.set_tab_id(tab_handle);
 
-  auto* ai_data_service =
-      AiDataKeyedServiceFactory::GetAiDataKeyedService(browser_context());
+  auto* actor_service = actor::ActorKeyedService::Get(browser_context());
 
-  ai_data_service->StartTask(
+  actor_service->StartTask(
       std::move(task),
       base::BindOnce(&ExperimentalActorStartTaskFunction::OnTaskStarted, this));
 
@@ -138,22 +139,11 @@
   auto params = api::experimental_actor::StopTask::Params::Create(args());
   EXTENSION_FUNCTION_VALIDATE(params);
 
-  auto* ai_data_service =
-      AiDataKeyedServiceFactory::GetAiDataKeyedService(browser_context());
+  auto* actor_service = actor::ActorKeyedService::Get(browser_context());
 
-  ai_data_service->StopTask(
-      params->task_id,
-      base::BindOnce(&ExperimentalActorStopTaskFunction::OnTaskStopped, this));
-
-  return RespondLater();
-}
-
-void ExperimentalActorStopTaskFunction::OnTaskStopped(bool success) {
-  if (!success) {
-    Respond(Error("Task not found."));
-    return;
-  }
-  Respond(ArgumentList(api::experimental_actor::StopTask::Results::Create()));
+  actor_service->StopTask(actor::TaskId(params->task_id));
+  return RespondNow(
+      ArgumentList(api::experimental_actor::StopTask::Results::Create()));
 }
 
 ExperimentalActorExecuteActionFunction::
@@ -164,6 +154,10 @@
 
 ExtensionFunction::ResponseAction
 ExperimentalActorExecuteActionFunction::Run() {
+#if !BUILDFLAG(ENABLE_GLIC)
+  return RespondNow(
+      Error("Execute action not supported for this build configuration."));
+#else
   auto params = api::experimental_actor::ExecuteAction::Params::Create(args());
   EXTENSION_FUNCTION_VALIDATE(params);
   optimization_guide::proto::BrowserAction action;
@@ -179,13 +173,13 @@
 
   auto* ai_data_service =
       AiDataKeyedServiceFactory::GetAiDataKeyedService(browser_context());
-
   ai_data_service->ExecuteAction(
       std::move(action),
       base::BindOnce(
           &ExperimentalActorExecuteActionFunction::OnResponseReceived, this));
 
   return RespondLater();
+#endif
 }
 
 void ExperimentalActorExecuteActionFunction::OnResponseReceived(
diff --git a/chrome/browser/extensions/api/experimental_actor/experimental_actor_api.h b/chrome/browser/extensions/api/experimental_actor/experimental_actor_api.h
index b8daef9..a86e2024 100644
--- a/chrome/browser/extensions/api/experimental_actor/experimental_actor_api.h
+++ b/chrome/browser/extensions/api/experimental_actor/experimental_actor_api.h
@@ -57,7 +57,6 @@
  protected:
   ~ExperimentalActorStopTaskFunction() override;
   ResponseAction Run() override;
-  void OnTaskStopped(bool success);
 
   DECLARE_EXTENSION_FUNCTION("experimentalActor.stopTask",
                              EXPERIMENTALACTOR_STOPTASK)
diff --git a/chrome/browser/extensions/api/font_settings/BUILD.gn b/chrome/browser/extensions/api/font_settings/BUILD.gn
index 1650073..8970ed8f 100644
--- a/chrome/browser/extensions/api/font_settings/BUILD.gn
+++ b/chrome/browser/extensions/api/font_settings/BUILD.gn
@@ -4,7 +4,7 @@
 
 import("//extensions/buildflags/buildflags.gni")
 
-assert(enable_extensions)
+assert(enable_extensions_core)
 
 source_set("font_settings") {
   sources = [
diff --git a/chrome/browser/extensions/api/font_settings/font_settings_api.cc b/chrome/browser/extensions/api/font_settings/font_settings_api.cc
index a21698d..471ca3d9 100644
--- a/chrome/browser/extensions/api/font_settings/font_settings_api.cc
+++ b/chrome/browser/extensions/api/font_settings/font_settings_api.cc
@@ -341,9 +341,15 @@
 }
 
 ExtensionFunction::ResponseAction FontSettingsGetFontListFunction::Run() {
+#if BUILDFLAG(IS_ANDROID)
+  // Android does not support a mechanism to get "all installed fonts" like
+  // Windows/Mac/Linux.
+  return RespondNow(WithArguments(base::Value::List()));
+#else
   content::GetFontListAsync(
       BindOnce(&FontSettingsGetFontListFunction::FontListHasLoaded, this));
   return RespondLater();
+#endif
 }
 
 void FontSettingsGetFontListFunction::FontListHasLoaded(
diff --git a/chrome/browser/extensions/api/font_settings/font_settings_api.h b/chrome/browser/extensions/api/font_settings/font_settings_api.h
index d4dc21e..4356e72 100644
--- a/chrome/browser/extensions/api/font_settings/font_settings_api.h
+++ b/chrome/browser/extensions/api/font_settings/font_settings_api.h
@@ -15,6 +15,9 @@
 #include "base/values.h"
 #include "extensions/browser/browser_context_keyed_api_factory.h"
 #include "extensions/browser/extension_function.h"
+#include "extensions/buildflags/buildflags.h"
+
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
 
 namespace content {
 class BrowserContext;
diff --git a/chrome/browser/extensions/api/font_settings/font_settings_apitest.cc b/chrome/browser/extensions/api/font_settings/font_settings_apitest.cc
index 9b2c1e5f..77e90886 100644
--- a/chrome/browser/extensions/api/font_settings/font_settings_apitest.cc
+++ b/chrome/browser/extensions/api/font_settings/font_settings_apitest.cc
@@ -6,17 +6,18 @@
 
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/browser_test.h"
+#include "extensions/buildflags/buildflags.h"
+
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
 
 namespace extensions {
 
 // Test of extension API on a standard profile.
 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, FontSettings) {
-  PrefService* prefs = browser()->profile()->GetPrefs();
+  PrefService* prefs = profile()->GetPrefs();
   prefs->SetString(prefs::kWebKitStandardFontFamilyKorean, "Tahoma");
   prefs->SetString(prefs::kWebKitSansSerifFontFamily, "Arial");
   prefs->SetInteger(prefs::kWebKitDefaultFontSize, 16);
@@ -28,7 +29,7 @@
 
 // Test of extension API in incognito split mode.
 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, FontSettingsIncognito) {
-  PrefService* prefs = browser()->profile()->GetPrefs();
+  PrefService* prefs = profile()->GetPrefs();
   prefs->SetString(prefs::kWebKitStandardFontFamilyKorean, "Tahoma");
   prefs->SetString(prefs::kWebKitSansSerifFontFamily, "Arial");
   prefs->SetInteger(prefs::kWebKitDefaultFontSize, 16);
@@ -36,12 +37,13 @@
   EXPECT_TRUE(RunExtensionTest(
       "font_settings/incognito",
       {.extension_url = "launch.html", .open_in_incognito = true},
-      {.allow_in_incognito = true}));
+      {.allow_in_incognito = true}))
+      << message_;
 }
 
 // Test the list of generic font families.
 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, FontSettingsGenericFamilies) {
-  PrefService* prefs = browser()->profile()->GetPrefs();
+  PrefService* prefs = profile()->GetPrefs();
   // TODO(crbug.com/40187445): Test generic font families added to CSS Fonts
   // Module Level 4.
   prefs->SetString(prefs::kWebKitStandardFontFamily, "default_standard");
diff --git a/chrome/browser/extensions/api/history/history_apitest.cc b/chrome/browser/extensions/api/history/history_apitest.cc
index 887d2194..2c34d5a 100644
--- a/chrome/browser/extensions/api/history/history_apitest.cc
+++ b/chrome/browser/extensions/api/history/history_apitest.cc
@@ -102,10 +102,6 @@
                          HistoryApiTest,
                          ::testing::Values(ContextType::kServiceWorker));
 
-// TODO(crbug.com/419057486): Enable for desktop Android once
-// SetUpBrowserContextKeyedServices can be called for Android browser tests.
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-
 class SyncEnabledHistoryApiTest : public HistoryApiTest {
  public:
   void SetUpBrowserContextKeyedServices(
@@ -132,8 +128,6 @@
                          SyncEnabledHistoryApiTest,
                          ::testing::Values(ContextType::kServiceWorker));
 
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
-
 IN_PROC_BROWSER_TEST_P(HistoryApiTest, MiscSearch) {
   ASSERT_TRUE(StartEmbeddedTestServer());
   ASSERT_TRUE(RunExtensionTest("history/regular/misc_search")) << message_;
@@ -161,10 +155,6 @@
   ASSERT_TRUE(RunExtensionTest("history/regular/get_visits")) << message_;
 }
 
-// TODO(crbug.com/419057486): Enable for desktop Android once
-// SetUpBrowserContextKeyedServices can be called for Android browser tests.
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-
 IN_PROC_BROWSER_TEST_P(SyncEnabledHistoryApiTest, GetVisits_Foreign) {
   ASSERT_TRUE(StartEmbeddedTestServer());
 
@@ -228,8 +218,6 @@
   ASSERT_TRUE(RunExtensionTest(test_dir.UnpackedPath(), {}, {})) << message_;
 }
 
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
-
 IN_PROC_BROWSER_TEST_P(HistoryApiTest, SearchAfterAdd) {
   ASSERT_TRUE(StartEmbeddedTestServer());
   ASSERT_TRUE(RunExtensionTest("history/regular/search_after_add")) << message_;
diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc
index 98ca994..2641c7c 100644
--- a/chrome/browser/extensions/component_loader.cc
+++ b/chrome/browser/extensions/component_loader.cc
@@ -714,7 +714,11 @@
 void ComponentLoader::AddChromeOsSpeechSynthesisExtensions() {
   if (!Exists(extension_misc::kGoogleSpeechSynthesisExtensionId)) {
     AddComponentFromDir(
-        base::FilePath(extension_misc::kGoogleSpeechSynthesisExtensionPath),
+        ::features::IsAccessibilityManifestV3EnabledForGoogleTts()
+            ? base::FilePath(
+                  extension_misc::kGoogleSpeechSynthesisManifestV3ExtensionPath)
+            : base::FilePath(
+                  extension_misc::kGoogleSpeechSynthesisExtensionPath),
         extension_misc::kGoogleSpeechSynthesisExtensionId,
         base::BindRepeating(
             &ComponentLoader::FinishLoadSpeechSynthesisExtension,
diff --git a/chrome/browser/extensions/extension_management.cc b/chrome/browser/extensions/extension_management.cc
index ae782891..5496b03 100644
--- a/chrome/browser/extensions/extension_management.cc
+++ b/chrome/browser/extensions/extension_management.cc
@@ -29,6 +29,7 @@
 #include "base/values.h"
 #include "base/version.h"
 #include "build/chromeos_buildflags.h"
+#include "chrome/browser/enterprise/util/managed_browser_utils.h"
 #include "chrome/browser/extensions/cws_info_service.h"
 #include "chrome/browser/extensions/extension_management_constants.h"
 #include "chrome/browser/extensions/extension_management_internal.h"
@@ -61,6 +62,10 @@
 #include "extensions/common/url_pattern.h"
 #include "url/gurl.h"
 
+#if BUILDFLAG(IS_ANDROID)
+#include "google_apis/gaia/gaia_auth_util.h"
+#endif
+
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/themes/theme_service_factory.h"
@@ -169,6 +174,25 @@
 ManagedInstallationMode ExtensionManagement::GetInstallationMode(
     const ExtensionId& extension_id,
     const std::string& update_url) {
+// Block extensions for managed profiles on Desktop Android. This is
+// temporary until extensions are ready for dogfooding.
+// TODO(crbug.com/422307625): Remove this check once extensions are ready for
+// dogfooding.
+#if BUILDFLAG(IS_ANDROID)
+  if (enterprise_util::IsBrowserManaged(profile_)) {
+    // Disable extensions only for specific managed accounts.
+    // This check keeps many tests from failing.
+    std::string user_name = profile_->GetProfileUserName();
+    // Crude check to avoid passing invalid strings to `ExtractDomainName`.
+    if (base::Contains(user_name, "@")) {
+      std::string domain = gaia::ExtractDomainName(user_name);
+      if (domain == "google.com" || domain == "managedchrome.com") {
+        return ManagedInstallationMode::kRemoved;
+      }
+    }
+  }
+#endif  // BUILDFLAG(IS_ANDROID)
+
   // Check per-extension installation mode setting first.
   auto* setting = GetSettingsForId(extension_id);
   if (setting)
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedResourceFetcher.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedResourceFetcher.java
index 0766bab..41ca698 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedResourceFetcher.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedResourceFetcher.java
@@ -77,8 +77,7 @@
         PostTask.postTask(
                 TaskTraits.UI_DEFAULT,
                 () -> {
-                    List<String> headerNamesAndValues =
-                            new ArrayList<String>(request.headers.size() * 2);
+                    List<String> headerNamesAndValues = new ArrayList<>(request.headers.size() * 2);
                     for (Header header : request.headers) {
                         headerNamesAndValues.add(header.name);
                         headerNamesAndValues.add(header.value);
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTracker.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTracker.java
index 3d88ad1..bcb0a89 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTracker.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTracker.java
@@ -55,7 +55,7 @@
     private @Nullable ListLayoutHelper mLayoutHelper;
     // The set of content keys already reported as mostly visible (66% threshold), which is used to
     // determine if a slice has been viewed by the user.
-    private final HashSet<String> mContentKeysMostlyVisible = new HashSet<String>();
+    private final HashSet<String> mContentKeysMostlyVisible = new HashSet<>();
     // The set of content keys already reported as barely visible (5% threshold), which is used to
     // determine if a slice has entered the view port.
     private final HashSet<String> mContentKeysBarelyVisible = new HashSet<>();
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSurfaceScopeDependencyProviderImplTest.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSurfaceScopeDependencyProviderImplTest.java
index 700fde81..75605a7 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSurfaceScopeDependencyProviderImplTest.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSurfaceScopeDependencyProviderImplTest.java
@@ -57,7 +57,7 @@
         FeedSurfaceScopeDependencyProviderImpl dependencyProvider =
                 new FeedSurfaceScopeDependencyProviderImpl(
                         /* activity= */ null, /* activityContext= */ null, /* darkMode= */ false);
-        ArrayList<String> calls = new ArrayList<String>();
+        ArrayList<String> calls = new ArrayList<>();
 
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
@@ -82,7 +82,7 @@
         FeedSurfaceScopeDependencyProviderImpl dependencyProvider =
                 new FeedSurfaceScopeDependencyProviderImpl(
                         /* activity= */ null, /* activityContext= */ null, /* darkMode= */ false);
-        ArrayList<String> calls = new ArrayList<String>();
+        ArrayList<String> calls = new ArrayList<>();
 
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
@@ -107,7 +107,7 @@
         FeedSurfaceScopeDependencyProviderImpl dependencyProvider =
                 new FeedSurfaceScopeDependencyProviderImpl(
                         /* activity= */ null, /* activityContext= */ null, /* darkMode= */ false);
-        ArrayList<String> calls = new ArrayList<String>();
+        ArrayList<String> calls = new ArrayList<>();
 
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/feedmanagement/FeedManagementCoordinator.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/feedmanagement/FeedManagementCoordinator.java
index a81cb39..c3a6953 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/feedmanagement/FeedManagementCoordinator.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/feedmanagement/FeedManagementCoordinator.java
@@ -35,7 +35,7 @@
         ModelListAdapter adapter = new ModelListAdapter(listItems);
         adapter.registerType(
                 FeedManagementItemProperties.DEFAULT_ITEM_TYPE,
-                new LayoutViewBuilder<FeedManagementItemView>(R.layout.feed_management_list_item),
+                new LayoutViewBuilder<>(R.layout.feed_management_list_item),
                 FeedManagementItemViewBinder::bind);
 
         // Inflate the XML.
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/followmanagement/FollowManagementCoordinator.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/followmanagement/FollowManagementCoordinator.java
index 56abff6..d5cd052 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/followmanagement/FollowManagementCoordinator.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/followmanagement/FollowManagementCoordinator.java
@@ -7,7 +7,6 @@
 import android.app.Activity;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.widget.LinearLayout;
 
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.recyclerview.widget.LinearLayoutManager;
@@ -38,15 +37,15 @@
         // Register types for both the full and empty states.
         adapter.registerType(
                 FollowManagementItemProperties.DEFAULT_ITEM_TYPE,
-                new LayoutViewBuilder<FollowManagementItemView>(R.layout.follow_management_item),
+                new LayoutViewBuilder<>(R.layout.follow_management_item),
                 FollowManagementItemViewBinder::bind);
         adapter.registerType(
                 FollowManagementItemProperties.EMPTY_ITEM_TYPE,
-                new LayoutViewBuilder<LinearLayout>(R.layout.follow_management_empty_state),
+                new LayoutViewBuilder<>(R.layout.follow_management_empty_state),
                 (unusedModel, unusedView, unusedKey) -> {});
         adapter.registerType(
                 FollowManagementItemProperties.LOADING_ITEM_TYPE,
-                new LayoutViewBuilder<LinearLayout>(R.layout.feed_spinner),
+                new LayoutViewBuilder<>(R.layout.feed_spinner),
                 (unusedModel, unusedView, unusedKey) -> {});
 
         // Inflate the XML for the activity.
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/followmanagement/FollowManagementMediatorTest.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/followmanagement/FollowManagementMediatorTest.java
index 676f9364..4ab8025 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/followmanagement/FollowManagementMediatorTest.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/followmanagement/FollowManagementMediatorTest.java
@@ -92,7 +92,7 @@
 
     @Test
     public void testEmptyWebFeedList() {
-        mFollowManagementMediator.fillRecyclerView(new ArrayList<WebFeedMetadata>());
+        mFollowManagementMediator.fillRecyclerView(new ArrayList<>());
 
         assertEquals("<empty>", modelListToString());
     }
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 6e54748d..e6ad254 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2623,6 +2623,26 @@
     "expiry_milestone": 145
   },
   {
+    "name": "enable-accessibility-manifest-v3-espeakng",
+    "owners": [
+      "mwoj@google.com",
+      "xiyuan@google.com",
+      "akihiroota@google.com",
+      "//ui/accessibility/OWNERS"
+    ],
+    "expiry_milestone": 145
+  },
+  {
+    "name": "enable-accessibility-manifest-v3-google-tts",
+    "owners": [
+      "xiyuan@google.com",
+      "mwoj@google.com",
+      "akihiroota@google.com",
+      "//ui/accessibility/OWNERS"
+    ],
+    "expiry_milestone": 145
+  },
+  {
     "name": "enable-accessibility-manifest-v3-select-to-speak",
     "owners": [ "spectranaut@igalia.com", "akihiroota@chromium.org", "//ui/accessibility/OWNERS" ],
     "expiry_milestone": 145
@@ -4699,15 +4719,6 @@
     "expiry_milestone": 150
   },
   {
-    "name": "espeakng-manifest-v3",
-    "owners": [
-      "mwoj@google.com",
-      "xiyuan@google.com",
-      "akihiroota@google.com"
-    ],
-    "expiry_milestone": 145
-  },
-  {
     "name": "event-based-log-upload",
     "owners": [ "iremuguz@google.com", "chromeos-commercial-supportability@google.com" ],
     "expiry_milestone": 140
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 823fcfc..8945259b 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -7140,6 +7140,11 @@
 const char kAccessibilityManifestV3EspeakNGDescription[] =
     "Experimental migration of EspeakNG TTS from extension manifest v2 to v3.";
 
+const char kAccessibilityManifestV3GoogleTtsName[] =
+    "Changes accessibility extension Google TTS manifest v2 to v3.";
+const char kAccessibilityManifestV3GoogleTtsDescription[] =
+    "Experimental migration of Google TTS from extension manifest v2 to v3.";
+
 const char kAccessibilityManifestV3SelectToSpeakName[] =
     "Changes accessibility extension Select to Speak manifest v2 to v3.";
 const char kAccessibilityManifestV3SelectToSpeakDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 6e2ebb8..649db32 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -4191,6 +4191,9 @@
 extern const char kAccessibilityManifestV3EspeakNGName[];
 extern const char kAccessibilityManifestV3EspeakNGDescription[];
 
+extern const char kAccessibilityManifestV3GoogleTtsName[];
+extern const char kAccessibilityManifestV3GoogleTtsDescription[];
+
 extern const char kAccessibilityManifestV3SelectToSpeakName[];
 extern const char kAccessibilityManifestV3SelectToSpeakDescription[];
 
diff --git a/chrome/browser/glic/glic_metrics.cc b/chrome/browser/glic/glic_metrics.cc
index cc8bf6c..e7eb4bec 100644
--- a/chrome/browser/glic/glic_metrics.cc
+++ b/chrome/browser/glic/glic_metrics.cc
@@ -14,6 +14,10 @@
 #include "chrome/browser/glic/glic_pref_names.h"
 #include "chrome/browser/glic/host/context/glic_focused_tab_manager.h"
 #include "chrome/browser/glic/widget/glic_window_controller.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list_observer.h"
+#include "chrome/browser/ui/browser_window/public/desktop_browser_window_capabilities.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/common/chrome_features.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/render_frame_host.h"
@@ -22,6 +26,7 @@
 #include "services/metrics/public/cpp/ukm_recorder.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "ui/base/accelerators/accelerator.h"
+#include "ui/views/widget/widget.h"
 
 namespace glic {
 
@@ -97,8 +102,102 @@
 }
 }  // namespace
 
+namespace internal {
+
+// LINT.IfChange(BrowserActiveState)
+// This must match enums.xml.
+enum class BrowserActiveState {
+  // A browser window is currently active, or was active less than one second
+  // ago. This 1 second allowance helps ignore differences in window activation
+  // timing for different platforms.
+  kBrowserActive = 0,
+  // A browser window is not active, but was active within the last N seconds,
+  // and is still visible.
+  kBrowserRecentlyActive1to5s = 1,
+  kBrowserRecentlyActive5to10s = 2,
+  kBrowserRecentlyActive10to30s = 3,
+  // No browser windows are active or have been active within the last 10
+  // seconds, but a browser window is still visible.
+  kBrowserInactive = 4,
+  // No browser windows are visible.
+  kBrowserHidden = 5,
+
+  kMaxValue = kBrowserHidden,
+};
+// LINT.ThenChange(//tools/metrics/histograms/metadata/glic/enums.xml:GlicBrowserActiveState)
+
+// Computes BrowserActiveState.
+class BrowserActivityObserver : public BrowserListObserver {
+ public:
+  BrowserActivityObserver() { BrowserList::AddObserver(this); }
+  ~BrowserActivityObserver() override { BrowserList::RemoveObserver(this); }
+
+  BrowserActiveState GetBrowserActiveState() const {
+    if (active_browser_) {
+      return BrowserActiveState::kBrowserActive;
+    }
+    bool browser_hidden = true;
+    for (Browser* browser : *BrowserList::GetInstance()) {
+      if (!browser->IsMinimized() &&
+          browser->capabilities()->IsVisibleOnScreen() &&
+          browser->IsVisible()) {
+        browser_hidden = false;
+        break;
+      }
+    }
+    if (browser_hidden) {
+      return BrowserActiveState::kBrowserHidden;
+    }
+    if (last_browser_active_time_) {
+      auto time_since_active =
+          base::TimeTicks::Now() - *last_browser_active_time_;
+      if (time_since_active < base::Seconds(1)) {
+        return BrowserActiveState::kBrowserActive;
+      } else if (time_since_active < base::Seconds(5)) {
+        return BrowserActiveState::kBrowserRecentlyActive1to5s;
+      } else if (time_since_active < base::Seconds(10)) {
+        return BrowserActiveState::kBrowserRecentlyActive5to10s;
+      } else if (time_since_active < base::Seconds(30)) {
+        return BrowserActiveState::kBrowserRecentlyActive10to30s;
+      }
+    }
+    return BrowserActiveState::kBrowserInactive;
+  }
+
+  // BrowserListObserver impl.
+  void OnBrowserRemoved(Browser* browser) override {
+    if (active_browser_ == browser) {
+      active_browser_ = nullptr;
+    }
+  }
+  void OnBrowserSetLastActive(Browser* browser) override {
+    active_browser_ = browser;
+    last_browser_active_time_ = std::nullopt;
+  }
+  void OnBrowserNoLongerActive(Browser* browser) override {
+    if (active_browser_ == browser) {
+      active_browser_ = nullptr;
+    }
+    if (!active_browser_) {
+      last_browser_active_time_ = base::TimeTicks::Now();
+    }
+  }
+
+ private:
+  // The active browser, or null if none is active.
+  raw_ptr<Browser> active_browser_ = nullptr;
+
+  // If the browser is not active, the time at which it was last active.
+  std::optional<base::TimeTicks> last_browser_active_time_;
+};
+
+}  // namespace internal
+
 GlicMetrics::GlicMetrics(Profile* profile, GlicEnabling* enabling)
-    : profile_(profile), enabling_(enabling) {
+    : profile_(profile),
+      enabling_(enabling),
+      browser_activity_observer_(
+          std::make_unique<internal::BrowserActivityObserver>()) {
   impression_timer_.Start(
       FROM_HERE, base::Minutes(15),
       base::BindRepeating(&GlicMetrics::OnImpressionTimerFired,
@@ -124,6 +223,9 @@
 GlicMetrics::~GlicMetrics() = default;
 
 void GlicMetrics::OnUserInputSubmitted(mojom::WebClientMode mode) {
+  base::UmaHistogramEnumeration(
+      "Glic.Session.InputSubmit.BrowserActiveState",
+      browser_activity_observer_->GetBrowserActiveState());
   base::RecordAction(base::UserMetricsAction("GlicResponseInputSubmit"));
   input_submitted_time_ = base::TimeTicks::Now();
   input_mode_ = mode;
@@ -132,6 +234,9 @@
 
 void GlicMetrics::OnResponseStarted() {
   response_started_ = true;
+  base::UmaHistogramEnumeration(
+      "Glic.Session.ResponseStart.BrowserActiveState",
+      browser_activity_observer_->GetBrowserActiveState());
   base::RecordAction(base::UserMetricsAction("GlicResponseStart"));
 
   // It doesn't make sense to record response start without input submission.
@@ -226,6 +331,9 @@
 
 void GlicMetrics::OnGlicWindowOpen(bool attached,
                                    mojom::InvocationSource source) {
+  base::UmaHistogramEnumeration(
+      "Glic.Session.Open.BrowserActiveState",
+      browser_activity_observer_->GetBrowserActiveState());
   base::RecordAction(base::UserMetricsAction("GlicSessionBegin"));
   session_start_time_ = base::TimeTicks::Now();
   invocation_source_ = source;
diff --git a/chrome/browser/glic/glic_metrics.h b/chrome/browser/glic/glic_metrics.h
index 7497658..8356ac0 100644
--- a/chrome/browser/glic/glic_metrics.h
+++ b/chrome/browser/glic/glic_metrics.h
@@ -161,6 +161,10 @@
 class GlicFocusedTabManager;
 class GlicWindowController;
 
+namespace internal {
+class BrowserActivityObserver;
+}
+
 // Responsible for all glic web-client metrics, and all stateful glic metrics.
 // Some stateless glic metrics are logged inline in the relevant code for
 // convenience.
@@ -319,6 +323,7 @@
   // `input_mode_`.
   base::TimeTicks scroll_input_submitted_time_;
   mojom::WebClientMode scroll_input_mode_;
+  std::unique_ptr<internal::BrowserActivityObserver> browser_activity_observer_;
 };
 
 }  // namespace glic
diff --git a/chrome/browser/glic/glic_metrics_unittest.cc b/chrome/browser/glic/glic_metrics_unittest.cc
index 217ab30..7e95589 100644
--- a/chrome/browser/glic/glic_metrics_unittest.cc
+++ b/chrome/browser/glic/glic_metrics_unittest.cc
@@ -186,6 +186,11 @@
   metrics_->OnSessionTerminated();
 
   histogram_tester_.ExpectTotalCount("Glic.Response.StopTime", 1);
+  histogram_tester_.ExpectUniqueSample(
+      "Glic.Session.InputSubmit.BrowserActiveState", 5 /*kBrowserHidden*/, 1);
+  histogram_tester_.ExpectUniqueSample(
+      "Glic.Session.ResponseStart.BrowserActiveState", 5 /*kBrowserHidden*/, 1);
+
   EXPECT_EQ(user_action_tester_.GetActionCount("GlicResponseInputSubmit"), 1);
   EXPECT_EQ(user_action_tester_.GetActionCount("GlicResponseStart"), 1);
   EXPECT_EQ(user_action_tester_.GetActionCount("GlicResponseStop"), 1);
@@ -206,6 +211,9 @@
   metrics_->OnGlicWindowClose(std::nullopt, gfx::Point());
 
   histogram_tester_.ExpectTotalCount("Glic.Response.StopTime", 1);
+  histogram_tester_.ExpectUniqueSample("Glic.Session.Open.BrowserActiveState",
+                                       5 /*kBrowserHidden*/, 1);
+  EXPECT_EQ(user_action_tester_.GetActionCount("GlicSessionBegin"), 1);
   EXPECT_EQ(user_action_tester_.GetActionCount("GlicResponseInputSubmit"), 1);
   EXPECT_EQ(user_action_tester_.GetActionCount("GlicResponseStart"), 1);
   EXPECT_EQ(user_action_tester_.GetActionCount("GlicResponseStop"), 1);
diff --git a/chrome/browser/glic/host/glic_ui_interactive_uitest.cc b/chrome/browser/glic/host/glic_ui_interactive_uitest.cc
index d9150de..6065a2ba9 100644
--- a/chrome/browser/glic/host/glic_ui_interactive_uitest.cc
+++ b/chrome/browser/glic/host/glic_ui_interactive_uitest.cc
@@ -7,6 +7,7 @@
 #include "base/scoped_observation.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/to_string.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
 #include "chrome/browser/glic/host/glic.mojom-shared.h"
@@ -224,6 +225,23 @@
   const DeepQuery kContentsPanel = {"#guestPanel"};
 };
 
+class GlicUiInteractiveTest : public GlicUiInteractiveUiTestBase {
+ public:
+  GlicUiInteractiveTest()
+      : GlicUiInteractiveUiTestBase(TestParams(/*connected=*/true)) {}
+  ~GlicUiInteractiveTest() override = default;
+};
+
+IN_PROC_BROWSER_TEST_F(GlicUiInteractiveTest, OpenGlicWindow) {
+  base::HistogramTester histogram_tester;
+  RunTestSequence(
+      ObserveState(kGlicUiStateHistory, &host()),
+      OpenGlicWindow(GlicWindowMode::kDetached, GlicInstrumentMode::kHostOnly));
+  // The browser is active when opening the Glic window.
+  histogram_tester.ExpectUniqueSample("Glic.Session.Open.BrowserActiveState",
+                                      0 /*kBrowserActive*/, 1);
+}
+
 // Tests the network being connected at startup (as normal).
 class GlicUiConnectedUiTest : public GlicUiInteractiveUiTestBase,
                               public testing::WithParamInterface<bool> {
diff --git a/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/PresetHubLayoutAnimatorProvider.java b/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/PresetHubLayoutAnimatorProvider.java
index ef7ad1c..c48fc1d 100644
--- a/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/PresetHubLayoutAnimatorProvider.java
+++ b/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/PresetHubLayoutAnimatorProvider.java
@@ -25,7 +25,7 @@
      * @param animator The {@link HubLayoutAnimator} to use.
      */
     public PresetHubLayoutAnimatorProvider(@NonNull HubLayoutAnimator animator) {
-        mPresetAnimatorSupplier = new SyncOneshotSupplierImpl<HubLayoutAnimator>();
+        mPresetAnimatorSupplier = new SyncOneshotSupplierImpl<>();
         mPresetAnimatorSupplier.set(animator);
     }
 
diff --git a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubLayoutAnimationRunnerImpl.java b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubLayoutAnimationRunnerImpl.java
index 32f9a04..80a54604d 100644
--- a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubLayoutAnimationRunnerImpl.java
+++ b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubLayoutAnimationRunnerImpl.java
@@ -195,7 +195,7 @@
     @EnsuresNonNull("mListeners")
     private void ensureListenersList() {
         if (mListeners == null) {
-            mListeners = new LinkedList<HubLayoutAnimationListener>();
+            mListeners = new LinkedList<>();
         }
     }
 }
diff --git a/chrome/browser/net/storage_test_utils.cc b/chrome/browser/net/storage_test_utils.cc
index 7bee6713..ea0592e6 100644
--- a/chrome/browser/net/storage_test_utils.cc
+++ b/chrome/browser/net/storage_test_utils.cc
@@ -11,15 +11,8 @@
 const std::vector<std::string> kCookiesTypesForFrame{"Cookie", "CookieStore"};
 
 const std::vector<std::string> kStorageTypesForFrame{
-    "LocalStorage", "FileSystem", "FileSystemAccess", "SessionStorage",
-    "IndexedDb",     "CacheStorage",     "ServiceWorker",
-#if BUILDFLAG(IS_ANDROID)
-    // TODO(crbug.com/333756088): WebSQL is disabled everywhere by default as of
-    // M119 (crbug/695592) except on Android WebView. This is enabled for
-    // Android only to indirectly cover WebSQL deletions on WebView.
-    "WebSql",
-#endif
-};
+    "LocalStorage", "FileSystem",   "FileSystemAccess", "SessionStorage",
+    "IndexedDb",    "CacheStorage", "ServiceWorker"};
 
 const std::vector<std::string> kStorageTypesForWorker{
     "WorkerFileSystemAccess", "WorkerCacheStorage", "WorkerIndexedDb"};
@@ -67,14 +60,7 @@
     actual[data_type] = content::EvalJs(frame, "set" + data_type + "()",
                                         content::EXECUTE_SCRIPT_NO_USER_GESTURE)
                             .ExtractBool();
-    if (frame->GetLastCommittedOrigin() !=
-            frame->GetMainFrame()->GetLastCommittedOrigin() &&
-        data_type == "WebSql") {
-      // Third-party context WebSQL is disabled as of M97.
-      expected[data_type] = false;
-    } else {
-      expected[data_type] = expected_to_be_set;
-    }
+    expected[data_type] = expected_to_be_set;
   }
   EXPECT_THAT(actual, testing::UnorderedElementsAreArray(expected))
       << "(expected at " << location.ToString() << ")";
@@ -103,14 +89,7 @@
     actual[data_type] = content::EvalJs(frame, "has" + data_type + "();",
                                         content::EXECUTE_SCRIPT_NO_USER_GESTURE)
                             .ExtractBool();
-    if (frame->GetLastCommittedOrigin() !=
-            frame->GetMainFrame()->GetLastCommittedOrigin() &&
-        data_type == "WebSql") {
-      // Third-party context WebSQL is disabled as of M97.
-      expected_elts[data_type] = false;
-    } else {
-      expected_elts[data_type] = expected;
-    }
+    expected_elts[data_type] = expected;
   }
   EXPECT_THAT(actual, testing::UnorderedElementsAreArray(expected_elts))
       << "(expected at " << location.ToString() << ")";
diff --git a/chrome/browser/notifications/mac/mac_notification_provider_factory.cc b/chrome/browser/notifications/mac/mac_notification_provider_factory.cc
index 14551305..0b29829c 100644
--- a/chrome/browser/notifications/mac/mac_notification_provider_factory.cc
+++ b/chrome/browser/notifications/mac/mac_notification_provider_factory.cc
@@ -12,6 +12,7 @@
 #include "base/task/thread_pool.h"
 #include "chrome/browser/apps/app_shim/app_shim_manager_mac.h"
 #include "chrome/browser/child_process_host_flags.h"
+#include "chrome/browser/web_applications/os_integration/mac/web_app_shortcut_mac.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/services/mac_notifications/mac_notification_provider_impl.h"
 #include "chrome/services/mac_notifications/public/mojom/mac_notifications.mojom.h"
@@ -64,8 +65,7 @@
       notification_style_ == mac_notifications::NotificationStyle::kAppShim,
       web_app_id_.empty());
   if (notification_style_ == mac_notifications::NotificationStyle::kAppShim) {
-    CHECK(base::FeatureList::IsEnabled(
-        features::kAppShimNotificationAttribution));
+    CHECK(web_app::UseNotificationAttributionForWebAppShims());
   }
 }
 
diff --git a/chrome/browser/notifications/mac/notification_platform_bridge_mac.cc b/chrome/browser/notifications/mac/notification_platform_bridge_mac.cc
index 06f5683..e4fe04d 100644
--- a/chrome/browser/notifications/mac/notification_platform_bridge_mac.cc
+++ b/chrome/browser/notifications/mac/notification_platform_bridge_mac.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/notifications/platform_notification_service_impl.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/os_integration/mac/app_shim_registry.h"
+#include "chrome/browser/web_applications/os_integration/mac/web_app_shortcut_mac.h"
 #include "chrome/browser/web_applications/proto/web_app_install_state.pb.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
@@ -76,7 +77,7 @@
     std::unique_ptr<NotificationCommon::Metadata> metadata) {
   NotificationDispatcherMac* dispatcher = nullptr;
 
-  if (base::FeatureList::IsEnabled(features::kAppShimNotificationAttribution) &&
+  if (web_app::UseNotificationAttributionForWebAppShims() &&
       notification.notifier_id().web_app_id.has_value() &&
       AppShimRegistry::Get()->IsAppInstalledInProfile(
           *notification.notifier_id().web_app_id, profile->GetPath())) {
@@ -124,7 +125,7 @@
 void NotificationPlatformBridgeMac::GetDisplayed(
     Profile* profile,
     GetDisplayedNotificationsCallback callback) const {
-  if (base::FeatureList::IsEnabled(features::kAppShimNotificationAttribution)) {
+  if (web_app::UseNotificationAttributionForWebAppShims()) {
     // We can't get all displayed notifications for all origins, since that
     // would involve starting up any app shim that might currently show
     // notifications. Fortunately we don't really need to implement this method
@@ -174,7 +175,7 @@
   bool incognito = profile->IsOffTheRecord();
 
   std::vector<webapps::AppId> web_app_ids;
-  if (base::FeatureList::IsEnabled(features::kAppShimNotificationAttribution)) {
+  if (web_app::UseNotificationAttributionForWebAppShims()) {
     if (auto* web_app_provider =
             web_app::WebAppProvider::GetForWebApps(profile)) {
       web_app::WebAppRegistrar& registrar =
diff --git a/chrome/browser/notifications/mac/notification_platform_bridge_mac_unittest.cc b/chrome/browser/notifications/mac/notification_platform_bridge_mac_unittest.cc
index 5298676..17142ec 100644
--- a/chrome/browser/notifications/mac/notification_platform_bridge_mac_unittest.cc
+++ b/chrome/browser/notifications/mac/notification_platform_bridge_mac_unittest.cc
@@ -561,6 +561,13 @@
 class NotificationPlatformBridgeMacTestWithNotificationAttribution
     : public NotificationPlatformBridgeMacTest {
  public:
+  NotificationPlatformBridgeMacTestWithNotificationAttribution() {
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{features::kAppShimNotificationAttribution,
+                              features::kUseAdHocSigningForWebAppShims},
+        /*disabled_features=*/{});
+  }
+
   void SetUp() override {
     NotificationPlatformBridgeMacTest::SetUp();
     web_app::test::AwaitStartWebAppProviderAndSubsystems(profile());
@@ -576,8 +583,7 @@
   webapps::AppId installed_app_id_;
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_{
-      features::kAppShimNotificationAttribution};
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 TEST_F(NotificationPlatformBridgeMacTestWithNotificationAttribution,
diff --git a/chrome/browser/on_device_translation/language_pack_util.cc b/chrome/browser/on_device_translation/language_pack_util.cc
index 259fd5c..acbed61 100644
--- a/chrome/browser/on_device_translation/language_pack_util.cc
+++ b/chrome/browser/on_device_translation/language_pack_util.cc
@@ -102,24 +102,6 @@
 
 }  // namespace
 
-bool IsPopularLanguage(SupportedLanguage supported_language) {
-  return supported_language == SupportedLanguage::kEn ||
-         supported_language == SupportedLanguage::kZh ||
-         supported_language == SupportedLanguage::kZhHant ||
-         supported_language == SupportedLanguage::kJa ||
-         supported_language == SupportedLanguage::kPt ||
-         supported_language == SupportedLanguage::kRu ||
-         supported_language == SupportedLanguage::kEs ||
-         supported_language == SupportedLanguage::kTr ||
-         supported_language == SupportedLanguage::kHi ||
-         supported_language == SupportedLanguage::kVi ||
-         supported_language == SupportedLanguage::kBn ||
-         supported_language == SupportedLanguage::kKn ||
-         supported_language == SupportedLanguage::kTa ||
-         supported_language == SupportedLanguage::kTe ||
-         supported_language == SupportedLanguage::kMr;
-}
-
 // Converts a SupportedLanguage to a language code.
 std::string_view ToLanguageCode(SupportedLanguage supported_language) {
   return kSupportedLanguageCodeMap.at(supported_language);
diff --git a/chrome/browser/on_device_translation/language_pack_util.h b/chrome/browser/on_device_translation/language_pack_util.h
index e6c17768..8eef7ef 100644
--- a/chrome/browser/on_device_translation/language_pack_util.h
+++ b/chrome/browser/on_device_translation/language_pack_util.h
@@ -85,10 +85,6 @@
 std::optional<SupportedLanguage> ToSupportedLanguage(
     std::string_view language_code);
 
-// Returns whether the language is in the top 12 by number of native speakers.
-// https://en.wikipedia.org/wiki/List_of_languages_by_number_of_native_speakers#Top_languages_by_population
-bool IsPopularLanguage(SupportedLanguage supported_language);
-
 // The key for language pack components.
 enum class LanguagePackKey {
   kEn_Es = 0,
diff --git a/chrome/browser/on_device_translation/language_pack_util_unittest.cc b/chrome/browser/on_device_translation/language_pack_util_unittest.cc
index a1fc636d..43ab8c2 100644
--- a/chrome/browser/on_device_translation/language_pack_util_unittest.cc
+++ b/chrome/browser/on_device_translation/language_pack_util_unittest.cc
@@ -106,49 +106,6 @@
   EXPECT_EQ(ToSupportedLanguage(""), std::nullopt);
 }
 
-TEST(LanguagePackUtilTest, IsPopularLanguage) {
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kEn));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kEn));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kEs));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kJa));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kAr));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kBn));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kDe));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kFr));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kHi));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kIt));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kKo));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kNl));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kPl));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kPt));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kRu));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kTh));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kTr));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kVi));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kZh));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kZhHant));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kBg));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kCs));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kDa));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kEl));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kFi));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kHr));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kHu));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kId));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kIw));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kLt));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kNo));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kRo));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kSk));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kSl));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kSv));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kUk));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kKn));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kTa));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kTe));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kMr));
-}
-
 TEST(LanguagePackUtilTest, GetLanguagePackComponentConfig) {
   // En to Es.
   EXPECT_EQ(GetLanguagePackComponentConfig(LanguagePackKey::kEn_Es).language1,
diff --git a/chrome/browser/on_device_translation/on_device_translation_browsertest.cc b/chrome/browser/on_device_translation/on_device_translation_browsertest.cc
index e07d1fe0..2a95d31 100644
--- a/chrome/browser/on_device_translation/on_device_translation_browsertest.cc
+++ b/chrome/browser/on_device_translation/on_device_translation_browsertest.cc
@@ -186,8 +186,6 @@
     case CanCreateTranslatorResult::kAfterDownloadTranslatorCreationRequired:
       return "downloadable";
     case CanCreateTranslatorResult::kNoNotSupportedLanguage:
-    case CanCreateTranslatorResult::kNoAcceptLanguagesCheckFailed:
-    case CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation:
     case CanCreateTranslatorResult::kNoServiceCrashed:
     case CanCreateTranslatorResult::kNoDisallowedByPolicy:
     case CanCreateTranslatorResult::kNoExceedsServiceCountLimitation:
@@ -362,6 +360,36 @@
     EXPECT_FALSE(console_observer.messages().empty());
   }
 
+  void ClearSiteContentSettings() {
+    content::BrowsingDataRemover* remover =
+        browser()->profile()->GetBrowsingDataRemover();
+    content::BrowsingDataRemoverCompletionObserver observer(remover);
+    remover->RemoveAndReply(
+        base::Time(), base::Time::Max(),
+        chrome_browsing_data_remover::DATA_TYPE_CONTENT_SETTINGS,
+        chrome_browsing_data_remover::ALL_ORIGIN_TYPES, &observer);
+    observer.BlockUntilCompletion();
+  }
+
+  content::RenderFrameHost* CreateIframe(Browser* target_browser = nullptr) {
+    EXPECT_EQ(EvalJsCatchingError(R"(
+      window._iframe = document.createElement('iframe');
+      document.body.appendChild(window._iframe);
+      return "OK";
+  )",
+                                  target_browser),
+              "OK");
+
+    return ChildFrameAt((target_browser ? target_browser : browser())
+                            ->tab_strip_model()
+                            ->GetActiveWebContents(),
+                        0);
+  }
+
+  bool RemoveIframe(Browser* target_browser = nullptr) {
+    return ExecJs("document.body.removeChild(window._iframe);");
+  }
+
  private:
   base::ScopedTempDir tmp_dir_;
   base::test::ScopedFeatureList scoped_feature_list_;
@@ -474,10 +502,13 @@
             "en to ja: hello");
 }
 
+// TODO(crbug.com/421947718): Disabled because there's a race between triggering
+// user activation and consuming it when calling `create` multiple times.
+//
 // Tests the behavior of multiple create() calls with different
 // source/target languages.
 IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
-                       CreateTranslatorInstallMultipleLanguagePacks) {
+                       DISABLED_CreateTranslatorInstallMultipleLanguagePacks) {
   MockComponentManager mock_component_manager(GetTempDir());
   NavigateToEmptyPage();
 
@@ -502,41 +533,45 @@
         run_loop_for_register_en_es_language_pack.Quit();
       }));
 
+  // Helper function to get the state of a promise at the moment the helper
+  // function is called.
+  EXPECT_TRUE(ExecJs(R"(
+    self.getPromiseState = async promise => {
+        const symbol = Symbol();
+        try {
+          const result = await Promise.race([promise, Promise.resolve(symbol)]);
+          return result == symbol ? "pending" : "fulfilled";
+        } catch (e) {
+          return "rejected";
+        }
+    }
+  )"));
+
   // Create create() multiple times.
   //   1. En => Ja.
   //   2. En => Es.
   //   3. En => Ja.
-  EXPECT_EQ(EvalJsCatchingError(R"(
-      window._testEnJaPromise1 = Translator.create({
+  EXPECT_TRUE(ExecJs(R"(
+      self.enJaPromise1 = Translator.create({
           sourceLanguage: 'en',
           targetLanguage: 'ja',
         });
-      window._testEnJaPromise1Resolved = false;
-      window._testEnJaPromise1.then(() => {
-        window._testEnJaPromise1Resolved = true;
-      });
-
-      window._testEnEsPromise = Translator.create({
+  )",
+                     browser(), content::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
+  EXPECT_TRUE(ExecJs(R"(
+      self.enEsPromise = Translator.create({
           sourceLanguage: 'en',
           targetLanguage: 'es',
         });
-      window._testEnEsPromiseResolved = false;
-      window._testEnEsPromise.then(() => {
-        window._testEnEsPromiseResolved = true;
-      });
-
-      window._testEnJaPromise2 = Translator.create({
+  )",
+                     browser(), content::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
+  EXPECT_TRUE(ExecJs(R"(
+      self.enJaPromise2 = Translator.create({
           sourceLanguage: 'en',
           targetLanguage: 'ja',
         });
-      window._testEnJaPromise2Resolved = false;
-      window._testEnJaPromise2.then(() => {
-        window._testEnJaPromise2Resolved = true;
-      });
-
-      return 'OK';
-  )"),
-            "OK");
+  )",
+                     browser(), content::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
 
   // Wait until RegisterTranslateKitComponentImpl() is called.
   run_loop_for_register_translate_kit.Run();
@@ -549,40 +584,40 @@
   mock_component_manager.InstallMockTranslateKitComponent();
 
   // All promises should not be resolved yet.
-  EXPECT_FALSE(EvalJs("window._testEnJaPromise1Resolved").ExtractBool());
-  EXPECT_FALSE(EvalJs("window._testEnJaPromise2Resolved").ExtractBool());
-  EXPECT_FALSE(EvalJs("window._testEnEsPromiseResolved").ExtractBool());
+  EXPECT_EQ(EvalJs("getPromiseState(enJaPromise1)").ExtractString(), "pending");
+  EXPECT_EQ(EvalJs("getPromiseState(enEsPromise)").ExtractString(), "pending");
+  EXPECT_EQ(EvalJs("getPromiseState(enJaPromise2)").ExtractString(), "pending");
 
   // Install the mock `en_ja` language pack.
   mock_component_manager.InstallMockLanguagePack(LanguagePackKey::kEn_Ja);
 
   // Translate to Japanese. Both `en_ja` promises should be resolved now.
+  EXPECT_EQ(EvalJsCatchingError(
+                "return await (await enJaPromise1).translate('hello');"),
+            "en to ja: hello");
   EXPECT_EQ(
-      EvalJsCatchingError(
-          "return await (await window._testEnJaPromise1).translate('hello');"),
-      "en to ja: hello");
-  EXPECT_EQ(
-      EvalJsCatchingError(
-          "return await (await window._testEnJaPromise2).translate('hi');"),
+      EvalJsCatchingError("return await (await enJaPromise2).translate('hi');"),
       "en to ja: hi");
 
   // The promise of `en_es` should not be resolved yet.
-  EXPECT_FALSE(EvalJs("window._testEnEsPromiseResolved").ExtractBool());
+  EXPECT_EQ(EvalJs("getPromiseState(enEsPromise)").ExtractString(), "pending");
 
   // Install the mock `en_es` language pack.
   mock_component_manager.InstallMockLanguagePack(LanguagePackKey::kEn_Es);
 
   // Translate to Spanish. The `en_es` promise should be resolved now.
-  EXPECT_EQ(
-      EvalJsCatchingError(
-          "return await (await window._testEnEsPromise).translate('hello');"),
-      "en to es: hello");
+  EXPECT_EQ(EvalJsCatchingError(
+                "return await (await self.enEsPromise).translate('hello');"),
+            "en to es: hello");
 }
 
+// TODO(crbug.com/421947718): Disabled because there's a race between triggering
+// user activation and consuming it when calling `create` multiple times.
+//
 // Tests the behavior of create() when the number of pending tasks
 // exceeds the limit.
 IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
-                       ExceedMaxPendingTaskCount) {
+                       DISABLED_ExceedMaxPendingTaskCount) {
   MockComponentManager mock_component_manager(GetTempDir());
   NavigateToEmptyPage();
 
@@ -601,6 +636,12 @@
         run_loop_for_register_language_pack.Quit();
       }));
 
+  // TODO(crbug.com/421947718): Each `Translator.create` call should be in it's
+  // own `EvalJs` call like
+  // `CreateTranslator_Delay_ForTranslatorCreatedDuringInitialTranslatorCreationWithDelay`,
+  // but since we're blocked on the race issue from crbug.com/421947718, this
+  // hasn't been updated yet.
+  //
   // Call create() kMaxPendingTaskCount times.
   EXPECT_EQ(EvalJsCatchingError(base::StringPrintf(R"(
       window._testPromises = [];
@@ -670,49 +711,6 @@
             "OK");
 }
 
-// Tests the behavior of TranslationAPILimitLanguagePackCount
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
-                       ExceedLanguagePackCount) {
-  MockComponentManager mock_component_manager(GetTempDir());
-  mock_component_manager.ExpectCallRegisterTranslateKitComponentAndInstall();
-  const base::span<const LanguagePackKey> language_packs =
-      base::span(kLanguagePackKeys);
-  NavigateToEmptyPage();
-
-  // Get the amount of packages we can install and assert that we have enough
-  // language packs for this test.
-  size_t installable_package_count =
-      on_device_translation::GetInstallablePackageCount(0);
-  ASSERT_GE(language_packs.size(), installable_package_count + 1);
-
-  // Add all the languages we're going to test to the selected languages so we
-  // don't fail PassAcceptLanguagesCheck.
-  SetSelectedLanguages(language_packs.first(installable_package_count + 1));
-
-  // Test that we can install all the language packs up to the language pack
-  // limitation.
-  mock_component_manager.ExpectCallRegisterLanguagePackComponentAndInstall(
-      language_packs.first(installable_package_count));
-  for (const auto& language_pack_key :
-       language_packs.first(installable_package_count)) {
-    TestSimpleTranslationWorks(browser(), language_pack_key);
-  }
-
-  // The language pack count is equal to the limitation. So no more language
-  // pack can be downloaded.
-  auto console_observer = CreateConsoleObserver(
-      "The Translator API language pack count exceeded the limitation. See "
-      "https://developer.chrome.com/docs/ai/"
-      "translator-api?#supported-languages for more details.");
-
-  TestCreateTranslator(browser(), language_packs.at(installable_package_count),
-                       "NotSupportedError: Unable to create translator for the "
-                       "given source and target language.");
-
-  // The console message should be logged.
-  WaitForConsoleObserver(*console_observer);
-}
-
 // Tests the behavior of the failure of translation.
 IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest, TranslationFailure) {
   MockComponentManager mock_component_manager(GetTempDir());
@@ -1091,71 +1089,10 @@
   ExpectUpdatesAre(expected_updates);
 }
 
-// Tests V1 behavior.
-class OnDeviceTranslationV1BrowserTest : public OnDeviceTranslationBrowserTest {
- public:
-  OnDeviceTranslationV1BrowserTest() {
-    scoped_feature_list_.InitAndEnableFeature(
-        blink::features::kTranslationAPIV1);
-  }
-  ~OnDeviceTranslationV1BrowserTest() override = default;
-
- protected:
-  void ClearSiteContentSettings() {
-    content::BrowsingDataRemover* remover =
-        browser()->profile()->GetBrowsingDataRemover();
-    content::BrowsingDataRemoverCompletionObserver observer(remover);
-    remover->RemoveAndReply(
-        base::Time(), base::Time::Max(),
-        chrome_browsing_data_remover::DATA_TYPE_CONTENT_SETTINGS,
-        chrome_browsing_data_remover::ALL_ORIGIN_TYPES, &observer);
-    observer.BlockUntilCompletion();
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-// The language model limit is not triggered when the V1 flag is enabled.
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationV1BrowserTest,
-                       NoLanguageModelLimitation) {
-  MockComponentManager mock_component_manager(GetTempDir());
-  mock_component_manager.ExpectCallRegisterTranslateKitComponentAndInstall();
-  const base::span<const LanguagePackKey> language_packs =
-      base::span(kLanguagePackKeys);
-  NavigateToEmptyPage();
-
-  // Expect that the number of available language packs is less than the
-  // installable language pack size, given there is no limitation in place.
-  size_t installable_package_count =
-      on_device_translation::GetInstallablePackageCount(0);
-  ASSERT_LE(language_packs.size() + 1, installable_package_count);
-
-  // Add all the languages we're going to test to the selected languages so we
-  // don't fail `PassAcceptLanguagesCheck`.
-  SetSelectedLanguages(language_packs);
-
-  // Test that we can install all of the possible language packs for
-  // translation.
-  mock_component_manager.ExpectCallRegisterLanguagePackComponentAndInstall(
-      language_packs);
-  for (const auto& language_pack_key : language_packs) {
-    TestSimpleTranslationWorks(browser(), language_pack_key);
-  }
-
-  // Get the last language pack key.
-  LanguagePackKey last_language_pack = *(language_packs.end() - 1);
-
-  // Confirm that the last language pack install succeeded.
-  TestTranslationAvailable(browser(), GetSourceLanguageCode(last_language_pack),
-                           GetTargetLanguageCode(last_language_pack),
-                           "available");
-}
-
 // Confirms that `Translator.availability()` is not masked for a translation
 // containing only English or the user's preferred languages.
 IN_PROC_BROWSER_TEST_F(
-    OnDeviceTranslationV1BrowserTest,
+    OnDeviceTranslationBrowserTest,
     TranslatorAvailabilityNotMasked_EnglishAndPreferredLanguages) {
   SetSelectedLanguages("fr");
   MockComponentManager mock_component_manager(GetTempDir());
@@ -1173,7 +1110,7 @@
 
 // Tests that `Translator.availability()` for a translation
 // containing a language outside of English + the user's preferred languages.
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationV1BrowserTest,
+IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
                        TranslatorAvailabilityMasked_ForNonPreferredLanguages) {
   SetSelectedLanguages("fr");
   MockComponentManager mock_component_manager(GetTempDir());
@@ -1209,7 +1146,7 @@
 // A delay is triggered for a "downloadable" translation containing a language
 // outside of English + preferred languages.
 IN_PROC_BROWSER_TEST_F(
-    OnDeviceTranslationV1BrowserTest,
+    OnDeviceTranslationBrowserTest,
     CreateTranslator_Delay_ForMaskedDownloadableTranslation) {
   // Setup Translate Kit Component and select Spanish as the preferred language.
   SetSelectedLanguages("en,es");
@@ -1234,12 +1171,15 @@
   TestSimpleTranslationWorks(browser(), "en", "ja");
 }
 
+// TODO(crbug.com/421947718): Disabled because there's a race between triggering
+// user activation and consuming it when calling `create` multiple times.
+//
 // A delay is triggered when a second translator for a given translation is
 // created during the delay time window of an initial translator's creation
 // (which is also expected to trigger a delay).
 IN_PROC_BROWSER_TEST_F(
-    OnDeviceTranslationV1BrowserTest,
-    CreateTranslator_Delay_ForTranslatorCreatedDuringInitialTranslatorCreationWithDelay) {
+    OnDeviceTranslationBrowserTest,
+    DISABLED_CreateTranslator_Delay_ForTranslatorCreatedDuringInitialTranslatorCreationWithDelay) {
   SetSelectedLanguages("es");
   MockComponentManager mock_component_manager(GetTempDir());
   mock_component_manager.InstallMockTranslateKitComponent();
@@ -1278,7 +1218,7 @@
 // `Translator.create` should still require user activation if the language pair
 // is readily available but the site hasn't created a Translator for the
 // language pair yet.
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationV1BrowserTest,
+IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
                        CreateRequiresUserActivationWhenDownloadedButMasked) {
   SetSelectedLanguages("es");
   MockComponentManager mock_component_manager(GetTempDir());
@@ -1301,7 +1241,7 @@
 
 // No delay is triggered for a "downloadable" translation between English +
 // preferred languages.
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationV1BrowserTest,
+IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
                        CreateTranslator_NoDelay_DownloadableTranslation) {
   SetSelectedLanguages("en,es");
   MockComponentManager mock_component_manager(GetTempDir());
@@ -1322,7 +1262,7 @@
 
 // No delay is triggered in attempt to create a translator for an unsupported
 // language.
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationV1BrowserTest,
+IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
                        CreateTranslator_NoDelay_UnsupportedLanguage) {
   SetSelectedLanguages("en,xx");
   MockComponentManager mock_component_manager(GetTempDir());
@@ -1481,16 +1421,17 @@
 
   NavigateToEmptyPage();
 
+  content::RenderFrameHost* iframe = CreateIframe();
+
   // Create a translator in an iframe.
-  EXPECT_EQ(EvalJsCatchingError(R"(
-      window._testIframe = document.createElement('iframe');
-      document.body.appendChild(window._testIframe);
-      window._testIframe.contentWindow.Translator.create({
+  EXPECT_EQ(content::EvalJs(iframe, R"(
+      Translator.create({
           sourceLanguage: 'en',
           targetLanguage: 'ja',
         });
-      return 'OK';
-    )"),
+      'OK';
+    )")
+                .ExtractString(),
             "OK");
   // Wait until RegisterTranslateKitComponentImpl() is called.
   run_loop_for_register_translate_kit.Run();
@@ -1498,7 +1439,7 @@
   run_loop_for_register_language_pack.Run();
 
   // Deletes the iframe after the browser process receives the request.
-  EXPECT_TRUE(ExecJs("document.body.removeChild(window._testIframe);"));
+  EXPECT_TRUE(RemoveIframe());
 
   // Install the mock TranslateKit component.
   mock_component_manager.InstallMockTranslateKitComponent();
@@ -1581,16 +1522,18 @@
   // Set the idle timeout to be 100 microseconds.
   service_controller->SetServiceIdleTimeoutForTesting(base::Microseconds(100));
 
+  content::RenderFrameHost* iframe = CreateIframe();
+
   // Test that Translator API on an iframe works.
-  EXPECT_EQ(EvalJsCatchingError(R"(
-      window._iframe = document.createElement('iframe');
-      document.body.appendChild(window._iframe);
-      const translator =
-          await window._iframe.contentWindow.Translator.create({
-            sourceLanguage: 'en',
-            targetLanguage: 'ja',
-          });
-      return await translator.translate('hello');
+  EXPECT_EQ(content::EvalJs(iframe, R"(
+     (async () => {
+        const translator =
+            await Translator.create({
+              sourceLanguage: 'en',
+              targetLanguage: 'ja',
+            });
+        return await translator.translate('hello');
+      })();
     )"),
             "en to ja: hello");
   // Check that the service is still running.
@@ -1624,7 +1567,15 @@
       .Times(0);
   mock_component_manager.InstallMockTranslateKitComponent();
   mock_component_manager.InstallMockLanguagePack(LanguagePackKey::kEn_Ja);
-  TestCanTranslateResult("en", "ja", CanCreateTranslatorResult::kReadily);
+
+  // Despite being ready, the availability will be masked since the site hasn't
+  // created a translator for this language pair yet.
+  // `kAfterDownloadTranslatorCreationRequired` is only ever returned in that
+  // situation, so receiving that value confirms that the package is readily
+  // available.
+  TestCanTranslateResult(
+      "en", "ja",
+      CanCreateTranslatorResult::kAfterDownloadTranslatorCreationRequired);
 }
 
 // Test the behavior of availability() when the language pack is not ready.
@@ -1679,118 +1630,6 @@
                          CanCreateTranslatorResult::kNoNotSupportedLanguage);
 }
 
-// Test the behavior of availability() when the language pack is not ready, and
-// the language pack count will exceed the limitation.
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
-                       CanTranslateNoExceedsLanguagePackCountLimitation) {
-  // This test case uses English as the source language and French as the target
-  // language. To avoid the failure of PassAcceptLanguagesCheck(), we set the
-  // SelectedLanguages to be English and French.
-  SetSelectedLanguages("en,fr");
-  MockComponentManager mock_component_manager(GetTempDir());
-  EXPECT_CALL(mock_component_manager, RegisterTranslateKitComponentImpl())
-      .Times(0);
-  mock_component_manager.InstallMockTranslateKitComponent();
-
-  // No language packs are installed yet.
-  size_t installed_package_count = 0;
-
-  // Get the amount of packages we can install.
-  size_t installable_package_count =
-      on_device_translation::GetInstallablePackageCount(
-          installed_package_count);
-  ASSERT_NE(installable_package_count, std::numeric_limits<size_t>::max());
-
-  for (const auto& language_pack_key : kLanguagePackKeys) {
-    mock_component_manager.InstallMockLanguagePack(language_pack_key);
-    installed_package_count++;
-
-    if (installed_package_count < installable_package_count) {
-      // The language pack count is less than the limitation.
-      TestCanTranslateResult(
-          "en", "fr",
-          CanCreateTranslatorResult::kAfterDownloadLanguagePackNotReady);
-    } else {
-      // The language pack count is equal to the limitation. So no more language
-      // pack can be downloaded.
-      TestCanTranslateResult(
-          "en", "fr",
-          CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation);
-      break;
-    }
-  }
-
-  ASSERT_EQ(installed_package_count, installable_package_count);
-}
-
-// Test the behavior of availability() when the language pack is not ready, and
-// the language pack count exceed the limitation after downloading two language
-// packs.
-IN_PROC_BROWSER_TEST_F(
-    OnDeviceTranslationBrowserTest,
-    CanTranslateNoExceedsLanguagePackCountLimitationTwoPackagesRequired) {
-  // This test case use Hindi and French as the source and target languages.
-  // To translate from Hindi to French, two language packs are required one for
-  // hi->en and one for en->fr.
-  SetSelectedLanguages("hi,fr");
-  MockComponentManager mock_component_manager(GetTempDir());
-  EXPECT_CALL(mock_component_manager, RegisterTranslateKitComponentImpl())
-      .Times(0);
-  mock_component_manager.InstallMockTranslateKitComponent();
-
-  // No language packs are installed yet.
-  size_t installed_package_count = 0;
-
-  // Get the amount of packages we can install.
-  size_t installable_package_count =
-      on_device_translation::GetInstallablePackageCount(
-          installed_package_count);
-  ASSERT_NE(installable_package_count, std::numeric_limits<size_t>::max());
-
-  for (const auto& language_pack_key : kLanguagePackKeys) {
-    mock_component_manager.InstallMockLanguagePack(language_pack_key);
-    installed_package_count++;
-
-    if (installed_package_count < installable_package_count - 1) {
-      // The language pack count is less than the limitation.
-      TestCanTranslateResult(
-          "hi", "fr",
-          CanCreateTranslatorResult::kAfterDownloadLanguagePackNotReady);
-    } else if (installed_package_count < installable_package_count) {
-      // The language pack count is less than the limitation. But if
-      // we download the required language packs, the language pack count will
-      // exceed the limitation. So availability() returns `no`.
-      TestCanTranslateResult(
-          "hi", "fr",
-          CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation);
-    } else {
-      // The language pack count is 3, which is equal to the limitation. So no
-      // more language pack can be downloaded.
-      TestCanTranslateResult(
-          "hi", "fr",
-          CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation);
-      break;
-    }
-  }
-
-  ASSERT_EQ(installed_package_count, installable_package_count);
-}
-
-// Test the behavior of availability() when PassAcceptLanguagesCheck() checks
-// fails.
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
-                       CanTranslateNoAcceptLanguagesCheckFailed) {
-  MockComponentManager mock_component_manager(GetTempDir());
-  EXPECT_CALL(mock_component_manager, RegisterTranslateKitComponentImpl())
-      .Times(0);
-  mock_component_manager.InstallMockTranslateKitComponent();
-  mock_component_manager.InstallMockLanguagePack(LanguagePackKey::kEn_Ko);
-  // Korean is not treated as a popular language. So if `ko` is not in the
-  // accept languages, PassAcceptLanguagesCheck() will return false.
-  TestCanTranslateResult(
-      "en", "ko", CanCreateTranslatorResult::kNoAcceptLanguagesCheckFailed);
-}
-
 // Test the behavior of `availability()` when the execution context is not
 // valid.
 IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
@@ -1828,20 +1667,6 @@
   TestTranslationAvailable(browser(), "en", "xx", "unavailable");
 }
 
-// Test the behavior of `availability()` when the `PassAcceptLanguagesCheck()`
-// check fails.
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
-                       Availability_Unavailable_AcceptLanguagesCheckFailed) {
-  MockComponentManager mock_component_manager(GetTempDir());
-  mock_component_manager.InstallMockTranslateKitComponent();
-  mock_component_manager.InstallMockLanguagePack(LanguagePackKey::kEn_Ko);
-  NavigateToEmptyPage();
-
-  // Korean is not treated as a popular language. So if `ko` is not in the
-  // accept languages, `PassAcceptLanguagesCheck()` will return false.
-  TestTranslationAvailable(browser(), "en", "ko", "unavailable");
-}
-
 // Test the behavior of `availability()` where the source language and the
 // target language are the same language.
 IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
@@ -1854,92 +1679,6 @@
   TestTranslationAvailable(browser(), "en", "en", "unavailable");
 }
 
-// Test the behavior of `availability()` when the language pack is not ready,
-// and the language pack count will exceed the imitation.
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
-                       Availability_No_ExceedsLangPackCountLimitation) {
-  // This test case uses English as the source language and French as the target
-  // language. To avoid the failure of `PassAcceptLanguagesCheck()`, we set the
-  // preferred languages to English and French.
-  SetSelectedLanguages("en,fr");
-  MockComponentManager mock_component_manager(GetTempDir());
-  NavigateToEmptyPage();
-  mock_component_manager.InstallMockTranslateKitComponent();
-
-  // No language packs are installed yet.
-  size_t installed_package_count = 0;
-
-  // Get the amount of packages we can install.
-  size_t installable_package_count =
-      on_device_translation::GetInstallablePackageCount(
-          installed_package_count);
-  ASSERT_NE(installable_package_count, std::numeric_limits<size_t>::max());
-
-  for (const auto& language_pack_key : kLanguagePackKeys) {
-    mock_component_manager.InstallMockLanguagePack(language_pack_key);
-    installed_package_count++;
-
-    if (installed_package_count < installable_package_count) {
-      // The language pack count is less than the limitation.
-      TestTranslationAvailable(browser(), "en", "fr", "downloadable");
-    } else {
-      // The language pack count is equal to the limitation. So no more language
-      // pack can be downloaded.
-      TestTranslationAvailable(browser(), "en", "fr", "unavailable");
-      break;
-    }
-  }
-
-  ASSERT_EQ(installed_package_count, installable_package_count);
-}
-
-// Test the behavior of `availability()` when the language pack is not ready,
-// and the language pack count exceeds the limitation after downloading two
-// more language packs.
-IN_PROC_BROWSER_TEST_F(
-    OnDeviceTranslationBrowserTest,
-    Availability_Unavailable_ExceedsLangPackCountLimitationTwoPackagesRequired) {
-  // This test case uses Hindi and French as the source and target languages.
-  // To translate from Hindi to French, two language packs are required: one for
-  // the hi->en translation and one for the en->fr translation.
-  SetSelectedLanguages("hi,fr");
-  MockComponentManager mock_component_manager(GetTempDir());
-  NavigateToEmptyPage();
-  mock_component_manager.InstallMockTranslateKitComponent();
-
-  // No language packs are installed yet.
-  size_t installed_package_count = 0;
-
-  // Get the amount of packages we can install.
-  size_t installable_package_count =
-      on_device_translation::GetInstallablePackageCount(
-          installed_package_count);
-  ASSERT_NE(installable_package_count, std::numeric_limits<size_t>::max());
-
-  for (const auto& language_pack_key : kLanguagePackKeys) {
-    mock_component_manager.InstallMockLanguagePack(language_pack_key);
-    installed_package_count++;
-
-    if (installed_package_count < installable_package_count - 1) {
-      // The language pack count is less than the limitation.
-      TestTranslationAvailable(browser(), "hi", "fr", "downloadable");
-    } else if (installed_package_count < installable_package_count) {
-      // The language pack count is less than the limitation.
-      // If we download the required language packs, the language pack count
-      // will exceed the limitation. As a result, `availability()` is
-      // 'unavailable'.
-      TestTranslationAvailable(browser(), "hi", "fr", "unavailable");
-    } else {
-      // The language pack count is 3, which is equal to the limitation. As a
-      // result, no more language packs can be downloaded.
-      TestTranslationAvailable(browser(), "hi", "fr", "unavailable");
-      break;
-    }
-  }
-
-  ASSERT_EQ(installed_package_count, installable_package_count);
-}
-
 // Test the behavior of `availability()` when both the library and the language
 // packs are not ready.
 IN_PROC_BROWSER_TEST_F(
@@ -2085,7 +1824,14 @@
       .Times(0);
   mock_component_manager.InstallMockTranslateKitComponent();
   mock_component_manager.InstallMockLanguagePack(LanguagePackKey::kEn_Ko);
-  TestCanTranslateResult("en", "ko", CanCreateTranslatorResult::kReadily);
+  // Despite being ready, the availability will be masked since the site hasn't
+  // created a translator for this language pair yet.
+  // `kAfterDownloadTranslatorCreationRequired` is only ever returned in that
+  // situation, so receiving that value confirms that the package is readily
+  // available.
+  TestCanTranslateResult(
+      "en", "ko",
+      CanCreateTranslatorResult::kAfterDownloadTranslatorCreationRequired);
 }
 
 // Test the behavior of Translator API in a cross origin iframe.
@@ -2127,14 +1873,19 @@
   }
 
   // Adds an iframe to the test page and optionally sets its permission policy.
-  void AddIframe(size_t index,
-                 Browser* target_browser,
-                 bool permission_policy_enabled) {
+  content::RenderFrameHost* AddIframe(size_t index,
+                                      Browser* target_browser,
+                                      bool permission_policy_enabled) {
     EXPECT_EQ(EvalJsCatchingError(JsReplace("return addIframe($1, $2);",
                                             CreateCrossOriginIframeUrl(index),
                                             permission_policy_enabled),
                                   target_browser),
               "loaded");
+
+    return ChildFrameAt((target_browser ? target_browser : browser())
+                            ->tab_strip_model()
+                            ->GetActiveWebContents(),
+                        index);
   }
 
   // Removes the iframe and waits for the service deletion.
@@ -2153,7 +1904,7 @@
 
   // Creates a translator and translates in the iframe. Returns successful
   // translation or the error message.
-  std::string CheckTranslateInIframe(size_t index, Browser* target_browser) {
+  std::string CheckTranslateInIframe(content::RenderFrameHost* iframe) {
     const std::string_view translateTestScript = R"(
         (async () => {
           try {
@@ -2169,14 +1920,11 @@
           }
         })()
       )";
-    return EvalJsCatchingError(
-        JsReplace("return evalInIframe($1, $2);",
-                  CreateCrossOriginIframeUrl(index), translateTestScript),
-        target_browser);
+    return content::EvalJs(iframe, translateTestScript).ExtractString();
   }
 
   // Checks the result of availability() in the iframe.
-  std::string TryCanTranslateInIframe(size_t index, Browser* target_browser) {
+  std::string TryCanTranslateInIframe(content::RenderFrameHost* iframe) {
     const std::string_view translateTestScript = R"(
       (async () => {
         try {
@@ -2189,10 +1937,7 @@
         }
       })()
     )";
-    return EvalJsCatchingError(
-        JsReplace("return evalInIframe($1, $2);",
-                  CreateCrossOriginIframeUrl(index), translateTestScript),
-        target_browser);
+    return content::EvalJs(iframe, translateTestScript).ExtractString();
   }
 
  private:
@@ -2268,11 +2013,12 @@
       .Times(0);
 
   NavigateToTestPage(browser());
-  AddIframe(0, browser(), /*enable_permission_policy=*/false);
+  content::RenderFrameHost* iframe =
+      AddIframe(0, browser(), /*enable_permission_policy=*/false);
 
   // Translation is not available in cross-origin iframes without permission
   // policy.
-  EXPECT_EQ(CheckTranslateInIframe(0, browser()), "NotAllowedError");
+  EXPECT_EQ(CheckTranslateInIframe(iframe), "NotAllowedError");
 }
 
 // Tests the behavior of the Translation API in a cross origin iframe when the
@@ -2289,25 +2035,27 @@
   // Until the service count exceeds the limit, the translator can be created,
   // and the translation is successful.
   for (; i < kTranslationAPIMaxServiceCount.Get(); i++) {
-    AddIframe(i, browser(), /*enable_permission_policy=*/true);
-    EXPECT_EQ(CheckTranslateInIframe(i, browser()), "en to ja: hello");
-    EXPECT_EQ(TryCanTranslateInIframe(i, browser()), "available");
+    content::RenderFrameHost* iframe =
+        AddIframe(i, browser(), /*enable_permission_policy=*/true);
+    EXPECT_EQ(CheckTranslateInIframe(iframe), "en to ja: hello");
+    EXPECT_EQ(TryCanTranslateInIframe(iframe), "available");
   }
 
   // When the service count exceeds the limit, the translator cannot be created,
   // even when the permission policy is still enabled.
-  AddIframe(i, browser(), /*enable_permission_policy=*/true);
+  content::RenderFrameHost* iframe =
+      AddIframe(i, browser(), /*enable_permission_policy=*/true);
   auto console_observer = CreateConsoleObserver(
       "The translation service count exceeded the limitation.");
-  EXPECT_EQ(CheckTranslateInIframe(i, browser()), "NotSupportedError");
+  EXPECT_EQ(CheckTranslateInIframe(iframe), "NotSupportedError");
   WaitForConsoleObserver(*console_observer);
-  EXPECT_EQ(TryCanTranslateInIframe(i, browser()), "unavailable");
+  EXPECT_EQ(TryCanTranslateInIframe(iframe), "unavailable");
 
   // When the service count is back to under the limit, the translator can be
   // created again.
   RemoveIframeAndWaitForServiceDeletion(0, browser());
-  EXPECT_EQ(CheckTranslateInIframe(i, browser()), "en to ja: hello");
-  EXPECT_EQ(TryCanTranslateInIframe(i, browser()), "available");
+  EXPECT_EQ(CheckTranslateInIframe(iframe), "en to ja: hello");
+  EXPECT_EQ(TryCanTranslateInIframe(iframe), "available");
 }
 
 // Tests the behavior of the Translation API in a cross origin iframe using the
@@ -2322,11 +2070,13 @@
   Browser* incognito_browser = CreateIncognitoBrowser();
 
   NavigateToTestPage(incognito_browser);
-  AddIframe(0, incognito_browser, /*enable_permission_policy=*/true);
-  EXPECT_EQ(CheckTranslateInIframe(0, incognito_browser), "en to ja: hello");
+  content::RenderFrameHost* iframe0 =
+      AddIframe(0, incognito_browser, /*enable_permission_policy=*/true);
+  EXPECT_EQ(CheckTranslateInIframe(iframe0), "en to ja: hello");
 
-  AddIframe(1, incognito_browser, /*enable_permission_policy=*/false);
-  EXPECT_EQ(CheckTranslateInIframe(1, incognito_browser), "NotAllowedError");
+  content::RenderFrameHost* iframe1 =
+      AddIframe(1, incognito_browser, /*enable_permission_policy=*/false);
+  EXPECT_EQ(CheckTranslateInIframe(iframe1), "NotAllowedError");
 }
 
 // Tests the behavior of the Translation API in a cross origin iframe using the
@@ -2341,8 +2091,9 @@
   Browser* guest_browser = CreateGuestBrowser();
 
   NavigateToTestPage(guest_browser);
-  AddIframe(0, guest_browser, /*enable_permission_policy=*/true);
-  EXPECT_EQ(CheckTranslateInIframe(0, guest_browser), "en to ja: hello");
+  content::RenderFrameHost* iframe =
+      AddIframe(0, guest_browser, /*enable_permission_policy=*/true);
+  EXPECT_EQ(CheckTranslateInIframe(iframe), "en to ja: hello");
 }
 
 // Tests the behavior of the Translation API in a cross origin iframe using
@@ -2377,32 +2128,39 @@
   // translation is successful.
   for (size_t i = 0; i < kTranslationAPIMaxServiceCount.Get(); i++) {
     for (auto* target_browser : browsers) {
-      AddIframe(i, target_browser, /*enable_permission_policy=*/true);
-      EXPECT_EQ(CheckTranslateInIframe(i, target_browser), "en to ja: hello");
+      content::RenderFrameHost* iframe =
+          AddIframe(i, target_browser, /*enable_permission_policy=*/true);
+      EXPECT_EQ(CheckTranslateInIframe(iframe), "en to ja: hello");
     }
   }
 
   const size_t limit_count = kTranslationAPIMaxServiceCount.Get();
 
+  std::vector<content::RenderFrameHost*> iframes;
+
   // When the service count per profile exceeds the limit, the translator
   // cannot be created.
   for (auto* target_browser : browsers) {
-    AddIframe(limit_count, target_browser, /*enable_permission_policy=*/true);
+    content::RenderFrameHost* iframe = AddIframe(
+        limit_count, target_browser, /*enable_permission_policy=*/true);
+    iframes.push_back(iframe);
     auto console_observer = CreateConsoleObserver(
         "The translation service count exceeded the limitation.",
         target_browser);
-    EXPECT_EQ(CheckTranslateInIframe(limit_count, target_browser),
-              "NotSupportedError");
+    EXPECT_EQ(CheckTranslateInIframe(iframe), "NotSupportedError");
     // The console message should be logged.
     WaitForConsoleObserver(*console_observer);
   }
 
+  ASSERT_EQ(iframes.size(), browsers.size());
+
   // When the service count per profile is back to under the limit, the
   // translator can be created again.
-  for (auto* target_browser : browsers) {
+  for (size_t i = 0; i < browsers.size(); i++) {
+    Browser* target_browser = browsers[i];
+    content::RenderFrameHost* iframe = iframes[i];
     RemoveIframeAndWaitForServiceDeletion(0, target_browser);
-    EXPECT_EQ(CheckTranslateInIframe(limit_count, target_browser),
-              "en to ja: hello");
+    EXPECT_EQ(CheckTranslateInIframe(iframe), "en to ja: hello");
   }
 }
 
@@ -2437,21 +2195,23 @@
   // Until the service count exceeds the limit, the translator can be created,
   // and the translation is successful.
   for (; i < kTranslationAPIMaxServiceCount.Get(); i++) {
-    AddIframe(i, browser(), /*enable_permission_policy=*/true);
-    EXPECT_EQ(CheckTranslateInIframe(i, browser()), "en to ja: hello");
-    EXPECT_EQ(TryCanTranslateInIframe(i, browser()), "available");
+    content::RenderFrameHost* iframe =
+        AddIframe(i, browser(), /*enable_permission_policy=*/true);
+    EXPECT_EQ(CheckTranslateInIframe(iframe), "en to ja: hello");
+    EXPECT_EQ(TryCanTranslateInIframe(iframe), "available");
   }
 
   // When the service count exceeds the limit, the translator cannot be created.
-  AddIframe(i, browser(), /*enable_permission_policy=*/true);
-  EXPECT_EQ(CheckTranslateInIframe(i, browser()), "NotSupportedError");
-  EXPECT_EQ(TryCanTranslateInIframe(i, browser()), "unavailable");
+  content::RenderFrameHost* last_iframe =
+      AddIframe(i, browser(), /*enable_permission_policy=*/true);
+  EXPECT_EQ(CheckTranslateInIframe(last_iframe), "NotSupportedError");
+  EXPECT_EQ(TryCanTranslateInIframe(last_iframe), "unavailable");
 
   // When the service count is back to under the limit, the translator can be
   // created again.
   RemoveIframeAndWaitForServiceDeletion(0, browser());
-  EXPECT_EQ(CheckTranslateInIframe(i, browser()), "en to ja: hello");
-  EXPECT_EQ(TryCanTranslateInIframe(i, browser()), "available");
+  EXPECT_EQ(CheckTranslateInIframe(last_iframe), "en to ja: hello");
+  EXPECT_EQ(TryCanTranslateInIframe(last_iframe), "available");
 }
 
 // Tests the behavior of the Origin Trial token for the Translation API.
@@ -2687,7 +2447,15 @@
   mock_component_manager.InstallMockTranslateKitComponent();
   mock_component_manager.DoNotExpectCallRegisterLanguagePackComponent();
   NavigateToEmptyPage();
-  TestCanTranslateResult("en", "ja", CanCreateTranslatorResult::kReadily);
+
+  // Despite being ready, the availability will be masked since the site hasn't
+  // created a translator for this language pair yet.
+  // `kAfterDownloadTranslatorCreationRequired` is only ever returned in that
+  // situation, so receiving that value confirms that the package is readily
+  // available.
+  TestCanTranslateResult(
+      "en", "ja",
+      CanCreateTranslatorResult::kAfterDownloadTranslatorCreationRequired);
 }
 
 // Tests the behavior of availability() when the required language package
diff --git a/chrome/browser/on_device_translation/service_controller.cc b/chrome/browser/on_device_translation/service_controller.cc
index 5ffb9c0..bc48543 100644
--- a/chrome/browser/on_device_translation/service_controller.cc
+++ b/chrome/browser/on_device_translation/service_controller.cc
@@ -156,19 +156,6 @@
                                       to_be_registered_packs);
 
     if (!to_be_registered_packs.empty()) {
-      if (!base::FeatureList::IsEnabled(blink::features::kTranslationAPIV1) &&
-          (kTranslationAPILimitLanguagePackCount.Get() &&
-           to_be_registered_packs.size() >
-               GetInstallablePackageCount(
-                   ComponentManager::GetRegisteredLanguagePacks().size()))) {
-        RecordLanguagePairUma(
-            "Translate.OnDeviceTranslation.DownloadExceedLimit.LanguagePair",
-            source_lang, target_lang);
-        std::move(callback).Run(base::unexpected(
-            CreateTranslatorError::kExceedsLanguagePackCountLimitation));
-        return;
-      }
-
       for (const auto& language_pack : to_be_registered_packs) {
         RecordLanguagePairUma(
             "Translate.OnDeviceTranslation.Download.LanguagePair",
@@ -320,17 +307,6 @@
     return CanCreateTranslatorResult::kNoNotSupportedLanguage;
   }
 
-  if (!to_be_registered_packs.empty() &&
-      !base::FeatureList::IsEnabled(blink::features::kTranslationAPIV1) &&
-      kTranslationAPILimitLanguagePackCount.Get() &&
-      to_be_registered_packs.size() >
-          GetInstallablePackageCount(
-              ComponentManager::GetRegisteredLanguagePacks().size())) {
-    // The number of installed language packs will exceed the limitation if the
-    // new required language packs are installed.
-    return CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation;
-  }
-
   if (required_not_installed_packs.empty()) {
     // All required language packages are installed.
     if (ComponentManager::GetTranslateKitLibraryPath().empty()) {
diff --git a/chrome/browser/on_device_translation/translation_manager_impl.cc b/chrome/browser/on_device_translation/translation_manager_impl.cc
index 1c07542..e847883 100644
--- a/chrome/browser/on_device_translation/translation_manager_impl.cc
+++ b/chrome/browser/on_device_translation/translation_manager_impl.cc
@@ -293,15 +293,6 @@
     return;
   }
 
-  if (!PassAcceptLanguagesCheck(GetAcceptLanguages(browser_context()),
-                                source_language, target_language)) {
-    mojo::Remote(std::move(client))
-        ->OnResult(CreateTranslatorResult::NewError(
-                       CreateTranslatorError::kAcceptLanguagesCheckFailed),
-                   nullptr, nullptr);
-    return;
-  }
-
   if (options->observer_remote) {
     base::flat_set<std::string> component_ids = {
         component_updater::TranslateKitComponentInstallerPolicy::
@@ -325,8 +316,7 @@
                      weak_ptr_factory_.GetWeakPtr(), std::move(client),
                      source_language, target_language);
 
-  if (base::FeatureList::IsEnabled(blink::features::kTranslationAPIV1) &&
-      add_fake_download_delay) {
+  if (add_fake_download_delay) {
     base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
         FROM_HERE, std::move(create_translator), GetTranslatorDownloadDelay());
   } else {
@@ -374,14 +364,6 @@
   const std::vector<std::string_view> accept_languages =
       GetAcceptLanguages(browser_context());
 
-  // TODO(crbug.com/385173766): Remove once V1 is launched.
-  if (!PassAcceptLanguagesCheck(accept_languages, source_language,
-                                target_language)) {
-    std::move(callback).Run(
-        CanCreateTranslatorResult::kNoAcceptLanguagesCheckFailed);
-    return;
-  }
-
   bool are_source_and_target_accept_or_english =
       (IsInAcceptLanguage(accept_languages, source_language) ||
        l10n_util::GetLanguage(source_language) == "en") &&
@@ -389,7 +371,6 @@
        l10n_util::GetLanguage(target_language) == "en");
 
   bool mask_readily_result =
-      base::FeatureList::IsEnabled(blink::features::kTranslationAPIV1) &&
       !HasInitializedTranslator(source_language, target_language) &&
       !are_source_and_target_accept_or_english;
 
diff --git a/chrome/browser/on_device_translation/translation_manager_util.cc b/chrome/browser/on_device_translation/translation_manager_util.cc
index b5c6b6d..aa6018d 100644
--- a/chrome/browser/on_device_translation/translation_manager_util.cc
+++ b/chrome/browser/on_device_translation/translation_manager_util.cc
@@ -18,19 +18,6 @@
 
 namespace on_device_translation {
 
-namespace {
-
-bool IsSupportedPopularLanguage(const std::string& lang) {
-  const std::optional<SupportedLanguage> supported_lang =
-      ToSupportedLanguage(lang);
-  if (!supported_lang) {
-    return false;
-  }
-  return IsPopularLanguage(*supported_lang);
-}
-
-}  // namespace
-
 const std::vector<std::string_view> GetAcceptLanguages(
     content::BrowserContext* browser_context) {
   CHECK(browser_context);
@@ -61,34 +48,4 @@
       ->GetBoolean(prefs::kTranslatorAPIAllowed);
 }
 
-bool PassAcceptLanguagesCheck(
-    const std::vector<std::string_view>& accept_languages,
-    const std::string& source_lang,
-    const std::string& target_lang) {
-  if (base::FeatureList::IsEnabled(blink::features::kTranslationAPIV1) ||
-      !kTranslationAPIAcceptLanguagesCheck.Get()) {
-    return true;
-  }
-
-  // TODO(crbug.com/371899260): Implement better language code handling.
-
-  // One of the source or the destination language must be in the user's accept
-  // language.
-  const bool source_lang_is_in_accept_langs =
-      IsInAcceptLanguage(accept_languages, source_lang);
-  const bool target_lang_is_in_accept_langs =
-      IsInAcceptLanguage(accept_languages, target_lang);
-
-  // The other language must be a popular language.
-  if (!source_lang_is_in_accept_langs &&
-      !IsSupportedPopularLanguage(source_lang)) {
-    return false;
-  }
-  if (!target_lang_is_in_accept_langs &&
-      !IsSupportedPopularLanguage(target_lang)) {
-    return false;
-  }
-  return true;
-}
-
 }  // namespace on_device_translation
diff --git a/chrome/browser/on_device_translation/translation_manager_util.h b/chrome/browser/on_device_translation/translation_manager_util.h
index 67b24d2..09910fb 100644
--- a/chrome/browser/on_device_translation/translation_manager_util.h
+++ b/chrome/browser/on_device_translation/translation_manager_util.h
@@ -27,14 +27,6 @@
 // Determines if the Translator API is enabled.
 bool IsTranslatorAllowed(content::BrowserContext* browser_context);
 
-// When the `TranslationAPIAcceptLanguagesCheck` feature is enabled, the
-// Translation API will fail if neither the source nor destination language is
-// in Accept Languages. This is intended to mitigate privacy concerns.
-bool PassAcceptLanguagesCheck(
-    const std::vector<std::string_view>& accept_languages,
-    const std::string& source_lang,
-    const std::string& target_lang);
-
 // Implementation of LookupMatchingLocaleByBestFit
 // (https://tc39.es/ecma402/#sec-lookupmatchinglocalebybestfit) as
 // LookupMatchingLocaleByPrefix
diff --git a/chrome/browser/on_device_translation/translation_manager_util_unittest.cc b/chrome/browser/on_device_translation/translation_manager_util_unittest.cc
index 4033fc7..a5c767b 100644
--- a/chrome/browser/on_device_translation/translation_manager_util_unittest.cc
+++ b/chrome/browser/on_device_translation/translation_manager_util_unittest.cc
@@ -19,143 +19,6 @@
   ~TranslationManagerUtilTest() override = default;
 };
 
-TEST_F(TranslationManagerUtilTest, PassAcceptLanguagesCheck) {
-  // Source lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : true
-  // Target lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : true
-  EXPECT_TRUE(PassAcceptLanguagesCheck({"en", "es"}, "en", "es"));
-
-  // Source lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : true
-  // Target lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : false
-  EXPECT_TRUE(PassAcceptLanguagesCheck({"en", "fr"}, "en", "fr"));
-
-  // Source lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : true
-  // Target lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : true
-  EXPECT_TRUE(PassAcceptLanguagesCheck({"en", "es"}, "en", "zh"));
-
-  // Source lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : true
-  // Target lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : false
-  // Target is not in accept-languages, and not popular.
-  EXPECT_FALSE(PassAcceptLanguagesCheck({"en", "es"}, "en", "fr"));
-
-  // Source lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : false
-  // Target lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : true
-  EXPECT_TRUE(PassAcceptLanguagesCheck({"de", "es"}, "de", "es"));
-
-  // Source lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : false
-  // Target lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : false
-  EXPECT_TRUE(PassAcceptLanguagesCheck({"de", "fr"}, "de", "fr"));
-
-  // Source lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : false
-  // Target lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : true
-  EXPECT_TRUE(PassAcceptLanguagesCheck({"de", "es"}, "de", "zh"));
-
-  // Source lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : false
-  // Target lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : false
-  // Target is not in accept-languages, and not popular.
-  EXPECT_FALSE(PassAcceptLanguagesCheck({"de", "es"}, "de", "fr"));
-
-  // Source lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : true
-  // Target lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : true
-  EXPECT_TRUE(PassAcceptLanguagesCheck({"en", "es"}, "ja", "es"));
-
-  // Source lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : true
-  // Target lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : false
-  EXPECT_TRUE(PassAcceptLanguagesCheck({"en", "fr"}, "ja", "fr"));
-
-  // Source lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : true
-  // Target lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : true
-  EXPECT_TRUE(PassAcceptLanguagesCheck({"en", "es"}, "ja", "zh"));
-
-  // Source lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : true
-  // Target lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : false
-  // Target is not in accept-languages, and not popular.
-  EXPECT_FALSE(PassAcceptLanguagesCheck({"en", "es"}, "ja", "fr"));
-
-  // Source lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : false
-  // Target lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : true
-  // Source is not in accept-languages, and not popular.
-  EXPECT_FALSE(PassAcceptLanguagesCheck({"en", "es"}, "de", "es"));
-
-  // Source lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : false
-  // Target lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : false
-  // Source is not in accept-languages, and not popular.
-  EXPECT_FALSE(PassAcceptLanguagesCheck({"en", "fr"}, "de", "fr"));
-
-  // Source lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : false
-  // Target lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : true
-  // Source is not in accept-languages, and not popular.
-  EXPECT_FALSE(PassAcceptLanguagesCheck({"en", "es"}, "de", "zh"));
-
-  // Source lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : false
-  // Target lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : false
-  // Target and source are not in accept-languages, and not popular.
-  EXPECT_FALSE(PassAcceptLanguagesCheck({"en", "es"}, "de", "fr"));
-}
-
 TEST_F(TranslationManagerUtilTest,
        LookupMatchingLocaleByBestFitFindsSupportedLanguageTag) {
   std::vector<std::string> en_variations{
diff --git a/chrome/browser/optimization_guide/BUILD.gn b/chrome/browser/optimization_guide/BUILD.gn
index 1418b68..2734aab 100644
--- a/chrome/browser/optimization_guide/BUILD.gn
+++ b/chrome/browser/optimization_guide/BUILD.gn
@@ -30,6 +30,7 @@
     "//components/optimization_guide/core:prediction",
     "//components/optimization_guide/optimization_guide_internals/webui",
     "//components/optimization_guide/public/mojom",
+    "//components/services/unzip/content:content",
     "//components/signin/public/identity_manager",
     "//content/public/browser",
     "//mojo/public/cpp/bindings",
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
index 75c5a56..d3b1fbe8 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
@@ -73,6 +73,7 @@
 #include "components/optimization_guide/proto/hints.pb.h"
 #include "components/optimization_guide/proto/models.pb.h"
 #include "components/prefs/pref_service.h"
+#include "components/services/unzip/content/unzip_service.h"
 #include "components/user_prefs/user_prefs.h"
 #include "components/variations/service/variations_service.h"
 #include "components/variations/synthetic_trials.h"
@@ -359,7 +360,8 @@
           &OptimizationGuideKeyedService::ComponentUpdatesEnabledProvider,
           // It's safe to use |base::Unretained(this)| here because
           // |this| owns |prediction_manager_|.
-          base::Unretained(this)));
+          base::Unretained(this)),
+      base::BindRepeating(&unzip::LaunchUnzipper));
 
   InitializeModelExecution(profile);
 
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
index 5e409fc..e5ba641 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
@@ -3822,18 +3822,23 @@
   // entry to report at the time of killing the page.
   double lcp_time = GetLCPTimeFromEmittedLCPEntry(contents);
 
-  // Kill the page.
-  content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
-  NavigateParams params(
-      browser(), GURL(GetParam()),
-      ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
-                                ui::PAGE_TRANSITION_FROM_ADDRESS_BAR));
-  ui_test_utils::NavigateToURL(&params);
+  // Crash the page.
+  content::RenderProcessHostWatcher crash_observer(
+      RenderFrameHost()->GetProcess(),
+      content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+
+  browser()->OpenURL(
+      content::OpenURLParams(GURL(GetParam()), content::Referrer(),
+                             WindowOpenDisposition::CURRENT_TAB,
+                             ui::PAGE_TRANSITION_TYPED, false),
+      /*navigation_handle_callback=*/{});
+  crash_observer.Wait();
+
   // Page being crashed is only verifiable in these crashes.
   if (GetParam() == blink::kChromeUIKillURL ||
       GetParam() == blink::kChromeUICrashURL) {
-    EXPECT_TRUE(
-        browser()->tab_strip_model()->GetActiveWebContents()->IsCrashed());
+    EXPECT_TRUE(web_contents()->IsCrashed());
+    EXPECT_FALSE(crash_observer.did_exit_normally());
   }
 
   // Verify page load metric is recorded.
diff --git a/chrome/browser/password_manager/password_change_browsertest.cc b/chrome/browser/password_manager/password_change_browsertest.cc
index cf7d1a6..4b5f1e1b 100644
--- a/chrome/browser/password_manager/password_change_browsertest.cc
+++ b/chrome/browser/password_manager/password_change_browsertest.cc
@@ -794,7 +794,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(PasswordChangeBrowserTest,
-                       FailureBubbleNotDisplayedAutomatically) {
+                       FailureDialogDisplayedAutomatically) {
   SetPrivacyNoticeAcceptedPref();
   const GURL main_url = WebContents()->GetLastCommittedURL();
   EXPECT_CALL(*affiliation_service(), GetChangePasswordURL(main_url))
@@ -813,13 +813,12 @@
     return delegate->GetCurrentState() ==
            PasswordChangeDelegate::State::kPasswordChangeFailed;
   }));
-
-  // TODO(crbug.com/417388947): Check that dialog is displayed instead.
-  EXPECT_FALSE(prompt_observer.IsBubbleDisplayedAutomatically());
+  // Now bubble should automatically appear.
+  EXPECT_TRUE(prompt_observer.IsBubbleDisplayedAutomatically());
 }
 
 IN_PROC_BROWSER_TEST_F(PasswordChangeBrowserTest,
-                       LeakCheckBubbleNotDisplayedAutomatically) {
+                       LeakCheckBubbleDisplayedAutomatically) {
   const GURL main_url = WebContents()->GetLastCommittedURL();
   EXPECT_CALL(*affiliation_service(), GetChangePasswordURL(main_url))
       .WillOnce(testing::Return(embedded_test_server()->GetURL(
@@ -833,9 +832,8 @@
       password_change_service()->GetPasswordChangeDelegate(WebContents());
   EXPECT_EQ(delegate->GetCurrentState(),
             PasswordChangeDelegate::State::kOfferingPasswordChange);
-
-  // TODO(crbug.com/417388947): Check that dialog is displayed instead.
-  EXPECT_FALSE(prompt_observer.IsBubbleDisplayedAutomatically());
+  // Now bubble should automatically appear.
+  EXPECT_TRUE(prompt_observer.IsBubbleDisplayedAutomatically());
 }
 
 IN_PROC_BROWSER_TEST_F(PasswordChangeBrowserTest,
diff --git a/chrome/browser/password_manager/password_change_delegate_impl.cc b/chrome/browser/password_manager/password_change_delegate_impl.cc
index bb5246a7..420f9f66 100644
--- a/chrome/browser/password_manager/password_change_delegate_impl.cc
+++ b/chrome/browser/password_manager/password_change_delegate_impl.cc
@@ -18,10 +18,7 @@
 #include "chrome/browser/tab_contents/tab_util.h"
 #include "chrome/browser/ui/autofill/autofill_client_provider.h"
 #include "chrome/browser/ui/autofill/autofill_client_provider_factory.h"
-#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
-#include "chrome/browser/ui/passwords/password_change_ui_controller.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "components/autofill/core/browser/logging/log_manager.h"
 #include "components/autofill/core/common/form_data.h"
 #include "components/autofill/core/common/save_password_progress_logger.h"
@@ -34,7 +31,6 @@
 #include "components/password_manager/core/browser/password_form.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/prefs/pref_service.h"
-#include "components/tabs/public/tab_interface.h"
 #include "components/url_formatter/elide_url.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_controller.h"
@@ -42,6 +38,9 @@
 #include "content/public/common/referrer.h"
 #include "ui/base/window_open_disposition.h"
 #include "url/gurl.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "components/tabs/public/tab_interface.h"
 
 namespace {
 
@@ -141,10 +140,7 @@
       username_(std::move(username)),
       original_password_(std::move(password)),
       profile_(Profile::FromBrowserContext(originator->GetBrowserContext())),
-      originator_(originator->GetWeakPtr()),
-      ui_controller_(std::make_unique<PasswordChangeUIController>(
-          this,
-          originator->GetWeakPtr())) {
+      originator_(originator->GetWeakPtr()) {
   if (auto logger = GetLoggerIfAvailable(originator_)) {
     logger->LogMessage(
         BrowserSavePasswordProgressLogger::STRING_PASSWORD_CHANGE_STARTED);
@@ -322,20 +318,19 @@
   current_state_ = new_state;
   observers_.Notify(&PasswordChangeDelegate::Observer::OnStateChanged,
                     new_state);
-  ui_controller_->UpdateState(new_state);
 
   switch (current_state_) {
     case State::kWaitingForChangePasswordForm:
     case State::kChangingPassword:
-    case State::kOfferingPasswordChange:
-    case State::kPasswordChangeFailed:
       return;
     case State::kPasswordSuccessfullyChanged:
       NotifyPasswordChangeFinishedSuccessfully(originator_);
       // Fallthrough to trigger bubble display.
       [[fallthrough]];
     case State::kChangePasswordFormNotFound:
+    case State::kOfferingPasswordChange:
     case State::kWaitingForAgreement:
+    case State::kPasswordChangeFailed:
     case State::kOtpDetected:
       DisplayChangePasswordBubbleAutomatically(originator_);
       break;
diff --git a/chrome/browser/password_manager/password_change_delegate_impl.h b/chrome/browser/password_manager/password_change_delegate_impl.h
index 1faf838..b50ab6cc 100644
--- a/chrome/browser/password_manager/password_change_delegate_impl.h
+++ b/chrome/browser/password_manager/password_change_delegate_impl.h
@@ -27,7 +27,6 @@
 class ChangePasswordFormFillingSubmissionHelper;
 class ChangePasswordFormFinder;
 class ModelQualityLogsUploader;
-class PasswordChangeUIController;
 class Profile;
 
 // This class controls password change process including acceptance of privacy
@@ -120,9 +119,6 @@
 
   base::Time flow_start_time_;
 
-  // The controller for password change views.
-  std::unique_ptr<PasswordChangeUIController> ui_controller_;
-
   base::WeakPtrFactory<PasswordChangeDelegateImpl> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index bf53641..6221608 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -2387,6 +2387,9 @@
   { key::kAIModeSearchSuggestSettings,
     omnibox::kAIModeSearchSuggestSettings,
     base::Value::Type::INTEGER },
+  { key::kAIModeSettings,
+    omnibox::kAIModeSettings,
+    base::Value::Type::INTEGER },
 
 };
 // clang-format on
@@ -3370,6 +3373,10 @@
       key::kAIModeSearchSuggestSettings, omnibox::kAIModeSearchSuggestSettings,
       GenAiDefaultSettingsPolicyHandler::PolicyValueToPrefMap(
           {{0, 0}, {1, 0}, {2, 1}}));
+  gen_ai_default_policies.emplace_back(
+      key::kAIModeSettings, omnibox::kAIModeSettings,
+      GenAiDefaultSettingsPolicyHandler::PolicyValueToPrefMap(
+          {{0, 0}, {1, 0}, {2, 1}}));
   handlers->AddHandler(std::make_unique<GenAiDefaultSettingsPolicyHandler>(
       std::move(gen_ai_default_policies)));
 
diff --git a/chrome/browser/prefs/chrome_pref_service_unittest.cc b/chrome/browser/prefs/chrome_pref_service_unittest.cc
index bb736b8..1cf5801 100644
--- a/chrome/browser/prefs/chrome_pref_service_unittest.cc
+++ b/chrome/browser/prefs/chrome_pref_service_unittest.cc
@@ -13,6 +13,7 @@
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/test_renderer_host.h"
+#include "extensions/buildflags/buildflags.h"
 #include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 using blink::web_pref::WebPreferences;
@@ -77,11 +78,11 @@
 
   // These values have been overridden by the profile preferences.
   EXPECT_EQ("UTF-8", webkit_prefs.default_encoding);
-#if !BUILDFLAG(IS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS)
   EXPECT_EQ(20, webkit_prefs.default_font_size);
 #else
-  // This pref is not configurable on Android so the default of 16 is always
-  // used.
+  // This pref is not configurable on Android without extensions so the default
+  // of 16 is always used.
   EXPECT_EQ(16, webkit_prefs.default_font_size);
 #endif
   EXPECT_FALSE(webkit_prefs.text_areas_are_resizable);
diff --git a/chrome/browser/profiles/android/java/src/org/chromium/chrome/browser/profiles/Profile.java b/chrome/browser/profiles/android/java/src/org/chromium/chrome/browser/profiles/Profile.java
index 2ed62ff..22bf766 100644
--- a/chrome/browser/profiles/android/java/src/org/chromium/chrome/browser/profiles/Profile.java
+++ b/chrome/browser/profiles/android/java/src/org/chromium/chrome/browser/profiles/Profile.java
@@ -73,7 +73,7 @@
     }
 
     /**
-     * Returns the OffTheRecord profile with given OtrProfileId. If the profile does not exist and
+     * Returns the OffTheRecord profile with given OtrProfileiD. If the profile does not exist and
      * createIfNeeded is true, a new profile is created, otherwise returns null.
      *
      * @param profileId {@link OtrProfileId} object.
@@ -86,16 +86,6 @@
     }
 
     /**
-     * Returns the OffTheRecord profile with given OtrProfileId, creating a new profile if
-     * necessary.
-     */
-    public Profile getOrCreateOffTheRecordProfile(OtrProfileId profileId) {
-        Profile profile = getOffTheRecordProfile(profileId, /* createIfNeeded= */ true);
-        assert profile != null : "OTR Profile should have been created";
-        return profile;
-    }
-
-    /**
      * Returns the OffTheRecord profile for incognito tabs. If the profile does not exist and
      * createIfNeeded is true, a new profile is created, otherwise returns null.
      *
@@ -106,15 +96,6 @@
     }
 
     /**
-     * Returns the OffTheRecord profile for incognito tabs, creating a new profile if necessary.
-     */
-    public Profile getOrCreatePrimaryOtrProfile() {
-        Profile profile = getPrimaryOtrProfile(/* createIfNeeded= */ true);
-        assert profile != null : "Primary OTR Profile should have been created";
-        return profile;
-    }
-
-    /**
      * Returns the OffTheRecord profile id for OffTheRecord profiles, and null for regular profiles.
      */
     public @Nullable OtrProfileId getOtrProfileId() {
diff --git a/chrome/browser/profiles/profile_destroyer.cc b/chrome/browser/profiles/profile_destroyer.cc
index 0e769c1..5fbd3f3c 100644
--- a/chrome/browser/profiles/profile_destroyer.cc
+++ b/chrome/browser/profiles/profile_destroyer.cc
@@ -8,6 +8,7 @@
 #include <sstream>
 #include <utility>
 
+#include "base/debug/alias.h"
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/location.h"
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java
index c50841b..0b47b8d9 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java
@@ -15,15 +15,13 @@
 import android.os.Looper;
 import android.util.LruCache;
 import android.view.WindowManager;
+
 import androidx.annotation.IntDef;
 import androidx.annotation.VisibleForTesting;
+
 import com.google.common.collect.ImmutableMap;
 import com.google.common.hash.Hashing;
-import java.time.Duration;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
+
 import org.chromium.base.ActivityState;
 import org.chromium.base.ApplicationState;
 import org.chromium.base.ApplicationStatus;
@@ -94,6 +92,12 @@
 import org.chromium.ui.insets.InsetObserver;
 import org.chromium.url.GURL;
 
+import java.time.Duration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
 /**
  * The main entrypoint component for Read Aloud feature. It's responsible for checking its
  * availability and triggering playback. Only instantiate after native is initialized.
@@ -573,7 +577,7 @@
         ReadAloudFeatures.init();
         mActivity = activity;
         mProfileSupplier = profileSupplier;
-        new OneShotCallback<Profile>(mProfileSupplier, this::onProfileAvailable);
+        new OneShotCallback<>(mProfileSupplier, this::onProfileAvailable);
         mTabModel = tabModel;
         mIncognitoTabModel = incognitoTabModel;
         mBottomSheetController = bottomSheetController;
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java
index 85b7dd2..cb992c7e6 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java
@@ -264,7 +264,7 @@
                         });
         when(mHooksImpl.isEnabled()).thenReturn(true);
         when(mHooksImpl.getCompatibleLanguages())
-                .thenReturn(new HashSet<String>(Arrays.asList("en", "es", "fr", "ja")));
+                .thenReturn(new HashSet<>(Arrays.asList("en", "es", "fr", "ja")));
         initPlaybackHooks();
         ReadAloudController.setReadabilityHooks(mHooksImpl);
         ReadAloudController.setPlaybackHooks(mPlaybackHooks);
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudReadabilityHooks.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudReadabilityHooks.java
index 2666514..c02a360 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudReadabilityHooks.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudReadabilityHooks.java
@@ -116,6 +116,6 @@
      * @return a hashset of compatible languages with the voices.
      */
     default HashSet<String> getCompatibleLanguages() {
-        return new HashSet<String>();
+        return new HashSet<>();
     }
 }
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudReadabilityHooksUpstreamImpl.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudReadabilityHooksUpstreamImpl.java
index 6a76566..dd411017 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudReadabilityHooksUpstreamImpl.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudReadabilityHooksUpstreamImpl.java
@@ -30,6 +30,6 @@
 
     @Override
     public HashSet<String> getCompatibleLanguages() {
-        return new HashSet<String>();
+        return new HashSet<>();
     }
 }
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerCoordinator.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerCoordinator.java
index c4ea611..a33280cf 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerCoordinator.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerCoordinator.java
@@ -52,7 +52,7 @@
     }
 
     public PlayerCoordinator(Delegate delegate) {
-        mObserverList = new ObserverList<Observer>();
+        mObserverList = new ObserverList<>();
         PropertyModel model =
                 new PropertyModel.Builder(PlayerProperties.ALL_KEYS)
                         // TODO Set voice and highlighting from settings when needed.
@@ -87,7 +87,7 @@
             PlayerMediator mediator,
             Delegate delegate,
             ExpandedPlayerCoordinator player) {
-        mObserverList = new ObserverList<Observer>();
+        mObserverList = new ObserverList<>();
         mMiniPlayer = miniPlayer;
         mMediator = mediator;
         mDelegate = delegate;
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerCoordinatorUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerCoordinatorUnitTest.java
index 3359a8ef..74f4d6a4 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerCoordinatorUnitTest.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerCoordinatorUnitTest.java
@@ -40,10 +40,8 @@
 import org.chromium.chrome.browser.readaloud.player.mini.MiniPlayerCoordinator;
 import org.chromium.chrome.browser.readaloud.player.mini.MiniPlayerLayout;
 import org.chromium.chrome.browser.readaloud.testing.MockPrefServiceHelper;
-import org.chromium.chrome.modules.readaloud.Feedback.FeedbackType;
 import org.chromium.chrome.modules.readaloud.Playback;
 import org.chromium.chrome.modules.readaloud.PlaybackArgs.PlaybackMode;
-import org.chromium.chrome.modules.readaloud.PlaybackArgs.PlaybackModeSelectionEnablementStatus;
 import org.chromium.chrome.modules.readaloud.PlaybackArgs.PlaybackVoice;
 import org.chromium.chrome.modules.readaloud.PlaybackListener;
 import org.chromium.chrome.modules.readaloud.Player;
@@ -115,13 +113,9 @@
         doReturn(new ObservableSupplierImpl<List<PlaybackVoice>>())
                 .when(mDelegate)
                 .getCurrentLanguageVoicesSupplier();
-        doReturn(new ObservableSupplierImpl<String>()).when(mDelegate).getVoiceIdSupplier();
-        doReturn(new ObservableSupplierImpl<PlaybackModeSelectionEnablementStatus>())
-                .when(mDelegate)
-                .getPlaybackModeSelectionEnabled();
-        doReturn(new ObservableSupplierImpl<FeedbackType>())
-                .when(mDelegate)
-                .getFeedbackTypeSupplier();
+        doReturn(new ObservableSupplierImpl<>()).when(mDelegate).getVoiceIdSupplier();
+        doReturn(new ObservableSupplierImpl<>()).when(mDelegate).getPlaybackModeSelectionEnabled();
+        doReturn(new ObservableSupplierImpl<>()).when(mDelegate).getFeedbackTypeSupplier();
         doReturn(mActivity).when(mDelegate).getActivity();
 
         mPlayerCoordinator = new PlayerCoordinator(mDelegate);
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/MenuItem.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/MenuItem.java
index 8991396..e2efc59c 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/MenuItem.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/MenuItem.java
@@ -95,7 +95,7 @@
                 });
         mLayout = layout;
         mLayoutSupplier = new ObservableSupplierImpl(mLayout);
-        new OneShotCallback<LinearLayout>(mLayoutSupplier, this::onLayoutInflated);
+        new OneShotCallback<>(mLayoutSupplier, this::onLayoutInflated);
         if (iconId != 0) {
             ImageView icon = layout.findViewById(R.id.icon);
             icon.setImageResource(iconId);
diff --git a/chrome/browser/recent_tabs/android/java/src/org/chromium/chrome/browser/recent_tabs/ForeignSessionHelper.java b/chrome/browser/recent_tabs/android/java/src/org/chromium/chrome/browser/recent_tabs/ForeignSessionHelper.java
index 59c0ca1..f0156fae 100644
--- a/chrome/browser/recent_tabs/android/java/src/org/chromium/chrome/browser/recent_tabs/ForeignSessionHelper.java
+++ b/chrome/browser/recent_tabs/android/java/src/org/chromium/chrome/browser/recent_tabs/ForeignSessionHelper.java
@@ -56,7 +56,7 @@
         public final String tag;
         public final String name;
         public final long modifiedTime;
-        public final List<ForeignSessionWindow> windows = new ArrayList<ForeignSessionWindow>();
+        public final List<ForeignSessionWindow> windows = new ArrayList<>();
         public final @FormFactor int formFactor;
 
         private ForeignSession(
@@ -86,7 +86,7 @@
     public static class ForeignSessionWindow {
         public final long timestamp;
         public final int sessionId;
-        public final List<ForeignSessionTab> tabs = new ArrayList<ForeignSessionTab>();
+        public final List<ForeignSessionTab> tabs = new ArrayList<>();
 
         private ForeignSessionWindow(long timestamp, int sessionId) {
             this(timestamp, sessionId, new ArrayList<>());
@@ -198,7 +198,7 @@
         if (!isTabSyncEnabled()) {
             return Collections.emptyList();
         }
-        List<ForeignSession> result = new ArrayList<ForeignSession>();
+        List<ForeignSession> result = new ArrayList<>();
         boolean received =
                 ForeignSessionHelperJni.get()
                         .getForeignSessions(mNativeForeignSessionHelper, result);
@@ -217,7 +217,7 @@
         if (!isTabSyncEnabled()) {
             return Collections.emptyList();
         }
-        List<ForeignSession> result = new ArrayList<ForeignSession>();
+        List<ForeignSession> result = new ArrayList<>();
         boolean received =
                 ForeignSessionHelperJni.get()
                         .getMobileAndTabletForeignSessions(mNativeForeignSessionHelper, result);
diff --git a/chrome/browser/recent_tabs/android/java/src/org/chromium/chrome/browser/recent_tabs/RestoreTabsFeatureHelperUnitTest.java b/chrome/browser/recent_tabs/android/java/src/org/chromium/chrome/browser/recent_tabs/RestoreTabsFeatureHelperUnitTest.java
index 7f18252..e6e7992 100644
--- a/chrome/browser/recent_tabs/android/java/src/org/chromium/chrome/browser/recent_tabs/RestoreTabsFeatureHelperUnitTest.java
+++ b/chrome/browser/recent_tabs/android/java/src/org/chromium/chrome/browser/recent_tabs/RestoreTabsFeatureHelperUnitTest.java
@@ -95,7 +95,7 @@
                 mGTSTabListModelSizeSupplier,
                 mScrollGTSToRestoredTabsCallback);
         verify(mForeignSessionHelperJniMock)
-                .getMobileAndTabletForeignSessions(1L, new ArrayList<ForeignSession>());
+                .getMobileAndTabletForeignSessions(1L, new ArrayList<>());
         verify(mForeignSessionHelperJniMock).destroy(1L);
     }
 
@@ -135,7 +135,7 @@
                             return true;
                         })
                 .when(mForeignSessionHelperJniMock)
-                .getMobileAndTabletForeignSessions(1L, new ArrayList<ForeignSession>());
+                .getMobileAndTabletForeignSessions(1L, new ArrayList<>());
         mHelper.maybeShowPromo(
                 mActivity,
                 mProfile,
@@ -169,7 +169,7 @@
                             return true;
                         })
                 .when(mForeignSessionHelperJniMock)
-                .getMobileAndTabletForeignSessions(eq(1L), eq(new ArrayList<ForeignSession>()));
+                .getMobileAndTabletForeignSessions(eq(1L), eq(new ArrayList<>()));
 
         mHelper.maybeShowPromo(
                 mActivity,
@@ -206,7 +206,7 @@
                             return true;
                         })
                 .when(mForeignSessionHelperJniMock)
-                .getMobileAndTabletForeignSessions(eq(1L), eq(new ArrayList<ForeignSession>()));
+                .getMobileAndTabletForeignSessions(eq(1L), eq(new ArrayList<>()));
 
         mHelper.maybeShowPromo(
                 mActivity,
diff --git a/chrome/browser/resources/ash/settings/os_about_page/about_page_browser_proxy.ts b/chrome/browser/resources/ash/settings/os_about_page/about_page_browser_proxy.ts
index f6d8e65..8720397 100644
--- a/chrome/browser/resources/ash/settings/os_about_page/about_page_browser_proxy.ts
+++ b/chrome/browser/resources/ash/settings/os_about_page/about_page_browser_proxy.ts
@@ -200,8 +200,6 @@
    */
   getChannelInfo(): Promise<ChannelInfo>;
 
-  canChangeFirmware(): Promise<boolean>;
-
   canChangeChannel(): Promise<boolean>;
 
   getVersionInfo(): Promise<VersionInfo>;
@@ -326,10 +324,6 @@
     return sendWithPromise('getChannelInfo');
   }
 
-  canChangeFirmware(): Promise<boolean> {
-    return sendWithPromise('canChangeFirmware');
-  }
-
   canChangeChannel(): Promise<boolean> {
     return sendWithPromise('canChangeChannel');
   }
diff --git a/chrome/browser/resources/ash/settings/os_about_page/os_about_page.html b/chrome/browser/resources/ash/settings/os_about_page/os_about_page.html
index 4055918..eb75373 100644
--- a/chrome/browser/resources/ash/settings/os_about_page/os_about_page.html
+++ b/chrome/browser/resources/ash/settings/os_about_page/os_about_page.html
@@ -253,7 +253,6 @@
             external
             deep-link-focus-id$="[[Setting.kDiagnostics]]">
         </cr-link-row>
-        <template is="dom-if" if="[[canChangeFirmware_]]">
         <cr-link-row class="hr" id="firmwareUpdates"
             start-icon="os-settings:about-firmware-updates"
             on-click="onFirmwareUpdatesClick_"
@@ -273,32 +272,6 @@
                 firmwareUpdateCount_)]]">
           </div>
         </cr-link-row>
-        </template>
-        <template is="dom-if" if="[[!canChangeFirmware_]]">
-        <cr-link-row class="hr" id="firmwareUpdates"
-            start-icon="os-settings:about-firmware-updates"
-            label="$i18n{aboutFirmwareUpdates}"
-            sub-label="[[getFirmwareSublabel_(firmwareUpdateCount_)]]"
-            external
-            using-slotted-label
-            disabled=true
-            deep-link-focus-id$="[[Setting.kFirmwareUpdates]]">
-          <iron-icon id="firmwareUpdateBadge"
-              icon$="[[getFirmwareUpdatesIcon_(firmwareUpdateCount_)]]"
-              hidden$="[[!shouldShowFirmwareUpdatesBadge_(
-                firmwareUpdateCount_)]]">
-          </iron-icon>
-          <div id="firmwareUpdateBadgeSeparator"
-              class="separator separator-firmware-updates-badge"
-              hidden$="[[!shouldShowFirmwareUpdatesBadge_(
-                firmwareUpdateCount_)]]">
-          </div>
-          <cr-policy-indicator
-            id="changeChannelPolicyIndicator"
-            indicator-type="[[getFirmwareDisabledIndicatorType_()]]">
-           </cr-policy-indicator>
-        </cr-link-row>
-        </template>
         <cr-link-row class="hr" id="detailedBuildInfoTrigger"
             start-icon="os-settings:about-additional-details"
             on-click="onDetailedBuildInfoClick_"
diff --git a/chrome/browser/resources/ash/settings/os_about_page/os_about_page.ts b/chrome/browser/resources/ash/settings/os_about_page/os_about_page.ts
index 519685a..bbc5370 100644
--- a/chrome/browser/resources/ash/settings/os_about_page/os_about_page.ts
+++ b/chrome/browser/resources/ash/settings/os_about_page/os_about_page.ts
@@ -23,12 +23,10 @@
 import './eol_offer_section.js';
 import './update_warning_dialog.js';
 import '../crostini_page/crostini_settings_card.js';
-import 'chrome://resources/ash/common/cr_elements/policy/cr_policy_indicator.js';
 
 import {LifetimeBrowserProxyImpl} from '/shared/settings/lifetime_browser_proxy.js';
 import type {CrButtonElement} from 'chrome://resources/ash/common/cr_elements/cr_button/cr_button.js';
 import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
-import {CrPolicyIndicatorType} from 'chrome://resources/ash/common/cr_elements/policy/cr_policy_indicator_mixin.js';
 import {WebUiListenerMixin} from 'chrome://resources/ash/common/cr_elements/web_ui_listener_mixin.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
@@ -94,11 +92,6 @@
         value: false,
       },
 
-      /**
-       * Whether users may initiate firmware updates
-       */
-      canChangeFirmware_: Boolean,
-
       currentUpdateStatusEvent_: {
         type: Object,
         value: {
@@ -295,7 +288,6 @@
   ]);
 
   private isDarkModeActive_: boolean;
-  private canChangeFirmware_: boolean;
   private currentUpdateStatusEvent_: UpdateStatusChangedEvent;
   private isManaged_: boolean;
   private deviceManager_: string;
@@ -423,12 +415,6 @@
         this.onExtendedUpdatesSettingChanged_.bind(this));
   }
 
-  private updateFirmwareInfo_(): void {
-    this.aboutBrowserProxy_.canChangeFirmware().then(canChangeFirmware => {
-      this.canChangeFirmware_ = canChangeFirmware;
-    });
-  }
-
   private onUpdateStatusChanged_(event: UpdateStatusChangedEvent): void {
     if (event.status === UpdateStatus.CHECKING) {
       this.hasCheckedForUpdates_ = true;
@@ -503,10 +489,6 @@
         this.currentUpdateStatusEvent_.status !== UpdateStatus.DISABLED;
   }
 
-  private getFirmwareDisabledIndicatorType_(): string {
-    return CrPolicyIndicatorType.DEVICE_POLICY;
-  }
-
   /**
    * Hide the button container if all buttons are hidden, otherwise the
    * container displays an unwanted border (see separator class).
@@ -773,7 +755,6 @@
   private onTpmFirmwareUpdateStatusChanged_(
       event: TpmFirmwareUpdateStatusChangedEvent): void {
     this.showTPMFirmwareUpdateLineItem_ = event.updateAvailable;
-    this.updateFirmwareInfo_();
   }
 
   private onTpmFirmwareUpdateClick_(): void {
@@ -806,13 +787,9 @@
   // </if>
 
   private getFirmwareSublabel_(): string|null {
-    if (!this.canChangeFirmware_) {
-      return this.i18n('aboutFirmwareUpdatesDisabledDescription');
-    }
-    if (this.firmwareUpdateCount_ > 0) {
-      return this.i18n('aboutFirmwareUpdateAvailableDescription');
-    }
-    return this.i18n('aboutFirmwareUpToDateDescription');
+    return this.firmwareUpdateCount_ > 0 ?
+        this.i18n('aboutFirmwareUpdateAvailableDescription') :
+        this.i18n('aboutFirmwareUpToDateDescription');
   }
 
   private computeShowExtendedUpdatesOption_(): boolean {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/mv2/background/event/desktop_automation_handler.ts b/chrome/browser/resources/chromeos/accessibility/chromevox/mv2/background/event/desktop_automation_handler.ts
index 46168b3..d9305de 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/mv2/background/event/desktop_automation_handler.ts
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/mv2/background/event/desktop_automation_handler.ts
@@ -57,9 +57,10 @@
   static MIN_ALERT_DELAY_MS = 50;
 
   /**
-   * URL for NTP (New tap page).
+   * URLs employing NTP (New tap page) searchbox.
    */
-  static NTP_URL = 'chrome://new-tab-page/';
+  static NTP_SEARCHBOX_URLS = new Set<string>(
+      ['chrome://new-tab-page/', 'chrome-untrusted://lens-overlay/']);
 
   /** The object that speaks changes to an editable text field. */
   private textEditHandler_: TextEditHandler|null = null;
@@ -565,7 +566,8 @@
       // crbug.com/346835896 lands in the stable.
       // TODO(crbug.com/314203187): Not null asserted, check that this is
       // correct.
-      if (evt.target.root!.url === DesktopAutomationHandler.NTP_URL &&
+      const urlString = evt.target.root?.url ?? '';
+      if (DesktopAutomationHandler.NTP_SEARCHBOX_URLS.has(urlString) &&
           evt.target.htmlTag === 'input' && !evt.intents?.length) {
         new Output()
             .withString(evt.target.value!)
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/background/event/desktop_automation_handler.ts b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/background/event/desktop_automation_handler.ts
index 46168b3..d9305de 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/background/event/desktop_automation_handler.ts
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/background/event/desktop_automation_handler.ts
@@ -57,9 +57,10 @@
   static MIN_ALERT_DELAY_MS = 50;
 
   /**
-   * URL for NTP (New tap page).
+   * URLs employing NTP (New tap page) searchbox.
    */
-  static NTP_URL = 'chrome://new-tab-page/';
+  static NTP_SEARCHBOX_URLS = new Set<string>(
+      ['chrome://new-tab-page/', 'chrome-untrusted://lens-overlay/']);
 
   /** The object that speaks changes to an editable text field. */
   private textEditHandler_: TextEditHandler|null = null;
@@ -565,7 +566,8 @@
       // crbug.com/346835896 lands in the stable.
       // TODO(crbug.com/314203187): Not null asserted, check that this is
       // correct.
-      if (evt.target.root!.url === DesktopAutomationHandler.NTP_URL &&
+      const urlString = evt.target.root?.url ?? '';
+      if (DesktopAutomationHandler.NTP_SEARCHBOX_URLS.has(urlString) &&
           evt.target.htmlTag === 'input' && !evt.intents?.length) {
         new Output()
             .withString(evt.target.value!)
diff --git a/chrome/browser/resources/pdf/elements/viewer_bottom_toolbar.html.ts b/chrome/browser/resources/pdf/elements/viewer_bottom_toolbar.html.ts
index 99688a5..b35d106 100644
--- a/chrome/browser/resources/pdf/elements/viewer_bottom_toolbar.html.ts
+++ b/chrome/browser/resources/pdf/elements/viewer_bottom_toolbar.html.ts
@@ -19,8 +19,8 @@
     <ink-brush-selector class="toolbar-icon" .currentType="${this.currentType}"
         @current-type-changed="${this.onCurrentTypeChanged}">
     </ink-brush-selector>
-    <span id="vertical-separator"></span>
     ${this.shouldShowBrushOptions_() ? html`
+      <span id="vertical-separator"></span>
       <viewer-bottom-toolbar-dropdown id="size" class="toolbar-icon"
           .buttonTitle="${this.getSizeTitle_()}">
         <cr-icon slot="icon" icon="${this.getSizeIcon_()}"></cr-icon>
diff --git a/chrome/browser/resources/pdf/elements/viewer_bottom_toolbar_dropdown.ts b/chrome/browser/resources/pdf/elements/viewer_bottom_toolbar_dropdown.ts
index c7091af..aec32ba 100644
--- a/chrome/browser/resources/pdf/elements/viewer_bottom_toolbar_dropdown.ts
+++ b/chrome/browser/resources/pdf/elements/viewer_bottom_toolbar_dropdown.ts
@@ -80,16 +80,16 @@
 
   // Exit out of the dropdown when focus shifts away from the dropdown menu.
   private handleFocusOut_(e: FocusEvent) {
-    if (!(e.relatedTarget instanceof HTMLElement)) {
+    // toggleDropdown_() should have already removed this event handler.
+    assert(this.showDropdown_);
+
+    // Skip if the focus target is the menu.
+    const nextElement = e.relatedTarget;
+    if (nextElement instanceof HTMLElement && nextElement !== this &&
+        this.contains(nextElement)) {
       return;
     }
 
-    // Skip if dropdown is not shown or if the focus target is the menu.
-    const nextElement = e.relatedTarget;
-    if (!this.showDropdown_ ||
-        (nextElement !== this && this.contains(nextElement))) {
-      return;
-    }
     this.toggleDropdown_();
   }
 
diff --git a/chrome/browser/resources/print_preview/ui/app.ts b/chrome/browser/resources/print_preview/ui/app.ts
index c9c4078..91832cc5 100644
--- a/chrome/browser/resources/print_preview/ui/app.ts
+++ b/chrome/browser/resources/print_preview/ui/app.ts
@@ -228,7 +228,15 @@
 
     if ((e.key === 'Enter' || e.key === 'NumpadEnter') &&
         this.state === State.READY) {
-      const activeElementTag = (e.composedPath()[0] as HTMLElement).tagName;
+      const activeElement = e.composedPath()[0] as HTMLElement | undefined;
+      // activeElement may be undefined if this is a forwarded key event from
+      // the plugin. Print Preview conventionally does not trigger a print for
+      // Enter when the plugin is focused
+      if (!activeElement) {
+        return;
+      }
+
+      const activeElementTag = activeElement.tagName;
       if (['CR-BUTTON', 'BUTTON', 'SELECT', 'A', 'CR-CHECKBOX'].includes(
               activeElementTag)) {
         return;
diff --git a/chrome/browser/resources/settings/safety_hub/unused_site_permissions_module.ts b/chrome/browser/resources/settings/safety_hub/unused_site_permissions_module.ts
index 590b9003..9b0fe95 100644
--- a/chrome/browser/resources/settings/safety_hub/unused_site_permissions_module.ts
+++ b/chrome/browser/resources/settings/safety_hub/unused_site_permissions_module.ts
@@ -20,7 +20,6 @@
 import {isUndoKeyboardEvent} from 'chrome://resources/js/util.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {loadTimeData} from '../i18n_setup.js';
 import type {MetricsBrowserProxy} from '../metrics_browser_proxy.js';
 import {MetricsBrowserProxyImpl, SafetyCheckUnusedSitePermissionsModuleInteractions} from '../metrics_browser_proxy.js';
 import {routes} from '../route.js';
@@ -130,14 +129,6 @@
         type: Boolean,
         computed: 'computeShouldShowCompletionInfo_(sites_.*)',
       },
-
-      // Indicates whether the abusive notification revocation feature
-      // is enabled.
-      safetyHubAbusiveNotificationRevocationEnabled_: {
-        type: Boolean,
-        value: () => loadTimeData.getBoolean(
-            'safetyHubAbusiveNotificationRevocationEnabled'),
-      },
     };
   }
 
@@ -147,7 +138,6 @@
   declare private toastText_: string|null;
   declare private sites_: UnusedSitePermissionsDisplay[]|null;
   declare private shouldShowCompletionInfo_: boolean;
-  declare private safetyHubAbusiveNotificationRevocationEnabled_: boolean;
   declare private lastUnusedSitePermissionsAllowedAgain_: UnusedSitePermissions|
       null;
   declare private lastUnusedSitePermissionsListAcknowledged_:
@@ -220,8 +210,7 @@
     // include notifications, then the revocation is for an abusive site.
     // In this case, we want to use the specific string for revoked abusive
     // notifications.
-    if (this.safetyHubAbusiveNotificationRevocationEnabled_ &&
-        permissionsI18n
+    if (permissionsI18n
             .map(permission => {
               return permission.toLowerCase();
             })
@@ -233,27 +222,19 @@
     switch (permissionsI18n.length) {
       case 1:
         return this.i18n(
-            this.safetyHubAbusiveNotificationRevocationEnabled_ ?
-                'safetyHubUnusedSitePermissionsRemovedOnePermissionLabel' :
-                'safetyCheckUnusedSitePermissionsRemovedOnePermissionLabel',
+            'safetyHubUnusedSitePermissionsRemovedOnePermissionLabel',
             ...permissionsI18n);
       case 2:
         return this.i18n(
-            this.safetyHubAbusiveNotificationRevocationEnabled_ ?
-                'safetyHubUnusedSitePermissionsRemovedTwoPermissionsLabel' :
-                'safetyCheckUnusedSitePermissionsRemovedTwoPermissionsLabel',
+            'safetyHubUnusedSitePermissionsRemovedTwoPermissionsLabel',
             ...permissionsI18n);
       case 3:
         return this.i18n(
-            this.safetyHubAbusiveNotificationRevocationEnabled_ ?
-                'safetyHubUnusedSitePermissionsRemovedThreePermissionsLabel' :
-                'safetyCheckUnusedSitePermissionsRemovedThreePermissionsLabel',
+            'safetyHubUnusedSitePermissionsRemovedThreePermissionsLabel',
             ...permissionsI18n);
       default:
         return this.i18n(
-            this.safetyHubAbusiveNotificationRevocationEnabled_ ?
-                'safetyHubUnusedSitePermissionsRemovedFourOrMorePermissionsLabel' :
-                'safetyCheckUnusedSitePermissionsRemovedFourOrMorePermissionsLabel',
+            'safetyHubUnusedSitePermissionsRemovedFourOrMorePermissionsLabel',
             permissionsI18n[0], permissionsI18n[1], permissionsI18n.length - 2);
     }
   }
@@ -391,10 +372,7 @@
             'safetyHubUnusedSitePermissionsPrimaryLabel', this.sites_.length);
     this.subheaderString_ =
         await PluralStringProxyImpl.getInstance().getPluralString(
-            this.safetyHubAbusiveNotificationRevocationEnabled_ ?
-                'safetyHubRevokedPermissionsSecondaryLabel' :
-                'safetyHubUnusedSitePermissionsSecondaryLabel',
-            this.sites_.length);
+            'safetyHubRevokedPermissionsSecondaryLabel', this.sites_.length);
     this.headerIconString_ = 'privacy:page-info';
   }
 
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_page.ts b/chrome/browser/resources/settings/site_settings_page/site_settings_page.ts
index 33415d5..381c64a 100644
--- a/chrome/browser/resources/settings/site_settings_page/site_settings_page.ts
+++ b/chrome/browser/resources/settings/site_settings_page/site_settings_page.ts
@@ -575,12 +575,6 @@
         value: false,
       },
 
-      safetyHubAbusiveNotificationRevocationEnabled_: {
-        type: Boolean,
-        value: () => loadTimeData.getBoolean(
-            'safetyHubAbusiveNotificationRevocationEnabled'),
-      },
-
       unusedSitePermissionsHeader_: String,
       unusedSitePermissionsSubheader_: String,
     };
@@ -605,7 +599,6 @@
   declare private contentExpanded_: boolean;
   declare private noRecentSitePermissions_: boolean;
   declare private showUnusedSitePermissions_: boolean;
-  declare private safetyHubAbusiveNotificationRevocationEnabled_: boolean;
   declare private unusedSitePermissionsHeader_: string;
   declare private unusedSitePermissionsSubheader_: string;
   private safetyHubBrowserProxy_: SafetyHubBrowserProxy =
@@ -665,10 +658,7 @@
     // TODO(crbug/342210522): Add test for this.
     this.unusedSitePermissionsSubheader_ =
         await PluralStringProxyImpl.getInstance().getPluralString(
-            this.safetyHubAbusiveNotificationRevocationEnabled_ ?
-                'safetyHubRevokedPermissionsSecondaryLabel' :
-                'safetyHubUnusedSitePermissionsSecondaryLabel',
-            permissions.length);
+            'safetyHubRevokedPermissionsSecondaryLabel', permissions.length);
   }
 
   /** @return Class for the all site settings link */
diff --git a/chrome/browser/resources/side_panel/read_anything/app.css b/chrome/browser/resources/side_panel/read_anything/app.css
index 0510e41..9db1d74 100644
--- a/chrome/browser/resources/side_panel/read_anything/app.css
+++ b/chrome/browser/resources/side_panel/read_anything/app.css
@@ -49,7 +49,7 @@
   padding-top: 2px;
   padding-left: var(--sp-card-block-padding);
   padding-right: var(--sp-card-block-padding);
-  padding-bottom: 0px;
+  padding-bottom: var(--sp-card-block-padding);
 }
 
 /* Provides vertical and horizontal scrolling */
diff --git a/chrome/browser/resources/side_panel/read_anything/language_menu.css b/chrome/browser/resources/side_panel/read_anything/language_menu.css
index dbeb6b0..a34333b 100644
--- a/chrome/browser/resources/side_panel/read_anything/language_menu.css
+++ b/chrome/browser/resources/side_panel/read_anything/language_menu.css
@@ -70,6 +70,12 @@
   padding-bottom: 16px;
 }
 
+.language-menu-footer {
+  padding-bottom: 8px;
+  padding-top: 0px;
+  border-top: 0px;
+}
+
 .search-field {
   padding: 0 var(--sp-body-padding) 0 var(--sp-body-padding);
 }
diff --git a/chrome/browser/resources/side_panel/read_anything/language_menu.html.ts b/chrome/browser/resources/side_panel/read_anything/language_menu.html.ts
index 4b8b61f..b80dbd6e 100644
--- a/chrome/browser/resources/side_panel/read_anything/language_menu.html.ts
+++ b/chrome/browser/resources/side_panel/read_anything/language_menu.html.ts
@@ -56,6 +56,8 @@
     <language-toast .numAvailableVoices="${this.availableVoices.length}">
     </language-toast>
   </div>
+  <div slot="footer" class="language-menu-footer">
+  </div>
 </cr-dialog>
 <!--_html_template_end_-->`;
   // clang-format on
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/file_analysis_request.cc b/chrome/browser/safe_browsing/cloud_content_scanning/file_analysis_request.cc
index 8e2b197..a9d994d 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/file_analysis_request.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/file_analysis_request.cc
@@ -149,7 +149,7 @@
 
   size_t max_file_size_bytes = BinaryUploadService::kMaxUploadSizeBytes;
   if (base::FeatureList::IsEnabled(
-          enterprise_connectors::kEnableNewUploadDownloadLimit)) {
+          enterprise_connectors::kEnableNewUploadSizeLimit)) {
     max_file_size_bytes =
         1024 * 1024 * enterprise_connectors::kMaxContentAnalysisFileSizeMB.Get();
   }
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/file_analysis_request_unittest.cc b/chrome/browser/safe_browsing/cloud_content_scanning/file_analysis_request_unittest.cc
index 42f7a97..7138c01a 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/file_analysis_request_unittest.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/file_analysis_request_unittest.cc
@@ -270,7 +270,7 @@
   base::test::ScopedFeatureList scoped_feature_list;
 
   scoped_feature_list.InitAndEnableFeatureWithParameters(
-      enterprise_connectors::kEnableNewUploadDownloadLimit,
+      enterprise_connectors::kEnableNewUploadSizeLimit,
       {{"max_file_size_mb", "100"}});
 
   base::test::TaskEnvironment task_environment;
diff --git a/chrome/browser/supervised_user/linux_mac_windows/supervised_user_extensions_metrics_delegate_impl.cc b/chrome/browser/supervised_user/linux_mac_windows/supervised_user_extensions_metrics_delegate_impl.cc
index b216329..e8df040 100644
--- a/chrome/browser/supervised_user/linux_mac_windows/supervised_user_extensions_metrics_delegate_impl.cc
+++ b/chrome/browser/supervised_user/linux_mac_windows/supervised_user_extensions_metrics_delegate_impl.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/supervised_user/linux_mac_windows/supervised_user_extensions_metrics_delegate_impl.h"
 
-#include "base/debug/stack_trace.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
 #include "chrome/browser/profiles/profile.h"
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabObscuringHandlerSupplier.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabObscuringHandlerSupplier.java
index 63afb2a..c178283 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabObscuringHandlerSupplier.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabObscuringHandlerSupplier.java
@@ -12,17 +12,17 @@
 import org.chromium.ui.base.WindowAndroid;
 
 /**
- * A {@link UnownedUserDataSupplier} which manages the supplier and UnownedUserData for a
- * {@link TabObscuringHandler}.
+ * A {@link UnownedUserDataSupplier} which manages the supplier and UnownedUserData for a {@link
+ * TabObscuringHandler}.
  */
 @NullMarked
 public class TabObscuringHandlerSupplier extends UnownedUserDataSupplier<TabObscuringHandler> {
     private static final UnownedUserDataKey<TabObscuringHandlerSupplier> KEY =
-            new UnownedUserDataKey<TabObscuringHandlerSupplier>(TabObscuringHandlerSupplier.class);
+            new UnownedUserDataKey<>(TabObscuringHandlerSupplier.class);
 
     /**
-     * Retrieves an {@link ObservableSupplier} from the given host. Real implementations should
-     * use {@link WindowAndroid}.
+     * Retrieves an {@link ObservableSupplier} from the given host. Real implementations should use
+     * {@link WindowAndroid}.
      */
     public static @Nullable TabObscuringHandler getValueOrNullFrom(
             @Nullable WindowAndroid windowAndroid) {
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/PersistedTabData.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/PersistedTabData.java
index fd709a3..a955949 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/PersistedTabData.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/PersistedTabData.java
@@ -407,7 +407,7 @@
     @VisibleForTesting
     protected Serializer<ByteBuffer> getOomAndMetricsWrapper() {
         final Serializer<ByteBuffer> serializer = getSerializerWithOomSoftFallback();
-        return new Serializer<ByteBuffer>() {
+        return new Serializer<>() {
             @Override
             public @Nullable ByteBuffer get() {
                 if (serializer == null) return null;
diff --git a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabUiThemeUtils.java b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabUiThemeUtils.java
index 5f2650c..34d00f0 100644
--- a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabUiThemeUtils.java
+++ b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabUiThemeUtils.java
@@ -38,7 +38,7 @@
      */
     public static @ColorInt int getChromeOwnedFaviconTintColor(
             Context context, boolean isIncognito, boolean isTabSelected) {
-        return getTitleTextColor(context, isIncognito, isTabSelected);
+        return getTitleTextColor(context, isIncognito, isTabSelected, null);
     }
 
     /**
@@ -46,21 +46,20 @@
      *
      * @param isIncognito Whether the text appearance is used for incognito mode.
      * @param isSelected Whether the tab is currently selected.
+     * @param colorId colorId Color chosen by user for the TabGroup, Null if not a tab group.
      * @return The text appearance for the tab grid card title.
      */
     public static @ColorInt int getTitleTextColor(
-            Context context, boolean isIncognito, boolean isSelected) {
-        if (isIncognito) {
-            @ColorRes
-            int colorRes =
-                    isSelected
-                            ? R.color.incognito_tab_title_selected_color
-                            : R.color.incognito_tab_title_color;
-            return context.getColor(colorRes);
+            Context context,
+            boolean isIncognito,
+            boolean isSelected,
+            @Nullable @TabGroupColorId Integer colorId) {
+        if (isSelected) {
+            return isIncognito
+                    ? context.getColor(R.color.incognito_tab_title_selected_color)
+                    : MaterialColors.getColor(context, R.attr.colorOnPrimary, TAG);
         } else {
-            return isSelected
-                    ? MaterialColors.getColor(context, R.attr.colorOnPrimary, TAG)
-                    : MaterialColors.getColor(context, R.attr.colorOnSurface, TAG);
+            return SurfaceColorUpdateUtils.getCardViewTextColor(context, isIncognito, colorId);
         }
     }
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index cae0c59..bbe3294 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1072,8 +1072,6 @@
       "passwords/password_base_dialog_controller.h",
       "passwords/password_change_icon_views_controller.cc",
       "passwords/password_change_icon_views_controller.h",
-      "passwords/password_change_ui_controller.cc",
-      "passwords/password_change_ui_controller.h",
       "passwords/password_dialog_prompts.h",
       "passwords/password_generation_popup_controller.h",
       "passwords/password_generation_popup_controller_impl.cc",
@@ -1610,6 +1608,7 @@
       "//chrome/app/vector_icons",
       "//chrome/browser:shell_integration",
       "//chrome/browser/actor",
+      "//chrome/browser/actor:impl",
       "//chrome/browser/apps/app_service",
       "//chrome/browser/apps/app_service/app_icon",
       "//chrome/browser/apps/app_service/app_icon:util",
@@ -1896,7 +1895,7 @@
 
       "//chrome/browser/ui:browser_list_impl",
       "//chrome/browser/ui/media_router:impl",
-      "//chrome/browser/actor",
+      "//chrome/browser/actor:impl",
       "//chrome/browser/ui/window_sizer:impl",
       "//chrome/browser/ui/webui/settings:impl",
       "//chrome/browser/ui/search_engine_choice:impl",
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java
index 14dfabe4..aa40c1c 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java
@@ -257,8 +257,8 @@
         mIsByPermanentButton = isByPermanentButton;
 
         // Find the height for each menu item.
-        List<Integer> menuItemIds = new ArrayList<Integer>();
-        List<Integer> heightList = new ArrayList<Integer>();
+        List<Integer> menuItemIds = new ArrayList<>();
+        List<Integer> heightList = new ArrayList<>();
         for (int i = 0; i < mModelList.size(); i++) {
             int itemId = mModelList.get(i).model.get(AppMenuItemProperties.MENU_ITEM_ID);
             menuItemIds.add(itemId);
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuDragHelper.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuDragHelper.java
index c27b5f1..e6888b1 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuDragHelper.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuDragHelper.java
@@ -261,7 +261,7 @@
         ListView listView = mAppMenu.getListView();
         assumeNonNull(listView);
 
-        ArrayList<View> itemViews = new ArrayList<View>();
+        ArrayList<View> itemViews = new ArrayList<>();
         for (int i = 0; i < listView.getChildCount(); ++i) {
             boolean hasImageButtons = false;
             if (listView.getChildAt(i) instanceof LinearLayout) {
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTest.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTest.java
index 49d4436..8efaca8 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTest.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTest.java
@@ -967,8 +967,8 @@
     public void testCalculateHeightForItems_enoughSpace() throws Exception {
         showMenuAndAssert();
 
-        List<Integer> menuItemIds = new ArrayList<Integer>();
-        List<Integer> heightList = new ArrayList<Integer>();
+        List<Integer> menuItemIds = new ArrayList<>();
+        List<Integer> heightList = new ArrayList<>();
         createMenuItem(menuItemIds, heightList, /* id= */ 0, /* height= */ 10);
         createMenuItem(menuItemIds, heightList, /* id= */ 1, /* height= */ 10);
         createMenuItem(menuItemIds, heightList, /* id= */ 2, /* height= */ 10);
@@ -989,8 +989,8 @@
     public void testCalculateHeightForItems_notEnoughSpaceForOneItem() throws Exception {
         showMenuAndAssert();
 
-        List<Integer> menuItemIds = new ArrayList<Integer>();
-        List<Integer> heightList = new ArrayList<Integer>();
+        List<Integer> menuItemIds = new ArrayList<>();
+        List<Integer> heightList = new ArrayList<>();
         createMenuItem(menuItemIds, heightList, /* id= */ 0, /* height= */ 10);
         createMenuItem(menuItemIds, heightList, /* id= */ 1, /* height= */ 10);
         createMenuItem(menuItemIds, heightList, /* id= */ 2, /* height= */ 10);
@@ -1012,8 +1012,8 @@
     public void testCalculateHeightForItems_notEnoughSpaceForTwoItem() throws Exception {
         showMenuAndAssert();
 
-        List<Integer> menuItemIds = new ArrayList<Integer>();
-        List<Integer> heightList = new ArrayList<Integer>();
+        List<Integer> menuItemIds = new ArrayList<>();
+        List<Integer> heightList = new ArrayList<>();
         createMenuItem(menuItemIds, heightList, /* id= */ 0, /* height= */ 10);
         createMenuItem(menuItemIds, heightList, /* id= */ 1, /* height= */ 10);
         createMenuItem(menuItemIds, heightList, /* id= */ 2, /* height= */ 10);
@@ -1037,8 +1037,8 @@
     public void testCalculateHeightForItems_notEnoughSpaceForThreeItem() throws Exception {
         showMenuAndAssert();
 
-        List<Integer> menuItemIds = new ArrayList<Integer>();
-        List<Integer> heightList = new ArrayList<Integer>();
+        List<Integer> menuItemIds = new ArrayList<>();
+        List<Integer> heightList = new ArrayList<>();
         createMenuItem(menuItemIds, heightList, /* id= */ 0, /* height= */ 10);
         createMenuItem(menuItemIds, heightList, /* id= */ 1, /* height= */ 10);
         createMenuItem(menuItemIds, heightList, /* id= */ 2, /* height= */ 10);
@@ -1063,8 +1063,8 @@
     public void testCalculateHeightForItems_notEnoughSpaceForDivider() throws Exception {
         showMenuAndAssert();
 
-        List<Integer> menuItemIds = new ArrayList<Integer>();
-        List<Integer> heightList = new ArrayList<Integer>();
+        List<Integer> menuItemIds = new ArrayList<>();
+        List<Integer> heightList = new ArrayList<>();
         createMenuItem(menuItemIds, heightList, /* id= */ 0, /* height= */ 10);
         createMenuItem(menuItemIds, heightList, /* id= */ 1, /* height= */ 10);
         createMenuItem(menuItemIds, heightList, /* id= */ 2, /* height= */ 10);
@@ -1089,8 +1089,8 @@
     public void testCalculateHeightForItems_showPartialDivider() throws Exception {
         showMenuAndAssert();
 
-        List<Integer> menuItemIds = new ArrayList<Integer>();
-        List<Integer> heightList = new ArrayList<Integer>();
+        List<Integer> menuItemIds = new ArrayList<>();
+        List<Integer> heightList = new ArrayList<>();
         createMenuItem(menuItemIds, heightList, /* id= */ 0, /* height= */ 10);
         createMenuItem(menuItemIds, heightList, /* id= */ 1, /* height= */ 10);
         createMenuItem(menuItemIds, heightList, /* id= */ 2, /* height= */ 10);
@@ -1116,8 +1116,8 @@
             throws Exception {
         showMenuAndAssert();
 
-        List<Integer> menuItemIds = new ArrayList<Integer>();
-        List<Integer> heightList = new ArrayList<Integer>();
+        List<Integer> menuItemIds = new ArrayList<>();
+        List<Integer> heightList = new ArrayList<>();
         createMenuItem(menuItemIds, heightList, /* id= */ 0, /* height= */ 10);
         createMenuItem(menuItemIds, heightList, /* id= */ 1, /* height= */ 10);
         createMenuItem(menuItemIds, heightList, /* id= */ 2, /* height= */ 10);
@@ -1144,8 +1144,8 @@
     public void testCalculateHeightForItems_minimalHight() throws Exception {
         showMenuAndAssert();
 
-        List<Integer> menuItemIds = new ArrayList<Integer>();
-        List<Integer> heightList = new ArrayList<Integer>();
+        List<Integer> menuItemIds = new ArrayList<>();
+        List<Integer> heightList = new ArrayList<>();
         createMenuItem(menuItemIds, heightList, /* id= */ 0, /* height= */ 10);
         createMenuItem(menuItemIds, heightList, /* id= */ 1, /* height= */ 10);
         createMenuItem(menuItemIds, heightList, /* id= */ 2, /* height= */ 10);
@@ -1168,8 +1168,8 @@
             throws Exception {
         showMenuAndAssert();
 
-        List<Integer> menuItemIds = new ArrayList<Integer>();
-        List<Integer> heightList = new ArrayList<Integer>();
+        List<Integer> menuItemIds = new ArrayList<>();
+        List<Integer> heightList = new ArrayList<>();
         createMenuItem(menuItemIds, heightList, /* id= */ 0, /* height= */ 10);
         createMenuItem(menuItemIds, heightList, /* id= */ 1, /* height= */ 10);
         createMenuItem(menuItemIds, heightList, /* id= */ 2, /* height= */ 10);
@@ -1191,8 +1191,8 @@
     public void testCalculateHeightForItems_nagativeSpaceForZeroItems() throws Exception {
         showMenuAndAssert();
 
-        List<Integer> menuItemIds = new ArrayList<Integer>();
-        List<Integer> heightList = new ArrayList<Integer>();
+        List<Integer> menuItemIds = new ArrayList<>();
+        List<Integer> heightList = new ArrayList<>();
 
         int height =
                 mAppMenuHandler
diff --git a/chrome/browser/ui/android/ephemeraltab/java/src/org/chromium/chrome/browser/ephemeraltab/EphemeralTabMediator.java b/chrome/browser/ui/android/ephemeraltab/java/src/org/chromium/chrome/browser/ephemeraltab/EphemeralTabMediator.java
index 812c37c3..7b71b89 100644
--- a/chrome/browser/ui/android/ephemeraltab/java/src/org/chromium/chrome/browser/ephemeraltab/EphemeralTabMediator.java
+++ b/chrome/browser/ui/android/ephemeraltab/java/src/org/chromium/chrome/browser/ephemeraltab/EphemeralTabMediator.java
@@ -58,7 +58,7 @@
         mBottomSheetController = bottomSheetController;
         mFaviconLoader = faviconLoader;
         mTopControlsHeightDp = topControlsHeightDp;
-        mObservers = new ObserverList<EphemeralTabObserver>();
+        mObservers = new ObserverList<>();
     }
 
     /** Initializes various objects for a new tab. */
diff --git a/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoView.java b/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoView.java
index c506d7a..aa9f250e 100644
--- a/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoView.java
+++ b/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoView.java
@@ -75,7 +75,7 @@
     private int mDoodleSize;
 
     private final FloatProperty<LogoView> mTransitionProperty =
-            new FloatProperty<LogoView>("") {
+            new FloatProperty<>("") {
                 @Override
                 public Float get(LogoView logoView) {
                     return logoView.mTransitionAmount;
diff --git a/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceStateUnitTest.java b/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceStateUnitTest.java
index 7be65757..0bd117d 100644
--- a/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceStateUnitTest.java
+++ b/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceStateUnitTest.java
@@ -4,6 +4,11 @@
 
 package org.chromium.chrome.browser.multiwindow;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
 import android.app.Activity;
 import android.app.ActivityManager.AppTask;
 import android.app.ActivityManager.RecentTaskInfo;
@@ -11,7 +16,6 @@
 import android.text.TextUtils;
 
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -30,7 +34,7 @@
 import java.util.List;
 import java.util.Map;
 
-/** Unit tests for MultiInstanceState. */
+/** Unit tests for {@link MultiInstanceState}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(
         manifest = Config.NONE,
@@ -97,9 +101,9 @@
         private void assertObserver(boolean called, String message) {
             try {
                 if (called) {
-                    Assert.assertTrue(message, getCallCount() == mCount + 1);
+                    assertEquals(message, getCallCount(), mCount + 1);
                 } else {
-                    Assert.assertFalse(message, getCallCount() == mCount + 1);
+                    assertNotEquals(message, getCallCount(), mCount + 1);
                 }
             } finally {
                 mCount = getCallCount();
@@ -135,7 +139,7 @@
     }
 
     private List<AppTask> getChromeTasks() {
-        return new ArrayList<AppTask>(sTasks.keySet());
+        return new ArrayList<>(sTasks.keySet());
     }
 
     private boolean matchesBaseActivity(String name) {
@@ -147,7 +151,7 @@
         ObserverHelper helper = new ObserverHelper();
         mMultiInstanceState.addObserver((visible) -> helper.notifyCalled());
 
-        BaseActivity baseActivity1 = createTaskAndLaunchActivity(29, new BrowserActivity());
+        createTaskAndLaunchActivity(29, new BrowserActivity());
         assertInSingleInstanceMode("initial state");
 
         BaseActivity baseActivity2 = createTaskAndLaunchActivity(31, new BrowserActivity());
@@ -171,18 +175,18 @@
     @Test
     public void testRuleOutOtherBaseActivityTasks() {
         BaseActivity baseActivity1 = createTaskAndLaunchActivity(29, new CustomTabActivity());
-        BaseActivity baseActivity2 = createTaskAndLaunchActivity(31, new CustomTabActivity());
+        createTaskAndLaunchActivity(31, new CustomTabActivity());
         assertInSingleInstanceMode("Base activity is not legit: " + baseActivity1);
     }
 
     private void assertInMultiInstanceMode(String msg) {
-        Assert.assertTrue(
+        assertTrue(
                 "Should be in multi-instance mode: " + msg,
                 mMultiInstanceState.isInMultiInstanceMode());
     }
 
     private void assertInSingleInstanceMode(String msg) {
-        Assert.assertFalse(
+        assertFalse(
                 "Should be in single-instance mode: " + msg,
                 mMultiInstanceState.isInMultiInstanceMode());
     }
diff --git a/chrome/browser/ui/android/night_mode/java/src/org/chromium/chrome/browser/night_mode/PowerSavingModeMonitor.java b/chrome/browser/ui/android/night_mode/java/src/org/chromium/chrome/browser/night_mode/PowerSavingModeMonitor.java
index 6036b4c0..76eb5b9 100644
--- a/chrome/browser/ui/android/night_mode/java/src/org/chromium/chrome/browser/night_mode/PowerSavingModeMonitor.java
+++ b/chrome/browser/ui/android/night_mode/java/src/org/chromium/chrome/browser/night_mode/PowerSavingModeMonitor.java
@@ -96,7 +96,7 @@
         if (mRegisterTaskPosted) return;
 
         mRegisterReceiverTask =
-                new BackgroundOnlyAsyncTask<Void>() {
+                new BackgroundOnlyAsyncTask<>() {
                     @Override
                     protected Void doInBackground() {
                         if (isCancelled()) return null;
@@ -118,7 +118,7 @@
         if (!mRegisterTaskPosted) return;
 
         mUnregisterReceiverTask =
-                new BackgroundOnlyAsyncTask<Void>() {
+                new BackgroundOnlyAsyncTask<>() {
                     @Override
                     protected Void doInBackground() {
                         ContextUtils.getApplicationContext().unregisterReceiver(mPowerModeReceiver);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/KeyboardHideHelperUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/KeyboardHideHelperUnitTest.java
index eac392b..9d03fe4 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/KeyboardHideHelperUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/KeyboardHideHelperUnitTest.java
@@ -72,7 +72,7 @@
         mKeyboardHideHelper.setWindowDelegate(mWindowDelegate);
         final AtomicInteger height = new AtomicInteger(300);
         Answer<Void> windowVisibleDisplayFrameAnswer =
-                new Answer<Void>() {
+                new Answer<>() {
                     @Override
                     public Void answer(InvocationOnMock invocation) {
                         ((Rect) invocation.getArgument(0)).set(0, 0, 100, height.get());
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index 5e452317..bb20230 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -351,7 +351,7 @@
      *     overlapping text and buttons.
      */
     protected List<View> getUrlContainerViewsForMargin() {
-        List<View> outList = new ArrayList<View>();
+        List<View> outList = new ArrayList<>();
         if (mUrlActionContainer == null) return outList;
 
         for (int i = 0; i < mUrlActionContainer.getChildCount(); i++) {
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 22d3f94..24a162ca 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
@@ -142,7 +142,7 @@
     }
 
     private final FloatProperty<LocationBarMediator> mUrlFocusChangeFractionProperty =
-            new FloatProperty<LocationBarMediator>("") {
+            new FloatProperty<>("") {
                 @Override
                 public Float get(LocationBarMediator object) {
                     return mUrlFocusChangeFraction;
@@ -155,7 +155,7 @@
             };
 
     private final FloatProperty<LocationBarMediator> mWidthChangeFractionPropertyTablet =
-            new FloatProperty<LocationBarMediator>("") {
+            new FloatProperty<>("") {
                 @Override
                 public Float get(LocationBarMediator object) {
                     return ((LocationBarTablet) mLocationBarLayout).getWidthChangeFraction();
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 cb4dbdb..719050327 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
@@ -24,7 +24,6 @@
 import static org.mockito.Mockito.verify;
 
 import android.animation.ObjectAnimator;
-import android.app.Activity;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
@@ -239,7 +238,7 @@
         doReturn(mRootView).when(mLocationBarLayout).getRootView();
         doReturn(true).when(mLocationBarLayout).shouldClearTextOnFocus();
         doReturn(mRootView).when(mLocationBarTablet).getRootView();
-        doReturn(new WeakReference<Activity>(null)).when(mWindowAndroid).getActivity();
+        doReturn(new WeakReference<>(null)).when(mWindowAndroid).getActivity();
         UrlUtilitiesJni.setInstanceForTesting(mUrlUtilitiesJniMock);
         OmniboxPrerenderJni.setInstanceForTesting(mPrerenderJni);
         PreloadPagesSettingsBridgeJni.setInstanceForTesting(mPreloadPagesSettingsJni);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SearchEngineUtils.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SearchEngineUtils.java
index c5fc6a7..0039875 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SearchEngineUtils.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SearchEngineUtils.java
@@ -31,9 +31,7 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileKeyedMap;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
-import org.chromium.chrome.browser.theme.ThemeUtils;
 import org.chromium.chrome.browser.ui.favicon.FaviconHelper;
-import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
 import org.chromium.components.browser_ui.util.GlobalDiscardableReferencePool;
 import org.chromium.components.image_fetcher.ImageFetcher;
 import org.chromium.components.image_fetcher.ImageFetcherConfig;
@@ -46,6 +44,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 
 /** Common Default Search Engine functions. */
 @NullMarked
@@ -65,6 +64,8 @@
     private final int mSearchEngineLogoTargetSizePixels;
     private final ObserverList<SearchBoxHintTextObserver> mSearchBoxHintTextObservers =
             new ObserverList<>();
+    private final ObserverList<SearchEngineIconObserver> mSearchEngineIconObservers =
+            new ObserverList<>();
     private @Nullable SearchEngineMetadata mDefaultSearchEngineMetadata;
     private @Nullable Boolean mNeedToCheckForSearchEnginePromo;
     private boolean mDoesDefaultSearchEngineHaveLogo;
@@ -107,6 +108,16 @@
         void onSearchBoxHintTextChanged(String newHintText);
     }
 
+    @FunctionalInterface
+    public interface SearchEngineIconObserver {
+        /**
+         * Invoked when the Search Engine icon changes.
+         *
+         * @param newIcon the new search engine icon to apply
+         */
+        void onSearchEngineIconChanged(@Nullable StatusIconResource newIcon);
+    }
+
     @VisibleForTesting
     SearchEngineUtils(Profile profile, FaviconHelper faviconHelper) {
         mProfile = profile;
@@ -155,6 +166,7 @@
         mTemplateUrlService.removeObserver(this);
         mFaviconHelper.destroy();
         mImageFetcher.destroy();
+        mSearchEngineIconObservers.clear();
         mSearchBoxHintTextObservers.clear();
     }
 
@@ -216,6 +228,26 @@
         }
     }
 
+    /** Add observer to be notified whenever the Search Enigne Icon changes. */
+    public void addIconObserver(SearchEngineIconObserver observer) {
+        mSearchEngineIconObservers.addObserver(observer);
+        observer.onSearchEngineIconChanged(mFavicon);
+    }
+
+    /** Remove previously registered Search Engine Icon observer. */
+    public void removeIconObserver(SearchEngineIconObserver observer) {
+        mSearchEngineIconObservers.removeObserver(observer);
+    }
+
+    private void setSearchEngineIcon(@Nullable StatusIconResource newIcon) {
+        if (Objects.equals(mFavicon, newIcon)) return;
+        mFavicon = newIcon;
+        for (var observer : mSearchEngineIconObservers) {
+            observer.onSearchEngineIconChanged(newIcon);
+            recordEvent(Events.FETCH_SUCCESS_CACHE_HIT);
+        }
+    }
+
     @VisibleForTesting
     void retrieveFavicon(TemplateUrl templateUrl) {
         if (!mTemplateUrlService.isDefaultSearchEngineGoogle()) {
@@ -225,7 +257,7 @@
             return;
         }
 
-        mFavicon = new StatusIconResource(R.drawable.ic_logo_googleg_20dp, 0);
+        setSearchEngineIcon(new StatusIconResource(R.drawable.ic_logo_googleg_20dp, 0));
     }
 
     private void retrieveFaviconFromFaviconUrl(TemplateUrl templateUrl) {
@@ -273,11 +305,11 @@
     }
 
     private void resetFavicon() {
-        mFavicon = null;
+        setSearchEngineIcon(null);
     }
 
     private void onFaviconRetrieveCompleted(GURL faviconUrl, Bitmap bitmap) {
-        mFavicon = new StatusIconResource(faviconUrl.getSpec(), bitmap, 0);
+        setSearchEngineIcon(new StatusIconResource(faviconUrl.getSpec(), bitmap, 0));
         recordEvent(Events.FETCH_SUCCESS);
     }
 
@@ -287,35 +319,6 @@
     }
 
     /**
-     * Get the search engine logo favicon. This can return a null bitmap under certain
-     * circumstances, such as: no logo url found, network/cache error, etc.
-     *
-     * @param brandedColorScheme The {@link BrandedColorScheme}, used to tint icons.
-     */
-    public StatusIconResource getSearchEngineLogo(@BrandedColorScheme int brandedColorScheme) {
-        if (needToCheckForSearchEnginePromo() || mFavicon == null) {
-            return getFallbackSearchIcon(brandedColorScheme);
-        }
-        recordEvent(Events.FETCH_SUCCESS_CACHE_HIT);
-        return mFavicon;
-    }
-
-    /** Returns an icon to be shown as a fallback Search icon. */
-    public static StatusIconResource getFallbackSearchIcon(
-            @BrandedColorScheme int brandedColorScheme) {
-        return new StatusIconResource(
-                R.drawable.ic_search, ThemeUtils.getThemedToolbarIconTintRes(brandedColorScheme));
-    }
-
-    /** Returns an icon to be shown as a fallback Navigation icon. */
-    public static StatusIconResource getFallbackNavigationIcon(
-            @BrandedColorScheme int brandedColorScheme) {
-        return new StatusIconResource(
-                R.drawable.ic_globe_24dp,
-                ThemeUtils.getThemedToolbarIconTintRes(brandedColorScheme));
-    }
-
-    /**
      * Returns whether the search engine promo is complete. Once fetchCheckForSearchEnginePromo()
      * returns false the first time, this method will cache that result as it's presumed we don't
      * need to re-run the promo during the process lifetime.
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SearchEngineUtilsUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SearchEngineUtilsUnitTest.java
index 2ca3acb..767744ec 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SearchEngineUtilsUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SearchEngineUtilsUnitTest.java
@@ -17,6 +17,7 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.robolectric.Shadows.shadowOf;
 
 import android.content.Context;
@@ -24,7 +25,6 @@
 import android.graphics.Bitmap;
 
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -51,9 +51,7 @@
 import org.chromium.chrome.browser.omnibox.test.R;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
-import org.chromium.chrome.browser.theme.ThemeUtils;
 import org.chromium.chrome.browser.ui.favicon.FaviconHelper;
-import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.components.metrics.OmniboxEventProtos.OmniboxEventProto.PageClassification;
 import org.chromium.components.omnibox.OmniboxFeatureList;
@@ -78,6 +76,7 @@
     @Mock Resources mResources;
     @Mock Profile mProfile;
     @Mock SearchEngineUtils.SearchBoxHintTextObserver mHintTextObserver;
+    @Mock SearchEngineUtils.SearchEngineIconObserver mEngineIconObserver;
 
     private Context mContext;
     private Bitmap mBitmap;
@@ -153,6 +152,10 @@
     @Test
     public void getSearchEngineLogo() {
         var searchEngineUtils = new SearchEngineUtils(mProfile, mFaviconHelper);
+        searchEngineUtils.addIconObserver(mEngineIconObserver);
+        verify(mEngineIconObserver).onSearchEngineIconChanged(null);
+        reset(mEngineIconObserver);
+
         // SearchEngineUtils retrieves logo when it's first created, and whenever the DSE changes.
         verify(mFaviconHelper).getLocalFaviconImageForURL(any(), any(), anyInt(), any());
         mCallbackCaptor.getValue().onFaviconAvailable(mBitmap, new GURL(LOGO_URL));
@@ -166,32 +169,36 @@
                 RecordHistogram.getHistogramValueCountForTesting(
                         EVENTS_HISTOGRAM, SearchEngineUtils.Events.FETCH_SUCCESS));
 
-        var expected = new StatusIconResource(LOGO_URL, mBitmap, 0);
-        var icon = searchEngineUtils.getSearchEngineLogo(BrandedColorScheme.APP_DEFAULT);
-        assertEquals(expected, icon);
+        ArgumentCaptor<StatusIconResource> captor =
+                ArgumentCaptor.forClass(StatusIconResource.class);
+        verify(mEngineIconObserver).onSearchEngineIconChanged(captor.capture());
+
+        assertEquals(captor.getValue(), new StatusIconResource(LOGO_URL, mBitmap, 0));
     }
 
     @Test
     public void getSearchEngineLogo_nullTemplateUrlService() {
         var searchEngineUtils = new SearchEngineUtils(mProfile, mFaviconHelper);
-        StatusIconResource expected =
-                SearchEngineUtils.getFallbackSearchIcon(BrandedColorScheme.APP_DEFAULT);
+        searchEngineUtils.addIconObserver(mEngineIconObserver);
 
-        var icon = searchEngineUtils.getSearchEngineLogo(BrandedColorScheme.APP_DEFAULT);
-
-        assertEquals(expected, icon);
+        verify(mEngineIconObserver).onSearchEngineIconChanged(null);
     }
 
     @Test
     public void getSearchEngineLogo_searchEngineGoogle() {
         var searchEngineUtils = new SearchEngineUtils(mProfile, mFaviconHelper);
+        searchEngineUtils.addIconObserver(mEngineIconObserver);
+        verify(mEngineIconObserver).onSearchEngineIconChanged(null);
+        reset(mEngineIconObserver);
+
         // Simulate DSE change to Google.
         doReturn(true).when(mTemplateUrlService).isDefaultSearchEngineGoogle();
         searchEngineUtils.onTemplateURLServiceChanged();
 
-        var expected = new StatusIconResource(R.drawable.ic_logo_googleg_20dp, 0);
-        var icon = searchEngineUtils.getSearchEngineLogo(BrandedColorScheme.APP_DEFAULT);
-        assertEquals(expected, icon);
+        ArgumentCaptor<StatusIconResource> captor =
+                ArgumentCaptor.forClass(StatusIconResource.class);
+        verify(mEngineIconObserver).onSearchEngineIconChanged(captor.capture());
+        assertEquals(captor.getValue(), new StatusIconResource(R.drawable.ic_logo_googleg_20dp, 0));
     }
 
     private void configureSearchEngine(String keyword, String shortName) {
@@ -430,15 +437,17 @@
     @Test
     public void getSearchEngineLogo_faviconCached() {
         var searchEngineUtils = new SearchEngineUtils(mProfile, mFaviconHelper);
+        searchEngineUtils.addIconObserver(mEngineIconObserver);
+        verify(mEngineIconObserver).onSearchEngineIconChanged(null);
+        reset(mEngineIconObserver);
+
         verify(mFaviconHelper).getLocalFaviconImageForURL(any(), any(), anyInt(), any());
         mCallbackCaptor.getValue().onFaviconAvailable(mBitmap, new GURL(LOGO_URL));
 
-        var expected = new StatusIconResource(LOGO_URL, mBitmap, 0);
-        var icon = searchEngineUtils.getSearchEngineLogo(BrandedColorScheme.APP_DEFAULT);
-        assertEquals(expected, icon);
-
-        icon = searchEngineUtils.getSearchEngineLogo(BrandedColorScheme.APP_DEFAULT);
-        assertEquals(expected, icon);
+        ArgumentCaptor<StatusIconResource> captor =
+                ArgumentCaptor.forClass(StatusIconResource.class);
+        verify(mEngineIconObserver).onSearchEngineIconChanged(captor.capture());
+        assertEquals(captor.getValue(), new StatusIconResource(LOGO_URL, mBitmap, 0));
 
         // Expect only one actual fetch, that happens independently from get request.
         // All get requests always supply cached value.
@@ -451,7 +460,7 @@
                 RecordHistogram.getHistogramValueCountForTesting(
                         EVENTS_HISTOGRAM, SearchEngineUtils.Events.FETCH_SUCCESS));
         assertEquals(
-                2,
+                1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         EVENTS_HISTOGRAM, SearchEngineUtils.Events.FETCH_SUCCESS_CACHE_HIT));
     }
@@ -460,15 +469,14 @@
     public void getSearchEngineLogo_nullUrl() {
         UmaRecorderHolder.resetForTesting();
         var searchEngineUtils = new SearchEngineUtils(mProfile, mFaviconHelper);
+        searchEngineUtils.addIconObserver(mEngineIconObserver);
 
         // Simulate DSE change - policy blocking searches
         doReturn(null).when(mTemplateUrlService).getDefaultSearchEngineTemplateUrl();
         searchEngineUtils.onTemplateURLServiceChanged();
 
-        var expected = SearchEngineUtils.getFallbackSearchIcon(BrandedColorScheme.APP_DEFAULT);
-        var icon = searchEngineUtils.getSearchEngineLogo(BrandedColorScheme.APP_DEFAULT);
+        verify(mEngineIconObserver).onSearchEngineIconChanged(null);
 
-        assertEquals(expected, icon);
         assertEquals(
                 1,
                 RecordHistogram.getHistogramValueCountForTesting(
@@ -488,11 +496,10 @@
                 .when(mFaviconHelper)
                 .getLocalFaviconImageForURL(any(), any(), anyInt(), mCallbackCaptor.capture());
         var searchEngineUtils = new SearchEngineUtils(mProfile, mFaviconHelper);
+        searchEngineUtils.addIconObserver(mEngineIconObserver);
 
-        var expected = SearchEngineUtils.getFallbackSearchIcon(BrandedColorScheme.APP_DEFAULT);
-        var icon = searchEngineUtils.getSearchEngineLogo(BrandedColorScheme.APP_DEFAULT);
+        verify(mEngineIconObserver).onSearchEngineIconChanged(null);
 
-        assertEquals(expected, icon);
         assertEquals(
                 1,
                 RecordHistogram.getHistogramValueCountForTesting(
@@ -507,16 +514,16 @@
     @Test
     public void getSearchEngineLogo_returnedBitmapNull() {
         var searchEngineUtils = new SearchEngineUtils(mProfile, mFaviconHelper);
-        StatusIconResource expected =
-                SearchEngineUtils.getFallbackSearchIcon(BrandedColorScheme.APP_DEFAULT);
+        searchEngineUtils.addIconObserver(mEngineIconObserver);
 
-        var icon = searchEngineUtils.getSearchEngineLogo(BrandedColorScheme.APP_DEFAULT);
+        verify(mEngineIconObserver).onSearchEngineIconChanged(null);
+        reset(mEngineIconObserver);
+
         verify(mFaviconHelper)
                 .getLocalFaviconImageForURL(any(), any(), anyInt(), mCallbackCaptor.capture());
         FaviconHelper.FaviconImageCallback faviconCallback = mCallbackCaptor.getValue();
         faviconCallback.onFaviconAvailable(null, new GURL(LOGO_URL));
 
-        assertEquals(expected, icon);
         assertEquals(
                 1,
                 RecordHistogram.getHistogramValueCountForTesting(
@@ -526,44 +533,9 @@
                 RecordHistogram.getHistogramValueCountForTesting(
                         EVENTS_HISTOGRAM,
                         SearchEngineUtils.Events.FETCH_FAILED_RETURNED_BITMAP_NULL));
-    }
 
-    @Test
-    public void getFallbackSearchIcon() {
-        var searchEngineUtils = new SearchEngineUtils(mProfile, mFaviconHelper);
-        StatusIconResource expected =
-                new StatusIconResource(
-                        R.drawable.ic_search, R.color.default_icon_color_white_tint_list);
-        Assert.assertEquals(
-                expected,
-                SearchEngineUtils.getFallbackSearchIcon(BrandedColorScheme.DARK_BRANDED_THEME));
-
-        expected =
-                new StatusIconResource(
-                        R.drawable.ic_search,
-                        ThemeUtils.getThemedToolbarIconTintRes(/* useLight= */ true));
-        Assert.assertEquals(
-                expected, SearchEngineUtils.getFallbackSearchIcon(BrandedColorScheme.INCOGNITO));
-    }
-
-    @Test
-    public void getFallbackNavigationIcon() {
-        var searchEngineUtils = new SearchEngineUtils(mProfile, mFaviconHelper);
-        StatusIconResource expected =
-                new StatusIconResource(
-                        R.drawable.ic_globe_24dp, R.color.default_icon_color_white_tint_list);
-
-        Assert.assertEquals(
-                expected,
-                SearchEngineUtils.getFallbackNavigationIcon(BrandedColorScheme.DARK_BRANDED_THEME));
-
-        expected =
-                new StatusIconResource(
-                        R.drawable.ic_globe_24dp,
-                        ThemeUtils.getThemedToolbarIconTintRes(/* useLight= */ true));
-        Assert.assertEquals(
-                expected,
-                SearchEngineUtils.getFallbackNavigationIcon(BrandedColorScheme.INCOGNITO));
+        // Not emitting second null icon
+        verifyNoMoreInteractions(mEngineIconObserver);
     }
 
     @Test
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusCoordinator.java
index 1e77215..bfa3f0d4b 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusCoordinator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusCoordinator.java
@@ -19,6 +19,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.ContextUtils;
+import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.OneshotSupplier;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.build.annotations.NullMarked;
@@ -91,7 +92,7 @@
             UrlBarEditingTextStateProvider urlBarEditingTextStateProvider,
             LocationBarDataProvider locationBarDataProvider,
             OneshotSupplier<TemplateUrlService> templateUrlServiceSupplier,
-            Supplier<Profile> profileSupplier,
+            ObservableSupplier<Profile> profileSupplier,
             WindowAndroid windowAndroid,
             PageInfoAction pageInfoAction,
             @Nullable Supplier<MerchantTrustSignalsCoordinator>
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
index 777133c3..4577467 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
@@ -17,6 +17,7 @@
 import androidx.annotation.StringRes;
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.OneshotSupplier;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.build.annotations.NullMarked;
@@ -62,7 +63,8 @@
         implements PermissionDialogController.Observer,
                 TemplateUrlServiceObserver,
                 MerchantTrustSignalsCoordinator.OmniboxIconController,
-                CookieControlsObserver {
+                CookieControlsObserver,
+                SearchEngineUtils.SearchEngineIconObserver {
     private static final int PERMISSION_ICON_DEFAULT_DISPLAY_TIMEOUT_MS = 8500;
     public static final String PERMISSION_ICON_TIMEOUT_MS_PARAM = "PermissionIconTimeoutMs";
 
@@ -70,7 +72,7 @@
 
     private final PropertyModel mModel;
     private final OneshotSupplier<TemplateUrlService> mTemplateUrlServiceSupplier;
-    private final Supplier<Profile> mProfileSupplier;
+    private final ObservableSupplier<Profile> mProfileSupplier;
     private final @Nullable Supplier<MerchantTrustSignalsCoordinator>
             mMerchantTrustSignalsCoordinatorSupplier;
     // When the parity update is enabled, we want to:
@@ -116,6 +118,8 @@
     private float mUrlFocusPercent;
 
     private @Nullable CookieControlsBridge mCookieControlsBridge;
+    private @Nullable SearchEngineUtils mSearchEngineUtils;
+    private @Nullable StatusIconResource mSearchEngineIcon;
     private int mBlockingStatus3pcd;
     private int mLastTabId;
     private boolean mCurrentTabCrashed;
@@ -148,7 +152,7 @@
             LocationBarDataProvider locationBarDataProvider,
             PermissionDialogController permissionDialogController,
             OneshotSupplier<TemplateUrlService> templateUrlServiceSupplier,
-            Supplier<Profile> profileSupplier,
+            ObservableSupplier<Profile> profileSupplier,
             PageInfoIphController pageInfoIphController,
             WindowAndroid windowAndroid,
             @Nullable Supplier<MerchantTrustSignalsCoordinator>
@@ -180,6 +184,15 @@
         mPermissionDialogController = permissionDialogController;
         mPermissionDialogController.addObserver(this);
 
+        mProfileSupplier.addObserver(
+                p -> {
+                    if (mSearchEngineUtils != null) {
+                        mSearchEngineUtils.removeIconObserver(this);
+                    }
+                    mSearchEngineUtils = SearchEngineUtils.getForProfile(p);
+                    mSearchEngineUtils.addIconObserver(this);
+                });
+
         updateColorTheme();
         setStatusIconShown(
                 /* show= */ mParityUpdateEnabled || !mLocationBarDataProvider.isIncognitoBranded());
@@ -187,6 +200,11 @@
     }
 
     public void destroy() {
+        if (mSearchEngineUtils != null) {
+            mSearchEngineUtils.removeIconObserver(this);
+            mSearchEngineUtils = null;
+        }
+
         mPermissionTaskHandler.removeCallbacksAndMessages(null);
         mPermissionDialogController.removeObserver(this);
         mStoreIconHandler.removeCallbacksAndMessages(null);
@@ -593,15 +611,18 @@
     private StatusIconResource getStatusIconResourceForSearchEngineIcon() {
         // If the current url text is a valid url, then swap the dse icon for a globe.
         if (!mUrlBarTextIsSearch) {
-            return SearchEngineUtils.getFallbackNavigationIcon(mBrandedColorScheme);
+            return new StatusIconResource(
+                    R.drawable.ic_globe_24dp,
+                    ThemeUtils.getThemedToolbarIconTintRes(mBrandedColorScheme));
         }
 
-        if (!mProfileSupplier.hasValue()) {
-            return SearchEngineUtils.getFallbackSearchIcon(mBrandedColorScheme);
+        if (mSearchEngineIcon == null) {
+            return new StatusIconResource(
+                    R.drawable.ic_search,
+                    ThemeUtils.getThemedToolbarIconTintRes(mBrandedColorScheme));
         }
 
-        var profile = mProfileSupplier.get();
-        return SearchEngineUtils.getForProfile(profile).getSearchEngineLogo(mBrandedColorScheme);
+        return mSearchEngineIcon;
     }
 
     /** Return the resource id for the accessibility description or 0 if none apply. */
@@ -845,6 +866,12 @@
         updateLocationBarIcon(IconTransitionType.CROSSFADE);
     }
 
+    @Override
+    public void onSearchEngineIconChanged(@Nullable StatusIconResource newIcon) {
+        mSearchEngineIcon = newIcon;
+        maybeUpdateStatusIconForSearchEngineIcon();
+    }
+
     void setTranslationX(float translationX) {
         mModel.set(StatusProperties.TRANSLATION_X, translationX);
     }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
index c0d7960..1b3cdd50 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
@@ -130,7 +130,6 @@
 
         doReturn(false).when(mLocationBarDataProvider).isIncognito();
         doReturn(mNewTabPageDelegate).when(mLocationBarDataProvider).getNewTabPageDelegate();
-        doReturn(logo).when(mSearchEngineUtils).getSearchEngineLogo(BrandedColorScheme.APP_DEFAULT);
 
         UserPrefsJni.setInstanceForTesting(mMockUserPrefsJni);
         doReturn(mPrefs).when(mMockUserPrefsJni).get(mProfile);
@@ -138,6 +137,8 @@
         TrackerFactory.setTrackerForTests(mTracker);
 
         setupStatusMediator(/* isTablet= */ false);
+
+        mMediator.onSearchEngineIconChanged(logo);
     }
 
     @After
@@ -159,7 +160,7 @@
                         mLocationBarDataProvider,
                         mPermissionDialogController,
                         mTemplateUrlServiceSupplier,
-                        () -> mProfile,
+                        new ObservableSupplierImpl(mProfile),
                         mPageInfoIphController,
                         mWindowAndroid,
                         merchantTrustSignalsCoordinatorObservableSupplier);
@@ -414,8 +415,6 @@
         mMediator.setUrlFocusChangePercent(0.9f);
         Assert.assertEquals(true, mModel.get(StatusProperties.SHOW_STATUS_ICON));
 
-        verify(mSearchEngineUtils, times(1)).getSearchEngineLogo(BrandedColorScheme.APP_DEFAULT);
-
         mMediator.setUrlFocusChangePercent(0.0f);
         Assert.assertEquals(false, mModel.get(StatusProperties.SHOW_STATUS_ICON));
     }
@@ -535,16 +534,6 @@
 
     @Test
     @SmallTest
-    public void testTemplateUrlServiceChanged() {
-        mMediator.setShowIconsWhenUrlFocused(true);
-        mMediator.setUrlHasFocus(true);
-
-        mMediator.onTemplateURLServiceChanged();
-        verify(mSearchEngineUtils, times(2)).getSearchEngineLogo(BrandedColorScheme.APP_DEFAULT);
-    }
-
-    @Test
-    @SmallTest
     public void testSetStoreIconController() {
         mMediator.setStoreIconController();
         verify(mMerchantTrustSignalsCoordinator, times(1)).setOmniboxIconController(eq(mMediator));
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
index c456a04..f477c68e 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
@@ -204,7 +204,7 @@
 
     private ViewProvider<SuggestionListViewHolder> createViewProvider(
             boolean forcePhoneStyleOmnibox) {
-        return new ViewProvider<SuggestionListViewHolder>() {
+        return new ViewProvider<>() {
             private AsyncViewProvider<ViewGroup> mAsyncProvider;
             private final List<Callback<SuggestionListViewHolder>> mCallbacks = new ArrayList<>();
             private @Nullable SuggestionListViewHolder mHolder;
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownAdapter.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownAdapter.java
index 67ed050..a052af5 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownAdapter.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownAdapter.java
@@ -43,45 +43,43 @@
         registerType(
                 OmniboxSuggestionUiType.DEFAULT,
                 parent ->
-                        new BaseSuggestionView<View>(
+                        new BaseSuggestionView<>(
                                 parent.getContext(), R.layout.omnibox_basic_suggestion),
-                new BaseSuggestionViewBinder<View>(SuggestionViewViewBinder::bind));
+                new BaseSuggestionViewBinder<>(SuggestionViewViewBinder::bind));
 
         // Similar to a default suggestion, with more action buttons.
         registerType(
                 OmniboxSuggestionUiType.EDIT_URL_SUGGESTION,
                 parent ->
-                        new BaseSuggestionView<View>(
+                        new BaseSuggestionView<>(
                                 parent.getContext(), R.layout.omnibox_basic_suggestion),
-                new BaseSuggestionViewBinder<View>(SuggestionViewViewBinder::bind));
+                new BaseSuggestionViewBinder<>(SuggestionViewViewBinder::bind));
 
         registerType(
                 OmniboxSuggestionUiType.ANSWER_SUGGESTION,
                 parent ->
-                        new BaseSuggestionView<View>(
+                        new BaseSuggestionView<>(
                                 parent.getContext(), R.layout.omnibox_answer_suggestion),
-                new BaseSuggestionViewBinder<View>(AnswerSuggestionViewBinder::bind));
+                new BaseSuggestionViewBinder<>(AnswerSuggestionViewBinder::bind));
 
         registerType(
                 OmniboxSuggestionUiType.ENTITY_SUGGESTION,
                 parent ->
-                        new BaseSuggestionView<View>(
+                        new BaseSuggestionView<>(
                                 parent.getContext(), R.layout.omnibox_basic_suggestion),
-                new BaseSuggestionViewBinder<View>(EntitySuggestionViewBinder::bind));
+                new BaseSuggestionViewBinder<>(EntitySuggestionViewBinder::bind));
 
         registerType(
                 OmniboxSuggestionUiType.TAIL_SUGGESTION,
-                parent ->
-                        new BaseSuggestionView<TailSuggestionView>(
-                                new TailSuggestionView(parent.getContext())),
-                new BaseSuggestionViewBinder<TailSuggestionView>(TailSuggestionViewBinder::bind));
+                parent -> new BaseSuggestionView<>(new TailSuggestionView(parent.getContext())),
+                new BaseSuggestionViewBinder<>(TailSuggestionViewBinder::bind));
 
         registerType(
                 OmniboxSuggestionUiType.CLIPBOARD_SUGGESTION,
                 parent ->
-                        new BaseSuggestionView<View>(
+                        new BaseSuggestionView<>(
                                 parent.getContext(), R.layout.omnibox_basic_suggestion),
-                new BaseSuggestionViewBinder<View>(SuggestionViewViewBinder::bind));
+                new BaseSuggestionViewBinder<>(SuggestionViewViewBinder::bind));
 
         registerType(
                 OmniboxSuggestionUiType.TILE_NAVSUGGEST,
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinder.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinder.java
index afa3f340..ad6b2ab9 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinder.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinder.java
@@ -71,7 +71,7 @@
         } else if (SuggestionListProperties.SUGGESTION_MODELS.equals(propertyKey)) {
             ModelList listItems = model.get(SuggestionListProperties.SUGGESTION_MODELS);
             listItems.addObserver(
-                    new ListObservable.ListObserver<Void>() {
+                    new ListObservable.ListObserver<>() {
                         @Override
                         public void onItemRangeChanged(
                                 ListObservable<Void> source,
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionViewBinderUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionViewBinderUnitTest.java
index a8c00aae..494199e 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionViewBinderUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionViewBinderUnitTest.java
@@ -30,7 +30,7 @@
     @Before
     public void setUp() {
         mView =
-                new BaseSuggestionView<View>(
+                new BaseSuggestionView<>(
                         ContextUtils.getApplicationContext(), R.layout.omnibox_basic_suggestion);
         mModel = new PropertyModel(EntitySuggestionViewProperties.ALL_KEYS);
         PropertyModelChangeProcessor.create(mModel, mView, EntitySuggestionViewBinder::bind);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/RecognitionTestHelper.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/RecognitionTestHelper.java
index 5035001a7..14bb746 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/RecognitionTestHelper.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/RecognitionTestHelper.java
@@ -48,8 +48,7 @@
     public static Bundle createPlaceholderBundle(String[] texts, float[] confidences) {
         Bundle b = new Bundle();
 
-        b.putStringArrayList(
-                RecognizerIntent.EXTRA_RESULTS, new ArrayList<String>(Arrays.asList(texts)));
+        b.putStringArrayList(RecognizerIntent.EXTRA_RESULTS, new ArrayList<>(Arrays.asList(texts)));
         b.putFloatArray(RecognizerIntent.EXTRA_CONFIDENCE_SCORES, confidences);
         return b;
     }
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 8381eb84c..3b5578bc3 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -2349,6 +2349,25 @@
         Downloaded PDFs automatically open with <ph name="APP_NAME">%1$s<ex>Drive PDF Viewer</ex></ph>
       </message>
 
+      <!-- Download warning bypass dialog -->
+      <message name="IDS_DOWNLOAD_WARNING_BYPASS_DIALOG_TEXT" desc="Text of the dialog for asking user to confirm that they want to bypass a download warning.">
+        This file contains malware or comes from a suspicious site
+      </message>
+      <message name="IDS_DOWNLOAD_WARNING_BYPASS_DIALOG_ACTION_DOWNLOAD" desc="Label for the button to confirm that the user wants to bypass a download warning.">
+        Download anyway
+      </message>
+      <message name="IDS_DOWNLOAD_WARNING_BYPASS_DIALOG_ACTION_LEARN_MORE" desc="Label for the button to open a help page about download warnings.">
+        Learn more
+      </message>
+
+      <!-- Download Home dangerous warning -->
+      <message name="IDS_DOWNLOAD_WARNING_HEED_MENU_ACTION_DELETE" desc="Label for the menu item to heed a download warning shown in the downloads list by removing the file from download history.">
+        Delete from history
+      </message>
+      <message name="IDS_DOWNLOAD_WARNING_BYPASS_MENU_ACTION_DOWNLOAD" desc="Label for the menu item to bypass a download warning shown in the downloads list and download the dangerous file anyway.">
+        Download
+      </message>
+
       <!-- About Chrome preferences -->
       <message name="IDS_PREFS_ABOUT_CHROME" desc="Title for the About Chrome page. [CHAR_LIMIT=32]">
         About Chrome
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_DOWNLOAD_WARNING_BYPASS_DIALOG_ACTION_DOWNLOAD.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_DOWNLOAD_WARNING_BYPASS_DIALOG_ACTION_DOWNLOAD.png.sha1
new file mode 100644
index 0000000..81c2f868
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_DOWNLOAD_WARNING_BYPASS_DIALOG_ACTION_DOWNLOAD.png.sha1
@@ -0,0 +1 @@
+675347e996f9da4646ed576012525cd3c424858f
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_DOWNLOAD_WARNING_BYPASS_DIALOG_ACTION_LEARN_MORE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_DOWNLOAD_WARNING_BYPASS_DIALOG_ACTION_LEARN_MORE.png.sha1
new file mode 100644
index 0000000..81c2f868
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_DOWNLOAD_WARNING_BYPASS_DIALOG_ACTION_LEARN_MORE.png.sha1
@@ -0,0 +1 @@
+675347e996f9da4646ed576012525cd3c424858f
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_DOWNLOAD_WARNING_BYPASS_DIALOG_TEXT.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_DOWNLOAD_WARNING_BYPASS_DIALOG_TEXT.png.sha1
new file mode 100644
index 0000000..81c2f868
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_DOWNLOAD_WARNING_BYPASS_DIALOG_TEXT.png.sha1
@@ -0,0 +1 @@
+675347e996f9da4646ed576012525cd3c424858f
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_DOWNLOAD_WARNING_BYPASS_MENU_ACTION_DOWNLOAD.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_DOWNLOAD_WARNING_BYPASS_MENU_ACTION_DOWNLOAD.png.sha1
new file mode 100644
index 0000000..7a64bcab
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_DOWNLOAD_WARNING_BYPASS_MENU_ACTION_DOWNLOAD.png.sha1
@@ -0,0 +1 @@
+534b0fa41fa439033b568ffda9dec16f255e91d5
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_DOWNLOAD_WARNING_HEED_MENU_ACTION_DELETE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_DOWNLOAD_WARNING_HEED_MENU_ACTION_DELETE.png.sha1
new file mode 100644
index 0000000..7a64bcab
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_DOWNLOAD_WARNING_HEED_MENU_ACTION_DELETE.png.sha1
@@ -0,0 +1 @@
+534b0fa41fa439033b568ffda9dec16f255e91d5
\ No newline at end of file
diff --git a/chrome/browser/ui/android/theme/BUILD.gn b/chrome/browser/ui/android/theme/BUILD.gn
index 27250226..5d6fb54 100644
--- a/chrome/browser/ui/android/theme/BUILD.gn
+++ b/chrome/browser/ui/android/theme/BUILD.gn
@@ -47,6 +47,8 @@
     "java/res/values/values.xml",
   ]
   deps = [
+    "//chrome/browser/tab_ui/android:java_resources",
+    "//components/browser_ui/strings/android:browser_ui_strings_grd",
     "//components/browser_ui/styles/android:java_resources",
     "//components/browser_ui/widget/android:java_resources",
   ]
diff --git a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtils.java b/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtils.java
index d8fa062..d051ea94 100644
--- a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtils.java
+++ b/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtils.java
@@ -187,6 +187,24 @@
     }
 
     /**
+     * Returns the text color for the card view in grid tab switcher on the enabled flag
+     *
+     * @param context {@link Context} used to retrieve colors.
+     * @param isIncognito Whether the color is used for incognito mode.
+     * @param colorId USer chosen color ID.
+     * @return The text color.
+     */
+    public static @ColorInt int getCardViewTextColor(
+            Context context, boolean isIncognito, @Nullable @TabGroupColorId Integer colorId) {
+        if (useNewGm3GtsTabGroupColors() && colorId != null) {
+            return TabGroupColorPickerUtils.getTabGroupCardTextColor(context, colorId, isIncognito);
+        }
+        return isIncognito
+                ? context.getColor(R.color.incognito_tab_title_color)
+                : SemanticColorUtils.getDefaultTextColor(context);
+    }
+
+    /**
      * Returns the background color for the grid tab switcher message card based on the enabled flag
      * and incognito.
      *
diff --git a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtilsUnitTest.java b/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtilsUnitTest.java
index f734003..2c0b5d6 100644
--- a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtilsUnitTest.java
+++ b/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtilsUnitTest.java
@@ -258,7 +258,7 @@
     @Test
     @EnableFeatures({ChromeFeatureList.ANDROID_TAB_GROUPS_COLOR_UPDATE_GM3})
     @DisableFeatures({ChromeFeatureList.GRID_TAB_SWITCHER_SURFACE_COLOR_UPDATE})
-    public void testGetCardViewBackgroundColor_NewGM3TabGroupColorsEnabled_WithColorId() {
+    public void testGetCardViewBackgroundColor_NewGm3TabGroupColorsEnabled_WithColorId() {
         @TabGroupColorId int blueColorId = TabGroupColorId.BLUE;
         @ColorInt
         int expectedColorFalse =
@@ -293,7 +293,7 @@
         ChromeFeatureList.GRID_TAB_SWITCHER_SURFACE_COLOR_UPDATE
     })
     public void
-            testGetCardViewBackgroundColor_NewGM3TabGroupColorsEnabled_WithColorId_GtsSurfaceAlsoEnabled() {
+            testGetCardViewBackgroundColor_NewGm3TabGroupColorsEnabled_WithColorId_GtsSurfaceAlsoEnabled() {
         @TabGroupColorId int greenColorId = TabGroupColorId.GREEN;
         @ColorInt
         int expectedColor =
@@ -337,4 +337,125 @@
                 expectedIncognito,
                 actualIncognito);
     }
+
+    @Test
+    @EnableFeatures({ChromeFeatureList.ANDROID_TAB_GROUPS_COLOR_UPDATE_GM3})
+    public void testGetCardViewTextColor_NewGm3FlagEnabled_withColorId() {
+        @TabGroupColorId int testColorId = TabGroupColorId.BLUE;
+
+        // Test non-incognito.
+        @ColorInt
+        int expectedNonIncognito =
+                TabGroupColorPickerUtils.getTabGroupCardTextColor(
+                        mContext, testColorId, /* isIncognito= */ false);
+        @ColorInt
+        int actualNonIncognito =
+                SurfaceColorUpdateUtils.getCardViewTextColor(
+                        mContext, /* isIncognito= */ false, testColorId);
+        assertEquals(
+                "Text color mismatch for non-incognito with GM3 flag and colorId.",
+                expectedNonIncognito,
+                actualNonIncognito);
+
+        // Test incognito.
+        @ColorInt
+        int expectedIncognito =
+                TabGroupColorPickerUtils.getTabGroupCardTextColor(
+                        mContext, testColorId, /* isIncognito= */ true);
+        @ColorInt
+        int actualIncognito =
+                SurfaceColorUpdateUtils.getCardViewTextColor(
+                        mContext, /* isIncognito= */ true, testColorId);
+        assertEquals(
+                "Text color mismatch for incognito with GM3 flag and colorId.",
+                expectedIncognito,
+                actualIncognito);
+    }
+
+    @Test
+    @EnableFeatures({ChromeFeatureList.ANDROID_TAB_GROUPS_COLOR_UPDATE_GM3})
+    public void testGetCardViewTextColor_NewGm3FlagEnabled_colorIdNull() {
+
+        // Test non-incognito with null colorId.
+        @ColorInt int expectedNonIncognito = SemanticColorUtils.getDefaultTextColor(mContext);
+        @ColorInt
+        int actualNonIncognito =
+                SurfaceColorUpdateUtils.getCardViewTextColor(
+                        mContext, /* isIncognito= */ false, /* colorId= */ null);
+        assertEquals(
+                "Text color mismatch for non-incognito with GM3 flag and null colorId (fallback).",
+                expectedNonIncognito,
+                actualNonIncognito);
+
+        // Test incognito with null colorId.
+        @ColorInt
+        int expectedIncognito = ContextCompat.getColor(mContext, R.color.incognito_tab_title_color);
+        @ColorInt
+        int actualIncognito =
+                SurfaceColorUpdateUtils.getCardViewTextColor(
+                        mContext, /* isIncognito= */ true, /* colorId= */ null);
+        assertEquals(
+                "Text color mismatch for incognito with GM3 flag and null colorId (fallback).",
+                expectedIncognito,
+                actualIncognito);
+    }
+
+    @Test
+    @DisableFeatures({ChromeFeatureList.ANDROID_TAB_GROUPS_COLOR_UPDATE_GM3})
+    public void testGetCardViewTextColor_NewGm3FlagDisabled_withColorId() {
+        // If GM3 flag is disabled, colorId should be ignored, and it should use fallback logic.
+        @TabGroupColorId int testColorId = TabGroupColorId.RED;
+
+        // Test non-incognito.
+        @ColorInt int expectedNonIncognito = SemanticColorUtils.getDefaultTextColor(mContext);
+        @ColorInt
+        int actualNonIncognito =
+                SurfaceColorUpdateUtils.getCardViewTextColor(
+                        mContext, /* isIncognito= */ false, testColorId);
+        assertEquals(
+                "Text color mismatch for non-incognito with GM3 flag disabled (colorId ignored).",
+                expectedNonIncognito,
+                actualNonIncognito);
+
+        // Test incognito.
+        @ColorInt
+        int expectedIncognito = ContextCompat.getColor(mContext, R.color.incognito_tab_title_color);
+        @ColorInt
+        int actualIncognito =
+                SurfaceColorUpdateUtils.getCardViewTextColor(
+                        mContext, /* isIncognito= */ true, testColorId);
+        assertEquals(
+                "Text color mismatch for incognito with GM3 flag disabled (colorId ignored).",
+                expectedIncognito,
+                actualIncognito);
+    }
+
+    @Test
+    @DisableFeatures({ChromeFeatureList.ANDROID_TAB_GROUPS_COLOR_UPDATE_GM3})
+    public void testGetCardViewTextColor_NewGm3FlagDisabled_colorIdNull() {
+        // If GM3 flag is disabled and colorId is null, it should use fallback logic.
+
+        // Test non-incognito.
+        @ColorInt int expectedNonIncognito = SemanticColorUtils.getDefaultTextColor(mContext);
+        @ColorInt
+        int actualNonIncognito =
+                SurfaceColorUpdateUtils.getCardViewTextColor(
+                        mContext, /* isIncognito= */ false, /* colorId= */ null);
+        assertEquals(
+                "Text color mismatch for non-incognito with GM3 flag disabled and null colorId.",
+                expectedNonIncognito,
+                actualNonIncognito);
+
+        // Test incognito.
+        @ColorInt
+        int expectedIncognito = ContextCompat.getColor(mContext, R.color.incognito_tab_title_color);
+        @ColorInt
+        int actualIncognito =
+                SurfaceColorUpdateUtils.getCardViewTextColor(
+                        mContext, /* isIncognito= */ true, /* colorId= */ null);
+        assertEquals(
+                "Text color mismatch for incognito with GM3 flag disabled and null colorId.",
+                expectedIncognito,
+                actualIncognito);
+    }
 }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionControllerTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionControllerTest.java
index 6ed58b0b..d268e5b 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionControllerTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionControllerTest.java
@@ -226,6 +226,11 @@
                 public int getControlsPosition() {
                     return mControlsPosition;
                 }
+
+                @Override
+                public boolean isVisibilityForced() {
+                    return false;
+                }
             };
 
     private final CoordinatorLayout.LayoutParams mControlContainerLayoutParams =
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ActionModeController.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ActionModeController.java
index 46b2f940..ccf224e 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ActionModeController.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ActionModeController.java
@@ -36,7 +36,7 @@
 
     /** Property for animating the top margin of ActionBarDelegate. */
     public static final IntProperty<ActionBarDelegate> TOP_MARGIN_ANIM_PROPERTY =
-            new IntProperty<ActionBarDelegate>("controlTopMargin") {
+            new IntProperty<>("controlTopMargin") {
                 @Override
                 public Integer get(ActionBarDelegate delegate) {
                     return delegate.getControlTopMargin();
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
index 99a443e..4b77abad 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
@@ -545,9 +545,10 @@
      * @param drawable The icon for the button.
      * @param description The content description for the button.
      * @param listener The {@link OnClickListener} to use for clicks to the button.
+     * @param {@link ButtonType} of the button.
      */
     protected void addCustomActionButton(
-            Drawable drawable, String description, OnClickListener listener) {
+            Drawable drawable, String description, OnClickListener listener, int type) {
         // This method should only be called for subclasses that override it.
         assert false;
     }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
index 70bc3b5..4e1798a 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
@@ -576,10 +576,11 @@
      * @param drawable The icon for the button.
      * @param description The content description for the button.
      * @param listener The {@link View.OnClickListener} to use for clicks to the button.
+     * @param {@link ButtonType} of the button.
      */
     public void addCustomActionButton(
-            Drawable drawable, String description, View.OnClickListener listener) {
-        mToolbarLayout.addCustomActionButton(drawable, description, listener);
+            Drawable drawable, String description, View.OnClickListener listener, int type) {
+        mToolbarLayout.addCustomActionButton(drawable, description, listener, type);
     }
 
     /**
diff --git a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionButtonModeIntegrationTest.java b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionButtonModeIntegrationTest.java
index c0e2ffa..0815b04 100644
--- a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionButtonModeIntegrationTest.java
+++ b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionButtonModeIntegrationTest.java
@@ -94,7 +94,7 @@
                 .check(matches(withText("Use a different account")));
 
         doAnswer(
-                        new Answer<Void>() {
+                        new Answer<>() {
                             @Override
                             public Void answer(InvocationOnMock invocation) {
                                 mAccountSelection.showAccounts(
@@ -145,7 +145,7 @@
                 .check(matches(withText("Use a different account")));
 
         doAnswer(
-                        new Answer<Void>() {
+                        new Answer<>() {
                             @Override
                             public Void answer(InvocationOnMock invocation) {
                                 mAccountSelection.showAccounts(
@@ -225,7 +225,7 @@
                 .check(matches(withText("Use a different account")));
 
         doAnswer(
-                        new Answer<Void>() {
+                        new Answer<>() {
                             @Override
                             public Void answer(InvocationOnMock invocation) {
                                 mAccountSelection.showAccounts(
diff --git a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionCoordinator.java b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionCoordinator.java
index a6d8bc8..8681189 100644
--- a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionCoordinator.java
+++ b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionCoordinator.java
@@ -330,7 +330,7 @@
 
     @Override
     public void setPopupComponent(AccountSelectionComponent component) {
-        mPopupComponent = new WeakReference<AccountSelectionComponent>(component);
+        mPopupComponent = new WeakReference<>(component);
     }
 
     // ActivityStateObserver
diff --git a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionIntegrationTest.java b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionIntegrationTest.java
index ccfd887..a849267 100644
--- a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionIntegrationTest.java
+++ b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionIntegrationTest.java
@@ -376,7 +376,7 @@
 
         // Close the use other account CCT when it is opened.
         doAnswer(
-                        new Answer<Void>() {
+                        new Answer<>() {
                             @Override
                             public Void answer(InvocationOnMock invocation) {
                                 mAccountSelection
diff --git a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionMediator.java b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionMediator.java
index 6ecc8f4..2c3509e 100644
--- a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionMediator.java
+++ b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionMediator.java
@@ -895,8 +895,7 @@
         boolean isSingleAccountChooser = accounts.size() == 1 && !accounts.get(0).isFilteredOut();
 
         // Check everything we need to render to determine if multiple IDPs are involved or not.
-        Set<IdentityProviderData> distinctIdps =
-                new HashSet<IdentityProviderData>(identityProviders);
+        Set<IdentityProviderData> distinctIdps = new HashSet<>(identityProviders);
         for (Account account : accounts) {
             distinctIdps.add(account.getIdentityProviderData());
         }
diff --git a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionViewBinder.java b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionViewBinder.java
index e6c84a5e..be87897 100644
--- a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionViewBinder.java
+++ b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionViewBinder.java
@@ -384,7 +384,7 @@
                     model.get(DataSharingConsentProperties.PROPERTIES);
 
             Context context = view.getContext();
-            ArrayList<String> fieldStrings = new ArrayList<String>();
+            ArrayList<String> fieldStrings = new ArrayList<>();
             for (@IdentityRequestDialogDisclosureField int field : properties.mDisclosureFields) {
                 switch (field) {
                     case IdentityRequestDialogDisclosureField.NAME:
diff --git a/chrome/browser/ui/android/whats_new/java/src/org/chromium/chrome/browser/ui/android/whats_new/WhatsNewCoordinator.java b/chrome/browser/ui/android/whats_new/java/src/org/chromium/chrome/browser/ui/android/whats_new/WhatsNewCoordinator.java
index 891f271..30ca783 100644
--- a/chrome/browser/ui/android/whats_new/java/src/org/chromium/chrome/browser/ui/android/whats_new/WhatsNewCoordinator.java
+++ b/chrome/browser/ui/android/whats_new/java/src/org/chromium/chrome/browser/ui/android/whats_new/WhatsNewCoordinator.java
@@ -44,7 +44,7 @@
         ModelListAdapter adapter = new ModelListAdapter(mModelList);
         adapter.registerType(
                 WhatsNewListItemProperties.DEFAULT_ITEM_TYPE,
-                new LayoutViewBuilder<WhatsNewListItemView>(R.layout.whats_new_list_item),
+                new LayoutViewBuilder<>(R.layout.whats_new_list_item),
                 WhatsNewListItemViewBinder::bind);
 
         ListView listView = mView.findViewById(R.id.whats_new_items_list);
diff --git a/chrome/browser/ui/autofill/autofill_ai/save_or_update_autofill_ai_data_controller_impl.cc b/chrome/browser/ui/autofill/autofill_ai/save_or_update_autofill_ai_data_controller_impl.cc
index 6b4e8a1..8244c17e 100644
--- a/chrome/browser/ui/autofill/autofill_ai/save_or_update_autofill_ai_data_controller_impl.cc
+++ b/chrome/browser/ui/autofill/autofill_ai/save_or_update_autofill_ai_data_controller_impl.cc
@@ -148,7 +148,7 @@
       return kNewEntityAttributeAdded;
     }
 
-    return std::ranges::all_of(new_entity_attribute.GetSupportedTypes(),
+    return std::ranges::all_of(new_entity_attribute.type().field_subtypes(),
                                [&](autofill::FieldType type) {
                                  return old_entity_attribute->GetInfo(
                                             type, app_locale_,
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index ebd7e0f..17734ad 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -34,6 +34,8 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/actor/actor_coordinator.h"
+#include "chrome/browser/actor/actor_keyed_service.h"
 #include "chrome/browser/app_mode/app_mode_utils.h"
 #include "chrome/browser/background/background_contents.h"
 #include "chrome/browser/background/background_contents_service.h"
@@ -461,6 +463,7 @@
 
 bool IsActorCoordinatorActingOnTab(Profile* profile,
                                    const content::WebContents* tab) {
+  // TODO(crbug.com/411462297): Delete this code.
 #if BUILDFLAG(ENABLE_GLIC)
   if (glic::GlicEnabling::IsEnabledByFlags()) {
     if (const auto* glic_service = glic::GlicKeyedService::Get(profile);
@@ -468,14 +471,15 @@
       return true;
     }
   }
-  // TODO(https://crbug.com/411462297): Deduplicate ownership of
-  // ActorCoordinators.
-  if (const auto* ai_data_service =
-          AiDataKeyedServiceFactory::GetAiDataKeyedService(profile);
-      ai_data_service && ai_data_service->IsActorCoordinatorActingOnTab(tab)) {
-    return true;
-  }
 #endif
+  auto* actor_service = actor::ActorKeyedService::Get(profile);
+  if (actor_service) {
+    for (auto& [task_id, task] : actor_service->GetTasks()) {
+      if (task->GetActorCoordinator()->HasTaskForTab(tab)) {
+        return true;
+      }
+    }
+  }
   return false;
 }
 
diff --git a/chrome/browser/ui/cocoa/touchbar/web_textfield_touch_bar_controller.mm b/chrome/browser/ui/cocoa/touchbar/web_textfield_touch_bar_controller.mm
index 4ee550b..14b739d 100644
--- a/chrome/browser/ui/cocoa/touchbar/web_textfield_touch_bar_controller.mm
+++ b/chrome/browser/ui/cocoa/touchbar/web_textfield_touch_bar_controller.mm
@@ -4,7 +4,6 @@
 
 #import "chrome/browser/ui/cocoa/touchbar/web_textfield_touch_bar_controller.h"
 
-#include "base/debug/stack_trace.h"
 #include "chrome/browser/ui/autofill/autofill_suggestion_controller.h"
 #import "chrome/browser/ui/cocoa/touchbar/browser_window_touch_bar_controller.h"
 #import "chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller.h"
diff --git a/chrome/browser/ui/content_settings/content_setting_image_model_unittest.cc b/chrome/browser/ui/content_settings/content_setting_image_model_unittest.cc
index e89a384..a70d3ac 100644
--- a/chrome/browser/ui/content_settings/content_setting_image_model_unittest.cc
+++ b/chrome/browser/ui/content_settings/content_setting_image_model_unittest.cc
@@ -90,6 +90,7 @@
         {features::kQuietNotificationPrompts,
 #if BUILDFLAG(IS_MAC)
          features::kAppShimNotificationAttribution,
+         features::kUseAdHocSigningForWebAppShims,
 #endif
          // Enable all sensors just to avoid hardcoding the expected messages
          // to the motion sensor-specific ones.
diff --git a/chrome/browser/ui/cookie_controls/cookie_controls_controller_unittest.cc b/chrome/browser/ui/cookie_controls/cookie_controls_controller_unittest.cc
index 201d4c7..d36e164b 100644
--- a/chrome/browser/ui/cookie_controls/cookie_controls_controller_unittest.cc
+++ b/chrome/browser/ui/cookie_controls/cookie_controls_controller_unittest.cc
@@ -247,32 +247,32 @@
 TEST_F(CookieControlsUserBypassTest, CookieBlockingChanged) {
   // Check that the controller correctly keeps track of whether the effective
   // cookie blocking setting for the page has been changed by
-  // `SetUserChangedCookieBlockingForSite`.
+  // `SetStateChangedViaBypass`.
   cookie_controls()->Update(web_contents());
   NavigateAndCommit(GURL(kUrl));
-  EXPECT_FALSE(cookie_controls()->HasUserChangedCookieBlockingForSite());
+  EXPECT_FALSE(cookie_controls()->StateChangedViaBypass());
 
-  cookie_controls()->SetUserChangedCookieBlockingForSite(false);
-  EXPECT_FALSE(cookie_controls()->HasUserChangedCookieBlockingForSite());
+  cookie_controls()->SetStateChangedViaBypass(false);
+  EXPECT_FALSE(cookie_controls()->StateChangedViaBypass());
 
-  cookie_controls()->SetUserChangedCookieBlockingForSite(true);
-  EXPECT_TRUE(cookie_controls()->HasUserChangedCookieBlockingForSite());
+  cookie_controls()->SetStateChangedViaBypass(true);
+  EXPECT_TRUE(cookie_controls()->StateChangedViaBypass());
 
   // Changing the toggle back should clear it.
-  cookie_controls()->SetUserChangedCookieBlockingForSite(true);
-  EXPECT_FALSE(cookie_controls()->HasUserChangedCookieBlockingForSite());
+  cookie_controls()->SetStateChangedViaBypass(true);
+  EXPECT_FALSE(cookie_controls()->StateChangedViaBypass());
 
   // Navigating to the same page should clear it.
-  cookie_controls()->SetUserChangedCookieBlockingForSite(true);
-  EXPECT_TRUE(cookie_controls()->HasUserChangedCookieBlockingForSite());
+  cookie_controls()->SetStateChangedViaBypass(true);
+  EXPECT_TRUE(cookie_controls()->StateChangedViaBypass());
   NavigateAndCommit(GURL(kUrl));
-  EXPECT_FALSE(cookie_controls()->HasUserChangedCookieBlockingForSite());
+  EXPECT_FALSE(cookie_controls()->StateChangedViaBypass());
 
   // Navigating to a different page should also clear it.
-  cookie_controls()->SetUserChangedCookieBlockingForSite(true);
-  EXPECT_TRUE(cookie_controls()->HasUserChangedCookieBlockingForSite());
+  cookie_controls()->SetStateChangedViaBypass(true);
+  EXPECT_TRUE(cookie_controls()->StateChangedViaBypass());
   NavigateAndCommit(GURL("https://thirdparty.com"));
-  EXPECT_FALSE(cookie_controls()->HasUserChangedCookieBlockingForSite());
+  EXPECT_FALSE(cookie_controls()->StateChangedViaBypass());
 }
 
 TEST_F(CookieControlsUserBypassTest, SiteCounts) {
@@ -1293,7 +1293,7 @@
   NavigateAndCommit(GURL(kUrl));
 
   // Loading a different page after making an effective change should not fire.
-  cookie_controls()->SetUserChangedCookieBlockingForSite(true);
+  cookie_controls()->SetStateChangedViaBypass(true);
   cookie_controls()->OnCookieBlockingEnabledForSite(false);
   ValidateCookieControlsActivatedUKM(
       /*fed_cm_initiated=*/false,
@@ -1307,7 +1307,7 @@
 
   // Observer should fire when reloaded after change.
   EXPECT_CALL(*mock(), OnFinishedPageReloadWithChangedSettings()).Times(2);
-  cookie_controls()->SetUserChangedCookieBlockingForSite(true);
+  cookie_controls()->SetStateChangedViaBypass(true);
   cookie_controls()->OnCookieBlockingEnabledForSite(false);
   ValidateCookieControlsActivatedUKM(
       /*fed_cm_initiated=*/false,
@@ -1317,7 +1317,7 @@
       ThirdPartySiteDataAccessType::kNoThirdPartySiteAccesses);
 
   NavigateAndCommit(GURL("https://example2.com"));
-  cookie_controls()->SetUserChangedCookieBlockingForSite(true);
+  cookie_controls()->SetStateChangedViaBypass(true);
   cookie_controls()->OnCookieBlockingEnabledForSite(true);
   NavigateAndCommit(GURL("https://example2.com"));
 }
diff --git a/chrome/browser/ui/page_info/chrome_page_info_ui_delegate.cc b/chrome/browser/ui/page_info/chrome_page_info_ui_delegate.cc
index 164e960..1227e16 100644
--- a/chrome/browser/ui/page_info/chrome_page_info_ui_delegate.cc
+++ b/chrome/browser/ui/page_info/chrome_page_info_ui_delegate.cc
@@ -223,8 +223,7 @@
       // ), however as we don't have any testcase for this branch, the changes
       // were refused by the test coverage bot.
       // TODO(b/345431801): Add a testcase to cover this case.
-      if (base::FeatureList::IsEnabled(
-              features::kAppShimNotificationAttribution)) {
+      if (web_app::UseNotificationAttributionForWebAppShims()) {
         // If this notification permission is associated with a locally
         // installed web app, the corresponding app shim needs to have system
         // level notification permission for notifications to work. If system
diff --git a/chrome/browser/ui/passwords/password_change_ui_controller.cc b/chrome/browser/ui/passwords/password_change_ui_controller.cc
deleted file mode 100644
index 092466cc..0000000
--- a/chrome/browser/ui/passwords/password_change_ui_controller.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright 2025 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/passwords/password_change_ui_controller.h"
-
-#include "base/functional/callback.h"
-#include "chrome/browser/ui/passwords/ui_utils.h"
-#include "chrome/browser/ui/tabs/public/tab_dialog_manager.h"
-#include "chrome/browser/ui/tabs/public/tab_features.h"
-#include "chrome/grit/generated_resources.h"
-#include "chrome/grit/theme_resources.h"
-#include "components/constrained_window/constrained_window_views.h"
-#include "components/tabs/public/tab_interface.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/models/dialog_model.h"
-#include "ui/views/bubble/bubble_dialog_model_host.h"
-
-namespace {
-
-// Whether a dialog should be displayed for a given `state`.
-bool ShouldDisplayDialog(PasswordChangeDelegate::State state) {
-  switch (state) {
-    case PasswordChangeDelegate::State::kOfferingPasswordChange:
-    case PasswordChangeDelegate::State::kPasswordChangeFailed:
-      return true;
-    case PasswordChangeDelegate::State::kWaitingForAgreement:
-    case PasswordChangeDelegate::State::kWaitingForChangePasswordForm:
-    case PasswordChangeDelegate::State::kChangePasswordFormNotFound:
-    case PasswordChangeDelegate::State::kChangingPassword:
-    case PasswordChangeDelegate::State::kPasswordSuccessfullyChanged:
-    case PasswordChangeDelegate::State::kOtpDetected:
-      return false;
-  }
-}
-
-// Creates dialog for `PasswordChangeDelegate::State::kOfferingPasswordChange`.
-std::unique_ptr<ui::DialogModel> CreateOfferChangePasswordDialog(
-    base::OnceClosure accept_callback) {
-  return ui::DialogModel::Builder()
-      .SetBannerImage(
-          ui::ImageModel::FromResourceId(IDR_PASSWORD_CHANGE_WARNING),
-          ui::ImageModel::FromResourceId(IDR_PASSWORD_CHANGE_WARNING_DARK))
-      .SetIcon(
-          ui::ImageModel::FromVectorIcon(GooglePasswordManagerVectorIcon()))
-      .SetTitle(l10n_util::GetStringUTF16(
-          IDS_PASSWORD_MANAGER_UI_PASSWORD_CHANGE_LEAK_BUBBLE_TITLE))
-      .AddParagraph(ui::DialogModelLabel(l10n_util::GetStringUTF16(
-          IDS_PASSWORD_MANAGER_UI_PASSWORD_CHANGE_LEAK_BUBBLE_DETAILS)))
-      .AddCancelButton(base::DoNothing(),
-                       ui::DialogModel::Button::Params().SetLabel(
-                           l10n_util::GetStringUTF16(IDS_NO_THANKS)))
-      .AddOkButton(
-          std::move(accept_callback),
-          ui::DialogModel::Button::Params().SetLabel(l10n_util::GetStringUTF16(
-              IDS_PASSWORD_MANAGER_UI_PASSWORD_CHANGE_CHANGE_PASSWORD)))
-      .Build();
-}
-
-// Creates dialog for `PasswordChangeDelegate::State::kPasswordChangeFailed`.
-std::unique_ptr<ui::DialogModel> CreatePasswordChangeFailedDialog(
-    base::OnceClosure accept_callback) {
-  return ui::DialogModel::Builder()
-      .SetBannerImage(
-          ui::ImageModel::FromResourceId(IDR_PASSWORD_CHANGE_WARNING),
-          ui::ImageModel::FromResourceId(IDR_PASSWORD_CHANGE_WARNING_DARK))
-      .SetTitle(l10n_util::GetStringUTF16(
-          IDS_PASSWORD_MANAGER_UI_PASSWORD_CHANGE_FAILED_TITLE))
-      .AddParagraph(ui::DialogModelLabel(l10n_util::GetStringUTF16(
-          IDS_PASSWORD_MANAGER_UI_PASSWORD_CHANGE_FAILED_BODY)))
-      .AddCancelButton(base::DoNothing(),
-                       ui::DialogModel::Button::Params().SetLabel(
-                           l10n_util::GetStringUTF16(IDS_CLOSE)))
-      .AddOkButton(
-          std::move(accept_callback),
-          ui::DialogModel::Button::Params().SetLabel(l10n_util::GetStringUTF16(
-              IDS_PASSWORD_MANAGER_UI_PASSWORD_CHANGE_FAILED_ACCEPT_BUTTON)))
-      .Build();
-}
-
-// Creates dialog for `state`.
-std::unique_ptr<ui::DialogModel> CreateDialog(
-    PasswordChangeDelegate::State state,
-    base::OnceClosure accept_callback) {
-  switch (state) {
-    case PasswordChangeDelegate::State::kOfferingPasswordChange:
-      return CreateOfferChangePasswordDialog(std::move(accept_callback));
-    case PasswordChangeDelegate::State::kPasswordChangeFailed:
-      return CreatePasswordChangeFailedDialog(std::move(accept_callback));
-    case PasswordChangeDelegate::State::kWaitingForAgreement:
-    case PasswordChangeDelegate::State::kWaitingForChangePasswordForm:
-    case PasswordChangeDelegate::State::kChangePasswordFormNotFound:
-    case PasswordChangeDelegate::State::kChangingPassword:
-    case PasswordChangeDelegate::State::kPasswordSuccessfullyChanged:
-    case PasswordChangeDelegate::State::kOtpDetected:
-      NOTREACHED();
-  }
-}
-
-}  // namespace
-
-PasswordChangeUIController::PasswordChangeUIController(
-    PasswordChangeDelegate* password_change_delegate,
-    base::WeakPtr<content::WebContents> web_contents)
-    : password_change_delegate_(password_change_delegate),
-      web_contents_(web_contents) {}
-
-PasswordChangeUIController::~PasswordChangeUIController() = default;
-
-void PasswordChangeUIController::UpdateState(
-    PasswordChangeDelegate::State state) {
-  if (state_ == state) {
-    return;
-  }
-
-  state_ = state;
-
-  // TODO(crbug.com/417389698): Handle other states.
-  if (ShouldDisplayDialog(state_)) {
-    tabs::TabInterface* tab_interface =
-        tabs::TabInterface::MaybeGetFromContents(web_contents_.get());
-    if (!tab_interface || !tab_interface->CanShowModalUI()) {
-      return;
-    }
-
-    base::OnceClosure accept_callback = base::BindOnce(
-        &PasswordChangeUIController::OnDialogAccepted, base::Unretained(this));
-    std::unique_ptr<views::BubbleDialogModelHost> model_host =
-        views::BubbleDialogModelHost::CreateModal(
-            CreateDialog(state_, std::move(accept_callback)),
-            ui::mojom::ModalType::kChild);
-    // TODO(crbug.com/338254375): Remove once it is a default state.
-    model_host->SetOwnershipOfNewWidget(
-        views::Widget::InitParams::CLIENT_OWNS_WIDGET);
-    tab_interface->GetTabFeatures()
-        ->tab_dialog_manager()
-        ->CreateAndShowDialog(
-            model_host.release(),
-            std::make_unique<tabs::TabDialogManager::Params>())
-        .release();
-    return;
-  }
-}
-
-void PasswordChangeUIController::OnDialogAccepted() {
-  CHECK(password_change_delegate_);
-
-  switch (state_) {
-    case PasswordChangeDelegate::State::kOfferingPasswordChange:
-      password_change_delegate_->StartPasswordChangeFlow();
-      return;
-    case PasswordChangeDelegate::State::kPasswordChangeFailed:
-      password_change_delegate_->OpenPasswordChangeTab();
-      password_change_delegate_->Stop();
-      return;
-    case PasswordChangeDelegate::State::kWaitingForAgreement:
-    case PasswordChangeDelegate::State::kWaitingForChangePasswordForm:
-    case PasswordChangeDelegate::State::kChangePasswordFormNotFound:
-    case PasswordChangeDelegate::State::kChangingPassword:
-    case PasswordChangeDelegate::State::kPasswordSuccessfullyChanged:
-    case PasswordChangeDelegate::State::kOtpDetected:
-      NOTREACHED();
-  }
-}
diff --git a/chrome/browser/ui/passwords/password_change_ui_controller.h b/chrome/browser/ui/passwords/password_change_ui_controller.h
deleted file mode 100644
index bd5de1d..0000000
--- a/chrome/browser/ui/passwords/password_change_ui_controller.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2025 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_PASSWORDS_PASSWORD_CHANGE_UI_CONTROLLER_H_
-#define CHROME_BROWSER_UI_PASSWORDS_PASSWORD_CHANGE_UI_CONTROLLER_H_
-
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/password_manager/password_change_delegate.h"
-
-namespace content {
-class WebContents;
-}  // namespace content
-
-// Responsible for creating and displaying appropriate views based on the
-// current state of the password change flow.
-class PasswordChangeUIController {
- public:
-  explicit PasswordChangeUIController(
-      PasswordChangeDelegate* password_change_delegate,
-      base::WeakPtr<content::WebContents> web_contents);
-  ~PasswordChangeUIController();
-
-  // Updates the `state_` and the UI.
-  void UpdateState(PasswordChangeDelegate::State state);
-
- private:
-  // Handles clicking accept button on the currently displayed dialog.
-  void OnDialogAccepted();
-
-  // Controls password change process. Owns this class.
-  const raw_ptr<PasswordChangeDelegate> password_change_delegate_;
-
-  base::WeakPtr<content::WebContents> web_contents_;
-
-  // Current state of the password change flow.
-  PasswordChangeDelegate::State state_;
-};
-
-#endif  // CHROME_BROWSER_UI_PASSWORDS_PASSWORD_CHANGE_UI_CONTROLLER_H_
diff --git a/chrome/browser/ui/prefs/BUILD.gn b/chrome/browser/ui/prefs/BUILD.gn
index c208487b6..b8b444d 100644
--- a/chrome/browser/ui/prefs/BUILD.gn
+++ b/chrome/browser/ui/prefs/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//extensions/buildflags/buildflags.gni")
+
 assert(is_win || is_mac || is_linux || is_chromeos || is_android)
 
 source_set("prefs") {
@@ -58,7 +60,7 @@
   }
 }
 
-if (!is_android) {
+if (!is_android || enable_desktop_android_extensions) {
   source_set("browser_tests") {
     testonly = true
     defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
@@ -71,10 +73,15 @@
       "//chrome/common:constants",
       "//chrome/common:non_code_constants",
       "//chrome/test:test_support",
-      "//chrome/test:test_support_ui",
       "//components/prefs",
       "//content/test:test_support",
       "//third_party/blink/public/common:headers",
     ]
+
+    if (enable_desktop_android_extensions) {
+      deps += [ "//chrome/test:test_support_ui_android" ]
+    } else {
+      deps += [ "//chrome/test:test_support_ui" ]
+    }
   }
 }
diff --git a/chrome/browser/ui/prefs/prefs_tab_helper.cc b/chrome/browser/ui/prefs/prefs_tab_helper.cc
index 644e00a..2e71022 100644
--- a/chrome/browser/ui/prefs/prefs_tab_helper.cc
+++ b/chrome/browser/ui/prefs/prefs_tab_helper.cc
@@ -22,6 +22,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/single_thread_task_runner.h"
+#include "build/android_buildflags.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/font_pref_change_notifier_factory.h"
@@ -66,7 +67,8 @@
 #include <windows.h>
 #endif
 
-#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || \
+    BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS)
 // If a font name in prefs default values starts with a comma, consider it's a
 // comma-separated font list and resolve it to the first available font.
 #define PREFS_FONT_LIST 1
@@ -80,16 +82,16 @@
 
 namespace {
 
-#if !BUILDFLAG(IS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS)
 // Registers a preference under the path |pref_name| for each script used for
 // per-script font prefs.
 // For example, for WEBKIT_WEBPREFS_FONTS_SERIF ("fonts.serif"):
 // "fonts.serif.Arab", "fonts.serif.Hang", etc. are registered.
 // |fonts_with_defaults| contains all |pref_names| already registered since they
 // have a specified default value.
-// On Android there are no default values for these properties and there is no
-// way to set them (because extensions are not supported so the Font Settings
-// API cannot be used), so we can avoid registering them altogether.
+// On non-desktop Android there are no default values for these properties and
+// there is no way to set them (because extensions are not supported so the
+// Font Settings API cannot be used), so we can avoid registering them.
 void RegisterFontFamilyPrefs(user_prefs::PrefRegistrySyncable* registry,
                              const std::set<std::string>& fonts_with_defaults) {
   // Expand the font concatenated with script name so this stays at RO memory
@@ -152,7 +154,8 @@
     {prefs::kWebKitCursiveFontFamily, IDS_CURSIVE_FONT_FAMILY},
     {prefs::kWebKitFantasyFontFamily, IDS_FANTASY_FONT_FAMILY},
     {prefs::kWebKitMathFontFamily, IDS_MATH_FONT_FAMILY},
-#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
+#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || \
+    BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS)
     {prefs::kWebKitStandardFontFamilyJapanese,
      IDS_STANDARD_FONT_FAMILY_JAPANESE},
     {prefs::kWebKitFixedFontFamilyJapanese, IDS_FIXED_FONT_FAMILY_JAPANESE},
@@ -291,7 +294,7 @@
   (*map)[script] = base::UTF8ToUTF16(pref_value);
 }
 
-#if !BUILDFLAG(IS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS)
 void RegisterLocalizedFontPref(user_prefs::PrefRegistrySyncable* registry,
                                const char* path,
                                int default_message_id) {
@@ -445,7 +448,7 @@
   }
 
 // Register font prefs.  This is only configurable on desktop Chrome.
-#if !BUILDFLAG(IS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS)
   RegisterFontFamilyPrefs(registry, fonts_with_defaults);
 
   registry->RegisterIntegerPref(prefs::kWebKitDefaultFontSize, 16);
diff --git a/chrome/browser/ui/prefs/prefs_tab_helper_browsertest.cc b/chrome/browser/ui/prefs/prefs_tab_helper_browsertest.cc
index c1e104d..f3fce1f 100644
--- a/chrome/browser/ui/prefs/prefs_tab_helper_browsertest.cc
+++ b/chrome/browser/ui/prefs/prefs_tab_helper_browsertest.cc
@@ -6,27 +6,33 @@
 #include "base/path_service.h"
 #include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/pref_names.h"
-#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/chrome_test_utils.h"
+#include "chrome/test/base/platform_browser_test.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/prefs/pref_service.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
+#include "extensions/buildflags/buildflags.h"
 #include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
-class PrefsTabHelperBrowserTest : public InProcessBrowserTest {
- protected:
-  virtual base::FilePath GetPreferencesFilePath() {
-    base::FilePath test_data_directory;
-    base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory);
-    return test_data_directory.AppendASCII("profiles")
-        .AppendASCII("web_prefs")
-        .AppendASCII("Default")
-        .Append(chrome::kPreferencesFilename);
-  }
+namespace {
 
+base::FilePath GetPreferencesFilePath() {
+  base::FilePath test_data_directory;
+  base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory);
+  return test_data_directory.AppendASCII("profiles")
+      .AppendASCII("web_prefs")
+      .AppendASCII("Default")
+      .Append(chrome::kPreferencesFilename);
+}
+
+}  // namespace
+
+class PrefsTabHelperBrowserTest : public PlatformBrowserTest {
+ protected:
   bool SetUpUserDataDirectory() override {
     base::FilePath user_data_directory;
     base::PathService::Get(chrome::DIR_USER_DATA, &user_data_directory);
@@ -64,7 +70,7 @@
 // Tests that a sampling of web prefs are registered and ones with values in the
 // test user preferences file take on those values.
 IN_PROC_BROWSER_TEST_F(PrefsTabHelperBrowserTest, WebPrefs) {
-  PrefService* prefs = browser()->profile()->GetPrefs();
+  PrefService* prefs = chrome_test_utils::GetProfile(this)->GetPrefs();
 
   EXPECT_TRUE(
       prefs->FindPreference(prefs::kWebKitCursiveFontFamily)->IsDefaultValue());
@@ -82,11 +88,12 @@
 }
 
 // Tests that changes in browser preferences are reflected in Blink's web
-// preferences. Note that these preferences are not handled on Android, see
-// http://crbug.com/308033.
-#if !BUILDFLAG(IS_ANDROID)
+// preferences. Note that these preferences are not handled on non-desktop
+// Android, see http://crbug.com/308033, but can be modified by extension APIs
+// on desktop Android.
+#if !BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS)
 IN_PROC_BROWSER_TEST_F(PrefsTabHelperBrowserTest, GenericFontFamilies) {
-  PrefService* prefs = browser()->profile()->GetPrefs();
+  PrefService* prefs = chrome_test_utils::GetProfile(this)->GetPrefs();
   prefs->SetString(prefs::kWebKitStandardFontFamily, "CustomStandard");
   prefs->SetString(prefs::kWebKitSerifFontFamily, "CustomSerif");
   prefs->SetString(prefs::kWebKitSansSerifFontFamily, "CustomSansSerif");
@@ -96,7 +103,7 @@
   prefs->SetString(prefs::kWebKitMathFontFamily, "CustomMath");
 
   content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
+      chrome_test_utils::GetActiveWebContents(this);
   web_contents->NotifyPreferencesChanged();
   blink::web_pref::WebPreferences web_prefs =
       web_contents->GetOrCreateWebPreferences();
diff --git a/chrome/browser/ui/safety_hub/menu_notification_service_unittest.cc b/chrome/browser/ui/safety_hub/menu_notification_service_unittest.cc
index 7142c955..9f86379 100644
--- a/chrome/browser/ui/safety_hub/menu_notification_service_unittest.cc
+++ b/chrome/browser/ui/safety_hub/menu_notification_service_unittest.cc
@@ -59,7 +59,6 @@
     feature_list_.InitWithFeatures(
         {
             features::kSafetyHub,
-            safe_browsing::kSafetyHubAbusiveNotificationRevocation,
 #if BUILDFLAG(IS_ANDROID)
             features::kSafetyHubFollowup,
 #endif  // BUILDFLAG(IS_ANDROID)
@@ -441,243 +440,3 @@
   EXPECT_FALSE(
       menu_notification_service()->GetNotificationToShow().has_value());
 }
-
-// TODO(crbug.com/328773301): Remove after
-// SafetyHubAbusiveNotificationRevocation is launched.
-class
-    SafetyHubMenuNotificationServiceTestDisableAutoAbusiveNotificationRevocation
-    : public SafetyHubMenuNotificationServiceTest {
- public:
-  void SetUp() override {
-    ChromeRenderViewHostTestHarness::SetUp();
-    feature_list_.InitWithFeatures(
-        {features::kSafetyHub},
-        {safe_browsing::kSafetyHubAbusiveNotificationRevocation});
-    prefs()->SetBoolean(
-        safety_hub_prefs::kUnusedSitePermissionsRevocationEnabled, true);
-
-    safety_hub_test_util::CreateRevokedPermissionsService(profile());
-    safety_hub_test_util::CreateNotificationPermissionsReviewService(profile());
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-TEST_F(
-    SafetyHubMenuNotificationServiceTestDisableAutoAbusiveNotificationRevocation,
-    TwoNotificationsSequentially) {
-  // Creating a mock result, which should result in a notification to be
-  // available.
-  CreateMockUnusedSitePermissionsEntry("https://example1.com:443");
-
-  // Show the notification sufficient days and times.
-  std::optional<MenuNotificationEntry> notification;
-  for (int i = 0; i < kSafetyHubMenuNotificationMinImpressionCount; ++i) {
-    notification = menu_notification_service()->GetNotificationToShow();
-    EXPECT_TRUE(notification.has_value());
-    ExpectPluralString(
-        IDS_SETTINGS_SAFETY_HUB_UNUSED_SITE_PERMISSIONS_MENU_NOTIFICATION, 1,
-        notification->label);
-  }
-  AdvanceClockBy(kSafetyHubMenuNotificationMinNotificationDuration);
-
-  // The notification has been shown sufficiently, so shouldn't be shown again.
-  notification = menu_notification_service()->GetNotificationToShow();
-  EXPECT_FALSE(notification.has_value());
-
-  CreateMockNotificationPermissionEntry();
-  notification = menu_notification_service()->GetNotificationToShow();
-  EXPECT_TRUE(notification.has_value());
-}
-
-#if !BUILDFLAG(IS_ANDROID)
-// TODO(crbug.com/328773301): Remove after
-// SafetyHubAbusiveNotificationRevocation is launched.
-class SafetyHubMenuNotificationServiceDesktopOnlyTest
-    : public SafetyHubMenuNotificationServiceTest {
- public:
-  void SetUp() override {
-    SafetyHubMenuNotificationServiceTest::SetUp();
-
-    password_store_ = CreateAndUseTestPasswordStore(profile());
-    PasswordStatusCheckService* password_service =
-        safety_hub_test_util::CreateAndUsePasswordStatusService(profile());
-
-    safety_hub_test_util::CreateRevokedPermissionsService(profile());
-    safety_hub_test_util::CreateNotificationPermissionsReviewService(profile());
-
-    RunUntilIdle();
-    EXPECT_EQ(password_service->compromised_credential_count(), 0UL);
-  }
-
-  void SetMockCredentialEntry(const std::string origin,
-                              bool leaked,
-                              bool update = false) {
-    // Create a password form and mark it as leaked.
-    password_manager::PasswordForm form;
-    form.username_value = u"username";
-    form.password_value = u"password";
-    form.signon_realm = origin;
-    form.url = GURL(origin);
-
-    if (leaked) {
-      form.password_issues.insert_or_assign(
-          password_manager::InsecureType::kLeaked,
-          password_manager::InsecurityMetadata(
-              base::Time::Now(), password_manager::IsMuted(false),
-              password_manager::TriggerBackendNotification(false)));
-    }
-
-    if (update) {
-      password_store().UpdateLogin(form);
-    } else {
-      password_store().AddLogin(form);
-    }
-    RunUntilIdle();
-  }
-
-  SafetyHubHatsService* safety_hub_hats_service() {
-    return SafetyHubHatsServiceFactory::GetForProfile(profile());
-  }
-  PasswordStatusCheckService* password_status_check_service() {
-    return PasswordStatusCheckServiceFactory::GetForProfile(profile());
-  }
-  extensions::CWSInfoService* extension_info_service() {
-    return extensions::CWSInfoService::Get(profile());
-  }
-  password_manager::TestPasswordStore& password_store() {
-    return *password_store_;
-  }
-
-  void CreateMockCWSInfoService() {
-    extensions::CWSInfoServiceFactory::GetInstance()->SetTestingFactory(
-        profile(), base::BindRepeating([](content::BrowserContext* context)
-                                           -> std::unique_ptr<KeyedService> {
-          return safety_hub_test_util::GetMockCWSInfoService(
-              Profile::FromBrowserContext(context));
-        }));
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-  scoped_refptr<password_manager::TestPasswordStore> password_store_;
-};
-
-TEST_F(SafetyHubMenuNotificationServiceDesktopOnlyTest,
-       ExtensionsMenuNotification) {
-  // Create mock extensions that should result in two violations that are shown
-  // in the menu notification.
-  safety_hub_test_util::CreateMockExtensions(profile());
-  // Create a mock CWS info service that will return the information that
-  // matches the mock extensions.
-  CreateMockCWSInfoService();
-  // Create a menu notification service with the mocked CWS info service.
-  std::unique_ptr<SafetyHubMenuNotificationService> mocked_service =
-      std::make_unique<SafetyHubMenuNotificationService>(
-          prefs(), revoked_permissions_service(),
-          notification_permissions_service(), password_status_check_service(),
-          safety_hub_hats_service(), profile());
-  std::optional<MenuNotificationEntry> notification =
-      mocked_service->GetNotificationToShow();
-  EXPECT_TRUE(notification.has_value());
-  ExpectPluralString(IDS_SETTINGS_SAFETY_HUB_EXTENSIONS_MENU_NOTIFICATION, 2,
-                     notification->label);
-}
-
-TEST_F(SafetyHubMenuNotificationServiceDesktopOnlyTest, PasswordOverride) {
-  const std::string origin = "https://www.example.com";
-  std::optional<MenuNotificationEntry> notification;
-  // Show Safe Browsing notification.
-  prefs()->SetBoolean(prefs::kSafeBrowsingEnabled, false);
-  notification = menu_notification_service()->GetNotificationToShow();
-  AdvanceClockBy(base::Days(1));
-  notification = menu_notification_service()->GetNotificationToShow();
-  EXPECT_TRUE(notification.has_value());
-
-  // A leaked password warning should override the safe browsing notification.
-  SetMockCredentialEntry(origin, true);
-  notification = menu_notification_service()->GetNotificationToShow();
-  EXPECT_TRUE(notification.has_value());
-  ExpectPluralString(
-      IDS_SETTINGS_SAFETY_HUB_COMPROMISED_PASSWORDS_MENU_NOTIFICATION, 1,
-      notification.value().label);
-
-  // Fixing the leaked password will clear notification. Because the safe
-  // browsing notification was dismissed, it will not be shown either.
-  SetMockCredentialEntry(origin, false, true);
-  notification = menu_notification_service()->GetNotificationToShow();
-  EXPECT_FALSE(notification.has_value());
-}
-
-TEST_F(SafetyHubMenuNotificationServiceDesktopOnlyTest, PasswordTrigger) {
-  const std::string& origin = "https://www.example1.com";
-  // A leaked password warning should create a password notification.
-  std::optional<MenuNotificationEntry> notification;
-  SetMockCredentialEntry(origin, true);
-  notification = menu_notification_service()->GetNotificationToShow();
-  EXPECT_TRUE(notification.has_value());
-  ExpectPluralString(
-      IDS_SETTINGS_SAFETY_HUB_COMPROMISED_PASSWORDS_MENU_NOTIFICATION, 1,
-      notification.value().label);
-
-  // Fixing the leaked password will clear notification.
-  SetMockCredentialEntry(origin, false, true);
-  notification = menu_notification_service()->GetNotificationToShow();
-  EXPECT_FALSE(notification.has_value());
-}
-
-TEST_F(SafetyHubMenuNotificationServiceDesktopOnlyTest,
-       DismissPasswordNotification) {
-  std::optional<MenuNotificationEntry> notification;
-  // Create mock password menu notification.
-  const std::string& kOrigin = "https://www.example.com";
-  SetMockCredentialEntry(kOrigin, true);
-  notification = menu_notification_service()->GetNotificationToShow();
-  EXPECT_TRUE(notification.has_value());
-  ExpectPluralString(
-      IDS_SETTINGS_SAFETY_HUB_COMPROMISED_PASSWORDS_MENU_NOTIFICATION, 1,
-      notification.value().label);
-
-  // The notification should no longer appear after it has been dismissed.
-  menu_notification_service()->DismissActiveNotificationOfModule(
-      safety_hub::SafetyHubModuleType::PASSWORDS);
-  notification = menu_notification_service()->GetNotificationToShow();
-  EXPECT_FALSE(notification.has_value());
-}
-
-TEST_F(SafetyHubMenuNotificationServiceDesktopOnlyTest, PasswordMigration) {
-  const std::string& kOrigin = "https://www.example1.com";
-  // Add a leaked password to make prefs store password data.
-  SetMockCredentialEntry(kOrigin, true);
-  EXPECT_TRUE(menu_notification_service()->GetNotificationToShow().has_value());
-
-  // Modify stored password data on prefs to test migration from old state to
-  // new state.
-  const base::Value::Dict& stored_notifications =
-      prefs()->GetDict(safety_hub_prefs::kMenuNotificationsPrefsKey);
-  base::Value::Dict new_stored_notification(stored_notifications.Clone());
-  const base::Value::Dict* stored_password_data =
-      new_stored_notification.FindDict("passwords");
-
-  // Store notification password as its old format by only passing origin.
-  base::Value::Dict old_password_not_value;
-  base::Value::List compromised_origins;
-  compromised_origins.Append(kOrigin);
-  old_password_not_value.Set(safety_hub::kSafetyHubPasswordCheckOriginsKey,
-                             std::move(compromised_origins));
-  base::Value::Dict new_stored_password_data(stored_password_data->Clone());
-  new_stored_password_data.Set("result", std::move(old_password_not_value));
-
-  // Update the value stored on prefs
-  new_stored_notification.Set("passwords", std::move(new_stored_password_data));
-  prefs()->SetDict(safety_hub_prefs::kMenuNotificationsPrefsKey,
-                   std::move(new_stored_notification));
-  EXPECT_TRUE(menu_notification_service()->GetNotificationToShow().has_value());
-
-  // Adding a new notification with the new format should keep showing the
-  // notification.
-  SetMockCredentialEntry(kOrigin, true);
-  EXPECT_TRUE(menu_notification_service()->GetNotificationToShow().has_value());
-}
-#endif  // !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/ui/safety_hub/revoked_permissions_service.cc b/chrome/browser/ui/safety_hub/revoked_permissions_service.cc
index 564e298..b8a3005 100644
--- a/chrome/browser/ui/safety_hub/revoked_permissions_service.cc
+++ b/chrome/browser/ui/safety_hub/revoked_permissions_service.cc
@@ -351,10 +351,7 @@
     return std::u16string();
   }
   return l10n_util::GetPluralStringFUTF16(
-      base::FeatureList::IsEnabled(
-          safe_browsing::kSafetyHubAbusiveNotificationRevocation)
-          ? IDS_SETTINGS_SAFETY_HUB_REVOKED_PERMISSIONS_MENU_NOTIFICATION
-          : IDS_SETTINGS_SAFETY_HUB_UNUSED_SITE_PERMISSIONS_MENU_NOTIFICATION,
+      IDS_SETTINGS_SAFETY_HUB_REVOKED_PERMISSIONS_MENU_NOTIFICATION,
       revoked_permissions_.size());
 }
 
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.cc b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.cc
index 25e6c63..1822d79 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.cc
@@ -287,7 +287,10 @@
                                   kColorExtensionsMenuSecondaryText)
                               .SetProperty(views::kMarginsKey,
                                            gfx::Insets::TLBR(
-                                               0, horizontal_spacing, 0, 0))),
+                                               0, horizontal_spacing, 0, 0))
+                              .SetElideBehavior(gfx::ELIDE_TAIL)
+                              .SetProperty(views::kFlexBehaviorKey,
+                                stretch_specification)),
                   // Close button.
                   views::Builder<views::Button>(
                       views::BubbleFrameView::CreateCloseButton(
@@ -443,5 +446,10 @@
   return site_access_buttons[GetSiteAccessButtonIndex(site_access)];
 }
 
+views::Label*
+ExtensionsMenuSitePermissionsPageView::GetExtensionNameForTesting() {
+  return extension_name_;
+}
+
 BEGIN_METADATA(ExtensionsMenuSitePermissionsPageView)
 END_METADATA
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.h b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.h
index f96c3c4..051ce0f 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.h
+++ b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.h
@@ -58,6 +58,7 @@
   }
   views::RadioButton* GetSiteAccessButtonForTesting(
       extensions::PermissionsManager::UserSiteAccess site_access);
+  views::Label* GetExtensionNameForTesting();
 
  private:
   const raw_ptr<Browser> browser_;
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view_unittest.cc b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view_unittest.cc
index 6095ff7..317d795 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view_unittest.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view_unittest.cc
@@ -169,6 +169,23 @@
   EXPECT_TRUE(IsMainPageOpened());
 }
 
+// Tests that the extension name is elided if it is too long.
+TEST_F(ExtensionsSitePermissionsPageViewUnitTest, LongExtensionNameIsElided) {
+  std::string long_name =
+      "A very very very very very very very very long extension name";
+  auto extension =
+      InstallExtensionWithHostPermissions(long_name, {"<all_urls>"});
+
+  NavigateAndCommit("http://www.url.com");
+  ShowSitePermissionsPage(extension->id());
+  EXPECT_TRUE(IsSitePermissionsPageOpened(extension->id()));
+
+  views::Label* extension_name_label =
+      site_permissions_page()->GetExtensionNameForTesting();
+  ASSERT_TRUE(extension_name_label);
+  EXPECT_EQ(extension_name_label->GetElideBehavior(), gfx::ELIDE_TAIL);
+}
+
 // Tests that menu navigates back to the main page when an extension, whose site
 // permissions page is open, is disabled.
 TEST_F(ExtensionsSitePermissionsPageViewUnitTest, DisableAndEnableExtension) {
diff --git a/chrome/browser/ui/views/frame/browser_frame.cc b/chrome/browser/ui/views/frame/browser_frame.cc
index 1556caf1..0228bd9f 100644
--- a/chrome/browser/ui/views/frame/browser_frame.cc
+++ b/chrome/browser/ui/views/frame/browser_frame.cc
@@ -12,6 +12,7 @@
 #include "base/debug/leak_annotations.h"
 #include "base/functional/bind.h"
 #include "base/i18n/rtl.h"
+#include "base/metrics/user_metrics.h"
 #include "build/build_config.h"
 #include "chrome/browser/app_mode/app_mode_utils.h"
 #include "chrome/browser/headless/headless_mode_util.h"
@@ -420,6 +421,7 @@
     menu_runner_->RunMenuAt(source->GetWidget(), nullptr,
                             gfx::Rect(p, gfx::Size(0, 0)),
                             views::MenuAnchorPosition::kTopLeft, source_type);
+    base::RecordAction(base::UserMetricsAction("SystemContextMenu_Opened"));
   }
 }
 
diff --git a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_browsertest.cc b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_browsertest.cc
index b923878..22915cf 100644
--- a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_browsertest.cc
@@ -122,6 +122,10 @@
     view_controller()->OnToggleButtonPressed(new_value);
   }
 
+  void SimulateTrackingProtectionsButtonPress() {
+    view_controller()->OnTrackingProtectionsButtonPressed();
+  }
+
   void CheckCookiesException(const GURL& first_party_url, bool should_exist) {
     content_settings::SettingInfo info;
     EXPECT_EQ(host_content_settings_map()->GetContentSetting(
@@ -182,43 +186,6 @@
   CheckCookiesException(third_party_cookie_page_url(), /*should_exist=*/false);
 }
 
-class TrackingProtectionBubbleViewBrowserTest
-    : public CookieControlsBubbleViewBrowserTest {
- public:
-  TrackingProtectionBubbleViewBrowserTest() {
-    https_server_ = std::make_unique<net::EmbeddedTestServer>(
-        net::EmbeddedTestServer::TYPE_HTTPS);
-    // Enable FPP to display UB UX with ACT features
-    feature_list_.InitWithFeatures(
-        {privacy_sandbox::kActUserBypassUx,
-         privacy_sandbox::kFingerprintingProtectionUx},
-        {});
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-  std::unique_ptr<net::EmbeddedTestServer> https_server_;
-};
-
-IN_PROC_BROWSER_TEST_F(TrackingProtectionBubbleViewBrowserTest,
-                       ToggleCreatesTrackingProtectionException) {
-  // Set FPP pref to display UB UX with ACT feature.
-  // Note: this value is set in the incognito profile for testing, in reality
-  // this will be set on the regular profile.
-  incognito_profile()->GetPrefs()->SetBoolean(
-      prefs::kFingerprintingProtectionEnabled, true);
-  auto* tracking_protection_settings =
-      TrackingProtectionSettingsFactory::GetForProfile(incognito_profile());
-  ShowIncognitoBubble();
-  EXPECT_FALSE(tracking_protection_settings->HasTrackingProtectionException(
-      third_party_cookie_page_url()));
-  SimulateTogglePress(false);
-  EXPECT_TRUE(tracking_protection_settings->HasTrackingProtectionException(
-      third_party_cookie_page_url()));
-  SimulateTogglePress(true);
-  EXPECT_FALSE(tracking_protection_settings->HasTrackingProtectionException(
-      third_party_cookie_page_url()));
-}
 
 IN_PROC_BROWSER_TEST_F(CookieControlsBubbleViewBrowserTest,
                        HidingControlsClosesBubble) {
@@ -305,3 +272,39 @@
 
   WaitForBubbleClose();
 }
+
+class TrackingProtectionBubbleViewBrowserTest
+    : public CookieControlsBubbleViewBrowserTest {
+ public:
+  TrackingProtectionBubbleViewBrowserTest() {
+    https_server_ = std::make_unique<net::EmbeddedTestServer>(
+        net::EmbeddedTestServer::TYPE_HTTPS);
+    // Enable FPP to display UB UX with ACT features
+    feature_list_.InitWithFeatures(
+        {privacy_sandbox::kActUserBypassUx,
+         privacy_sandbox::kFingerprintingProtectionUx},
+        {});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+  std::unique_ptr<net::EmbeddedTestServer> https_server_;
+};
+
+IN_PROC_BROWSER_TEST_F(
+    TrackingProtectionBubbleViewBrowserTest,
+    TrackingProtectionsButtonUpdatesTrackingProtectionException) {
+  auto* tracking_protection_settings =
+      TrackingProtectionSettingsFactory::GetForProfile(incognito_profile());
+  ShowIncognitoBubble();
+  EXPECT_FALSE(tracking_protection_settings->HasTrackingProtectionException(
+      third_party_cookie_page_url()));
+  SimulateTrackingProtectionsButtonPress();
+  EXPECT_TRUE(tracking_protection_settings->HasTrackingProtectionException(
+      third_party_cookie_page_url()));
+  // Reset reloading state before pressing button again.
+  view_controller()->SetIsReloadingState(false);
+  SimulateTrackingProtectionsButtonPress();
+  EXPECT_FALSE(tracking_protection_settings->HasTrackingProtectionException(
+      third_party_cookie_page_url()));
+}
diff --git a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_controller.cc b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_controller.cc
index a546c01..4edc48d 100644
--- a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_controller.cc
+++ b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_controller.cc
@@ -80,7 +80,7 @@
 }
 
 void CookieControlsBubbleViewController::OnUserTriggeredReloadingAction() {
-  if (!controller_->HasUserChangedCookieBlockingForSite()) {
+  if (!controller_->StateChangedViaBypass()) {
     controller_observation_.Reset();
     bubble_view_->CloseWidget();
     return;
@@ -164,7 +164,6 @@
 void CookieControlsBubbleViewController::FillViewForThirdPartyCookies(
     CookieControlsEnforcement enforcement,
     base::Time expiration) {
-  // TODO(crbug.com/388294499): Add support for ACT UI separately.
   bool tpcs_allowed = controls_state_ == CookieControlsState::kAllowed3pc ||
                       controls_state_ == CookieControlsState::kPausedTp;
   if (tpcs_allowed) {
@@ -172,6 +171,7 @@
   } else {
     ApplyThirdPartyCookiesBlockedState();
   }
+  bubble_view_->GetContentView()->SetTrackingProtectionsButtonVisible(false);
   bubble_view_->GetContentView()->SetToggleIsOn(tpcs_allowed);
   bubble_view_->GetContentView()->SetToggleIcon(GetToggleIcon(tpcs_allowed));
   switch (enforcement) {
@@ -202,6 +202,29 @@
   bubble_view_->GetContentView()->PreferredSizeChanged();
 }
 
+void CookieControlsBubbleViewController::FillViewForTrackingProtections() {
+  bool tp_paused = controls_state_ == CookieControlsState::kPausedTp;
+  int desc_title, desc, button_label;
+  if (tp_paused) {
+    desc_title = IDS_TRACKING_PROTECTIONS_BUBBLE_PAUSED_PROTECTIONS_TITLE;
+    desc = IDS_TRACKING_PROTECTIONS_BUBBLE_PAUSED_PROTECTIONS_DESCRIPTION;
+    button_label = IDS_TRACKING_PROTECTIONS_BUBBLE_RESUME_PROTECTIONS_LABEL;
+  } else {
+    desc_title = IDS_COOKIE_CONTROLS_BUBBLE_SITE_NOT_WORKING_TITLE;
+    desc = IDS_TRACKING_PROTECTIONS_BUBBLE_ACTIVE_PROTECTIONS_DESCRIPTION;
+    button_label = IDS_TRACKING_PROTECTIONS_BUBBLE_PAUSE_PROTECTIONS_LABEL;
+  }
+  bubble_view_->GetContentView()->SetCookiesRowVisible(false);
+  bubble_view_->UpdateTitle(
+      l10n_util::GetStringUTF16(IDS_INCOGNITO_TRACKING_PROTECTIONS_HEADER));
+  bubble_view_->GetContentView()->UpdateContentLabels(
+      l10n_util::GetStringUTF16(desc_title), l10n_util::GetStringUTF16(desc));
+  bubble_view_->GetContentView()->SetTrackingProtectionsButtonLabel(
+      l10n_util::GetStringUTF16(button_label));
+  bubble_view_->GetContentView()->SetFeedbackSectionVisibility(tp_paused);
+  bubble_view_->GetContentView()->PreferredSizeChanged();
+}
+
 CookieControlsBubbleViewController::~CookieControlsBubbleViewController() =
     default;
 
@@ -217,11 +240,19 @@
   }
   blocking_status_ = blocking_status;
   controls_state_ = controls_state;
-  if (controls_state_ == CookieControlsState::kHidden) {
-    bubble_view_->CloseWidget();
-    return;
+  switch (controls_state_) {
+    case CookieControlsState::kHidden:
+      bubble_view_->CloseWidget();
+      return;
+    case CookieControlsState::kActiveTp:
+    case CookieControlsState::kPausedTp:
+      FillViewForTrackingProtections();
+      break;
+    case CookieControlsState::kBlocked3pc:
+    case CookieControlsState::kAllowed3pc:
+      FillViewForThirdPartyCookies(enforcement, expiration);
+      break;
   }
-  FillViewForThirdPartyCookies(enforcement, expiration);
 }
 
 void CookieControlsBubbleViewController::
@@ -260,6 +291,13 @@
               &CookieControlsBubbleViewController::OnToggleButtonPressed,
               base::Unretained(this)));
 
+  tracking_protections_button_callback_ =
+      bubble_view_->GetContentView()
+          ->RegisterTrackingProtectionsButtonPressedCallback(
+              base::BindRepeating(&CookieControlsBubbleViewController::
+                                      OnTrackingProtectionsButtonPressed,
+                                  base::Unretained(this)));
+
   feedback_button_callback_ =
       bubble_view_->GetContentView()->RegisterFeedbackButtonPressedCallback(
           base::BindRepeating(
@@ -272,18 +310,23 @@
   base::RecordAction(base::UserMetricsAction(
       toggled_on ? "CookieControls.Bubble.AllowThirdPartyCookies"
                  : "CookieControls.Bubble.BlockThirdPartyCookies"));
+  controller_->SetStateChangedViaBypass(true);
+  controller_->OnCookieBlockingEnabledForSite(!toggled_on);
+  bubble_view_->GetContentView()->NotifyAccessibilityEventDeprecated(
+      ax::mojom::Event::kAlert, true);
+}
 
-  controller_->SetUserChangedCookieBlockingForSite(true);
-  // We should only enter the reloading state in the Incognito ACT UI.
-  // TODO(crbug.com/388294499): Move this logic into a separate function for the
-  // ACT toggle button.
-  if (controller_->ShowActFeatures()) {
-    is_reloading_state_ = true;
-    OnUserTriggeredReloadingAction();
-    controller_->OnTrackingProtectionsChangedForSite();
-  } else {
-    controller_->OnCookieBlockingEnabledForSite(!toggled_on);
+void CookieControlsBubbleViewController::OnTrackingProtectionsButtonPressed() {
+  if (is_reloading_state_) {
+    // TODO(crbug.com/388294499): Ensure that the button UI is visually disabled
+    // when reloading state is active.
+    return;
   }
+  // TODO(crbug.com/388294499): Add metrics for ACT actions.
+  controller_->SetStateChangedViaBypass(true);
+  is_reloading_state_ = true;
+  OnUserTriggeredReloadingAction();
+  controller_->OnTrackingProtectionsChangedForSite();
   bubble_view_->GetContentView()->NotifyAccessibilityEventDeprecated(
       ax::mojom::Event::kAlert, true);
 }
diff --git a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_controller.h b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_controller.h
index 9a25238..4b1121a 100644
--- a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_controller.h
+++ b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_controller.h
@@ -53,6 +53,7 @@
   void OnUserTriggeredReloadingAction();
   void OnToggleButtonPressed(bool toggled_on);
   void OnFeedbackButtonPressed();
+  void OnTrackingProtectionsButtonPressed();
 
   void OnFaviconFetched(const favicon_base::FaviconImageResult& result) const;
 
@@ -64,11 +65,15 @@
                                           base::Time expiration);
   void ApplyThirdPartyCookiesBlockedState();
 
+  void ApplyTrackingProtectionsActiveState();
+  void ApplyTrackingProtectionsPausedState();
+
   void FillDescriptionAndToggle(CookieControlsEnforcement enforcement,
                                 base::Time expiration);
 
   void FillViewForThirdPartyCookies(CookieControlsEnforcement enforcement,
                                     base::Time expiration);
+  void FillViewForTrackingProtections();
 
   void CloseBubble();
 
@@ -98,6 +103,7 @@
   base::CallbackListSubscription on_user_triggered_reloading_action_callback_;
   base::CallbackListSubscription toggle_button_callback_;
   base::CallbackListSubscription feedback_button_callback_;
+  base::CallbackListSubscription tracking_protections_button_callback_;
   base::WeakPtr<content_settings::CookieControlsController> controller_;
   base::WeakPtr<content::WebContents> web_contents_;
   base::ScopedObservation<content_settings::CookieControlsController,
diff --git a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_content_view.cc b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_content_view.cc
index e2e8981..81e1c67d 100644
--- a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_content_view.cc
+++ b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_content_view.cc
@@ -13,6 +13,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/content_settings/browser/ui/cookie_controls_util.h"
 #include "components/content_settings/core/common/cookie_controls_enforcement.h"
+#include "components/content_settings/core/common/cookie_controls_state.h"
 #include "components/content_settings/core/common/features.h"
 #include "components/privacy_sandbox/privacy_sandbox_features.h"
 #include "components/strings/grit/privacy_sandbox_strings.h"
@@ -21,11 +22,13 @@
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/views/accessibility/view_accessibility.h"
+#include "ui/views/controls/button/md_text_button.h"
 #include "ui/views/controls/button/toggle_button.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/separator.h"
 #include "ui/views/layout/box_layout.h"
+#include "ui/views/layout/box_layout_view.h"
 #include "ui/views/vector_icons.h"
 #include "ui/views/view.h"
 #include "ui/views/view_class_properties.h"
@@ -64,6 +67,8 @@
 
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(CookieControlsContentView, kTitle);
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(CookieControlsContentView, kDescription);
+DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(CookieControlsContentView,
+                                      kTrackingProtectionsButton);
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(CookieControlsContentView, kToggleButton);
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(CookieControlsContentView, kToggleLabel);
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(CookieControlsContentView,
@@ -76,6 +81,7 @@
       views::BoxLayout::Orientation::kVertical));
   AddChildView(CreateFullWidthSeparator());
   AddContentLabels();
+  AddTrackingProtectionsButton();
   AddToggleRow();
   AddFeedbackSection();
 }
@@ -159,6 +165,48 @@
   }
 }
 
+void CookieControlsContentView::SetCookiesRowVisible(bool visible) {
+  cookies_row_->SetVisible(visible);
+}
+
+void CookieControlsContentView::SetTrackingProtectionsButtonVisible(
+    bool visible) {
+  tracking_protections_button_->SetVisible(visible);
+}
+
+void CookieControlsContentView::SetTrackingProtectionsButtonLabel(
+    const std::u16string& label) {
+  tracking_protections_button_->SetText(label);
+  tracking_protections_button_->GetViewAccessibility().SetName(label);
+}
+
+void CookieControlsContentView::AddTrackingProtectionsButton() {
+  auto* button_container =
+      AddChildView(std::make_unique<views::BoxLayoutView>());
+  button_container->SetCrossAxisAlignment(
+      views::BoxLayout::CrossAxisAlignment::kStart);
+  tracking_protections_button_ = button_container->AddChildView(
+      std::make_unique<views::MdTextButton>(base::BindRepeating(
+          &CookieControlsContentView::
+              NotifyTrackingProtectionsButtonPressedCallback,
+          base::Unretained(this))));
+
+  tracking_protections_button_->SetProperty(views::kCrossAxisAlignmentKey,
+                                            views::LayoutAlignment::kStart);
+  const int controls_spacing = ChromeLayoutProvider::Get()->GetDistanceMetric(
+      views::DISTANCE_RELATED_CONTROL_VERTICAL);
+  ChromeLayoutProvider* layout_provider = ChromeLayoutProvider::Get();
+  const int side_offset =
+      layout_provider
+          ->GetInsetsMetric(ChromeInsetsMetric::INSETS_PAGE_INFO_HOVER_BUTTON)
+          .left();
+  tracking_protections_button_->SetProperty(
+      views::kMarginsKey, gfx::Insets::TLBR(controls_spacing, side_offset,
+                                            controls_spacing, side_offset));
+  tracking_protections_button_->SetProperty(views::kElementIdentifierKey,
+                                            kTrackingProtectionsButton);
+}
+
 void CookieControlsContentView::AddToggleRow() {
   cookies_row_ = AddChildView(std::make_unique<RichControlsContainerView>());
   cookies_row_->SetTitle(l10n_util::GetStringUTF16(
@@ -262,6 +310,17 @@
   return feedback_button_callback_list_.Add(std::move(callback));
 }
 
+base::CallbackListSubscription
+CookieControlsContentView::RegisterTrackingProtectionsButtonPressedCallback(
+    base::RepeatingCallback<void()> callback) {
+  return tracking_protections_button_callback_list_.Add(std::move(callback));
+}
+
+void CookieControlsContentView::
+    NotifyTrackingProtectionsButtonPressedCallback() {
+  tracking_protections_button_callback_list_.Notify();
+}
+
 void CookieControlsContentView::NotifyToggleButtonPressedCallback() {
   toggle_button_callback_list_.Notify(toggle_button_->GetIsOn());
 }
diff --git a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_content_view.h b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_content_view.h
index 6ff68291..c37ca19 100644
--- a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_content_view.h
+++ b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_content_view.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_COOKIE_CONTROLS_COOKIE_CONTROLS_CONTENT_VIEW_H_
 
 #include "base/memory/raw_ptr.h"
+#include "components/content_settings/core/common/cookie_controls_state.h"
 #include "ui/base/interaction/element_identifier.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/gfx/vector_icon_types.h"
@@ -15,6 +16,7 @@
 class RichControlsContainerView;
 namespace views {
 class Label;
+class LabelButton;
 class ToggleButton;
 class ImageView;
 }  // namespace views
@@ -26,6 +28,7 @@
  public:
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kTitle);
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kDescription);
+  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kTrackingProtectionsButton);
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kToggleButton);
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kToggleLabel);
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kThirdPartyCookiesLabel);
@@ -42,9 +45,13 @@
   virtual void SetToggleIcon(const gfx::VectorIcon& icon);
 
   virtual void SetToggleVisible(bool visible);
+  virtual void SetCookiesRowVisible(bool visible);
+  virtual void SetTrackingProtectionsButtonVisible(bool visible);
+
   virtual void SetCookiesLabel(const std::u16string& label);
   virtual void SetEnforcedIcon(const gfx::VectorIcon& icon,
                                const std::u16string& tooltip);
+  virtual void SetTrackingProtectionsButtonLabel(const std::u16string& label);
 
   virtual void SetEnforcedIconVisible(bool visible);
 
@@ -54,6 +61,9 @@
       base::RepeatingCallback<void(bool)> callback);
   base::CallbackListSubscription RegisterFeedbackButtonPressedCallback(
       base::RepeatingClosureList::CallbackType callback);
+  base::CallbackListSubscription
+  RegisterTrackingProtectionsButtonPressedCallback(
+      base::RepeatingCallback<void()> callback);
 
   void PreferredSizeChanged() override;
 
@@ -67,6 +77,7 @@
 
   void NotifyToggleButtonPressedCallback();
   void NotifyFeedbackButtonPressedCallback();
+  void NotifyTrackingProtectionsButtonPressedCallback();
 
   // Used for 3PC-only UI.
   void AddContentLabels();
@@ -79,9 +90,16 @@
   raw_ptr<views::Label> description_ = nullptr;
   raw_ptr<views::Label> cookies_label_ = nullptr;
   raw_ptr<views::ImageView> enforced_icon_ = nullptr;
-
   raw_ptr<views::ToggleButton> toggle_button_ = nullptr;
+
   base::RepeatingCallbackList<void(bool)> toggle_button_callback_list_;
+  base::RepeatingCallbackList<void()>
+      tracking_protections_button_callback_list_;
+
+  // Used for Tracking protections UI.
+  void AddTrackingProtectionsButton();
+  raw_ptr<views::LabelButton> tracking_protections_button_ = nullptr;
+
   base::RepeatingClosureList feedback_button_callback_list_;
 };
 
diff --git a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_interactive_uitest.cc b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_interactive_uitest.cc
index 3134210..fcfc594 100644
--- a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_interactive_uitest.cc
+++ b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_interactive_uitest.cc
@@ -800,10 +800,13 @@
         browser()->profile()->GetPrimaryOTRProfile(true));
   }
 
-  auto CheckUserBypassFrozenContentBlockedState() {
+  auto CheckTrackingProtectionsActiveState() {
     return Steps(
-        CheckViewProperty(CookieControlsContentView::kToggleButton,
-                          &views::ToggleButton::GetIsOn, true),
+        CheckViewProperty(
+            CookieControlsContentView::kTrackingProtectionsButton,
+            &views::LabelButton::GetText,
+            l10n_util::GetStringUTF16(
+                IDS_TRACKING_PROTECTIONS_BUBBLE_PAUSE_PROTECTIONS_LABEL)),
         CheckViewProperty(
             CookieControlsContentView::kTitle, &views::Label::GetText,
             l10n_util::GetStringUTF16(
@@ -811,32 +814,24 @@
         CheckViewProperty(
             CookieControlsContentView::kDescription, &views::Label::GetText,
             l10n_util::GetStringUTF16(
-                IDS_TRACKING_PROTECTION_BUBBLE_SITE_NOT_WORKING_DESCRIPTION)),
-        CheckViewProperty(
-            CookieControlsContentView::kToggleLabel, &views::Label::GetText,
-            l10n_util::GetStringUTF16(
-                IDS_TRACKING_PROTECTION_BUBBLE_3PC_BLOCKED_SUBTITLE)),
-        CheckIcon(RichControlsContainerView::kIcon,
-                  views::kEyeCrossedRefreshIcon));
+                IDS_TRACKING_PROTECTIONS_BUBBLE_ACTIVE_PROTECTIONS_DESCRIPTION)));
   }
 
-  auto CheckUserBypassFrozenContentAllowedState() {
+  auto CheckTrackingProtectionsPausedState() {
     return Steps(
-        CheckViewProperty(CookieControlsContentView::kToggleButton,
-                          &views::ToggleButton::GetIsOn, false),
+        CheckViewProperty(
+            CookieControlsContentView::kTrackingProtectionsButton,
+            &views::LabelButton::GetText,
+            l10n_util::GetStringUTF16(
+                IDS_TRACKING_PROTECTIONS_BUBBLE_RESUME_PROTECTIONS_LABEL)),
         CheckViewProperty(
             CookieControlsContentView::kTitle, &views::Label::GetText,
             l10n_util::GetStringUTF16(
-                IDS_TRACKING_PROTECTION_BUBBLE_PERMANENT_ALLOWED_TITLE)),
+                IDS_TRACKING_PROTECTIONS_BUBBLE_PAUSED_PROTECTIONS_TITLE)),
         CheckViewProperty(
             CookieControlsContentView::kDescription, &views::Label::GetText,
             l10n_util::GetStringUTF16(
-                IDS_TRACKING_PROTECTION_BUBBLE_PERMANENT_ALLOWED_DESCRIPTION)),
-        CheckViewProperty(
-            CookieControlsContentView::kToggleLabel, &views::Label::GetText,
-            l10n_util::GetStringUTF16(
-                IDS_TRACKING_PROTECTION_BUBBLE_3PC_ALLOWED_SUBTITLE)),
-        CheckIcon(RichControlsContainerView::kIcon, views::kEyeRefreshIcon));
+                IDS_TRACKING_PROTECTIONS_BUBBLE_PAUSED_PROTECTIONS_DESCRIPTION)));
   }
 };
 
@@ -852,10 +847,15 @@
                                 third_party_cookie_page_url()),
             PressButton(kCookieControlsIconElementId),
             InAnyContext(WaitForShow(CookieControlsBubbleView::kContentView)),
-            CheckUserBypassBlockedState(/*incognito=*/true),
-            PressButton(CookieControlsContentView::kToggleButton),
+            CheckTrackingProtectionsActiveState(),
+            PressButton(CookieControlsContentView::kTrackingProtectionsButton),
             EnsureNotPresent(CookieControlsBubbleView::kReloadingView),
-            CheckUserBypassFrozenContentBlockedState())));
+            // TODO(crbug.com/388294499): Add testing for reloading state UI.
+            CheckTrackingProtectionsActiveState(),
+            WaitForHide(CookieControlsBubbleView::kCookieControlsBubble))));
+  // Ensure that the reloading timeout doesn't execute when page reloads faster
+  // than the timeout window.
+  EXPECT_EQ(user_actions_.GetActionCount(kUMABubbleReloadingTimeout), 0);
 }
 
 IN_PROC_BROWSER_TEST_F(CookieControlsInteractiveUiTrackingProtectionTest,
@@ -874,29 +874,14 @@
                                 third_party_cookie_page_url()),
             PressButton(kCookieControlsIconElementId),
             InAnyContext(WaitForShow(CookieControlsBubbleView::kContentView)),
-            CheckUserBypassAllowedState(/*incognito=*/true),
-            PressButton(CookieControlsContentView::kToggleButton),
+            CheckTrackingProtectionsPausedState(),
+            PressButton(CookieControlsContentView::kTrackingProtectionsButton),
             EnsureNotPresent(CookieControlsBubbleView::kReloadingView),
-            CheckUserBypassFrozenContentAllowedState())));
-}
-
-IN_PROC_BROWSER_TEST_F(CookieControlsInteractiveUiTrackingProtectionTest,
-                       ReloadsWithoutShowingReloadingViewWhenStatusChanged) {
-  // Test that opening the bubble and making a change results in the
-  // reloading view not showing and the bubble automatically closing.
-  BlockThirdPartyCookies();
-  EnableFpProtection();
-  auto* const incognito_browser = CreateIncognitoBrowser(browser()->profile());
-  RunTestSequence(InContext(
-      incognito_browser->window()->GetElementContext(),
-      Steps(InstrumentTab(kWebContentsElementId),
-            NavigateWebContents(kWebContentsElementId,
-                                third_party_cookie_page_url()),
-            PressButton(kCookieControlsIconElementId),
-            InAnyContext(WaitForShow(CookieControlsBubbleView::kContentView)),
-            PressButton(CookieControlsContentView::kToggleButton),
-            EnsureNotPresent(CookieControlsBubbleView::kReloadingView),
+            // TODO(crbug.com/388294499): Add testing for reloading state UI.
+            CheckTrackingProtectionsPausedState(),
             WaitForHide(CookieControlsBubbleView::kCookieControlsBubble))));
+  // Ensure that the reloading timeout doesn't execute when page reloads faster
+  // than the timeout window.
   EXPECT_EQ(user_actions_.GetActionCount(kUMABubbleReloadingTimeout), 0);
 }
 
@@ -923,7 +908,7 @@
             InAnyContext(WaitForShow(kCookieControlsIconElementId)),
             PressButton(kCookieControlsIconElementId),
             InAnyContext(WaitForShow(CookieControlsBubbleView::kContentView)),
-            PressButton(CookieControlsContentView::kToggleButton),
+            PressButton(CookieControlsContentView::kTrackingProtectionsButton),
             EnsureNotPresent(CookieControlsBubbleView::kReloadingView),
             WaitForHide(CookieControlsBubbleView::kCookieControlsBubble))));
   EXPECT_EQ(user_actions_.GetActionCount(kUMABubbleReloadingTimeout), 1);
diff --git a/chrome/browser/ui/views/passwords/password_change/password_change_icon_views_interactive_ui_test.cc b/chrome/browser/ui/views/passwords/password_change/password_change_icon_views_interactive_ui_test.cc
index 6fe24dda..e9606cb 100644
--- a/chrome/browser/ui/views/passwords/password_change/password_change_icon_views_interactive_ui_test.cc
+++ b/chrome/browser/ui/views/passwords/password_change/password_change_icon_views_interactive_ui_test.cc
@@ -109,6 +109,41 @@
             GetView()->GetVectorIcon().name);
 }
 
+IN_PROC_BROWSER_TEST_F(
+    PasswordChangeIconViewsTest,
+    ViewIsVisibleWhenChangingPasswordWaitingForPasswordForm) {
+  EnableSignIn();
+  SetPrivacyNoticeAcceptedPref();
+  SetupPasswordChange();
+  EXPECT_TRUE(GetView()->GetVisible());
+  EXPECT_EQ(views::kPasswordChangeIcon.name, GetView()->GetVectorIcon().name);
+  EXPECT_EQ(l10n_util::GetStringUTF16(
+                IDS_PASSWORD_MANAGER_UI_PASSWORD_CHANGE_OMNIBOX_SIGN_IN_CHECK),
+            GetView()->GetText());
+}
+
+IN_PROC_BROWSER_TEST_F(PasswordChangeIconViewsTest,
+                       ViewIsVisibleWhenChangingPasswordFinished) {
+  EnableSignIn();
+  SetPrivacyNoticeAcceptedPref();
+  SetupPasswordChange();
+  // Wait until the password change flow runs. The flow will fail
+  // because OptimizationGuideKeyedService is not set up for this test. But it
+  // doesn't matter here, it only checks that the icon should change
+  // back to standard password manger icon and label should be removed.
+  ASSERT_TRUE(base::test::RunUntil([&]() {
+    PasswordChangeDelegate* delegate =
+        static_cast<PasswordsModelDelegate*>(GetController())
+            ->GetPasswordChangeDelegate();
+    return delegate->GetCurrentState() ==
+           PasswordChangeDelegate::State::kPasswordChangeFailed;
+  }));
+  EXPECT_TRUE(GetView()->GetVisible());
+  EXPECT_EQ(vector_icons::kPasswordManagerIcon.name,
+            GetView()->GetVectorIcon().name);
+  EXPECT_EQ(u"", GetView()->GetText());
+}
+
 IN_PROC_BROWSER_TEST_F(PasswordChangeIconViewsTest,
                        ViewIsNotVisibleWhenChangingPasswordCanceled) {
   SetupPasswordChange();
diff --git a/chrome/browser/ui/views/passwords/password_change/password_change_ui_browsertest.cc b/chrome/browser/ui/views/passwords/password_change/password_change_ui_browsertest.cc
index a566faee..ca8d5390 100644
--- a/chrome/browser/ui/views/passwords/password_change/password_change_ui_browsertest.cc
+++ b/chrome/browser/ui/views/passwords/password_change/password_change_ui_browsertest.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
 #include "chrome/browser/ui/test/test_browser_dialog.h"
+#include "chrome/browser/ui/views/passwords/password_bubble_view_base.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/affiliations/core/browser/mock_affiliation_service.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
@@ -71,6 +72,8 @@
           GURL("https://example.com/"), u"username", u"password",
           /*in_account_store=*/false));
     }
+    PasswordBubbleViewBase::ShowBubble(
+        web_contents, LocationBarBubbleDelegateView::USER_GESTURE);
   }
 
   ChromePasswordChangeService* password_change_service() {
diff --git a/chrome/browser/ui/views/permissions/permission_prompt_factory.cc b/chrome/browser/ui/views/permissions/permission_prompt_factory.cc
index 13f9782..a14c5b7 100644
--- a/chrome/browser/ui/views/permissions/permission_prompt_factory.cc
+++ b/chrome/browser/ui/views/permissions/permission_prompt_factory.cc
@@ -32,6 +32,7 @@
 
 #if BUILDFLAG(IS_MAC)
 #include "chrome/browser/ui/views/permissions/permission_prompt_notifications_mac.h"
+#include "chrome/browser/web_applications/os_integration/mac/web_app_shortcut_mac.h"
 #endif
 
 namespace {
@@ -249,7 +250,7 @@
   // this request, try using a OS-native permission prompt. If showing the
   // prompt fails, it will trigger the view to be recreated for the request, at
   // which point we end up in the normal code path below.
-  if (base::FeatureList::IsEnabled(features::kAppShimNotificationAttribution) &&
+  if (web_app::UseNotificationAttributionForWebAppShims() &&
       !delegate->WasCurrentRequestAlreadyDisplayed() &&
       PermissionPromptNotificationsMac::CanHandleRequest(web_contents,
                                                          delegate)) {
diff --git a/chrome/browser/ui/views/webauthn/combined_selector_views.cc b/chrome/browser/ui/views/webauthn/combined_selector_views.cc
index a1c39b7..17ce0fa 100644
--- a/chrome/browser/ui/views/webauthn/combined_selector_views.cc
+++ b/chrome/browser/ui/views/webauthn/combined_selector_views.cc
@@ -146,7 +146,8 @@
                                      ? ax::mojom::Role::kRadioButton
                                      : ax::mojom::Role::kButton);
   GetViewAccessibility().SetName(base::JoinString(texts, u"\n"));
-  SetBorder(views::CreateEmptyBorder(gfx::Insets::VH(8, 16)));
+  const int horizontal_padding = radio_status == RadioStatus::kNone ? 0 : 16;
+  SetBorder(views::CreateEmptyBorder(gfx::Insets::VH(8, horizontal_padding)));
 
   const int icon_padding = ChromeLayoutProvider::Get()->GetDistanceMetric(
       views::DISTANCE_RELATED_LABEL_HORIZONTAL);
@@ -235,25 +236,39 @@
   wrapper->SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical, gfx::Insets(),
       /*between_child_spacing=*/kRowGap));
+  size_t num_mechanisms = model->dialog_model()->mechanisms.size();
 
-  for (size_t i = 0; i < model->dialog_model()->mechanisms.size(); i++) {
+  if (num_mechanisms > 1) {
+    wrapper->AddChildView(std::make_unique<views::Separator>());
+  }
+
+  for (size_t i = 0; i < num_mechanisms; i++) {
     if (i > 0) {
       wrapper->AddChildView(std::make_unique<views::Separator>());
     }
     const auto& mechanism = model->dialog_model()->mechanisms[i];
     auto image_model =
         ui::ImageModel::FromVectorIcon(*mechanism.icon, ui::kColorIcon, 20);
+    auto texts = mechanism.display_name.empty() ||
+                         (mechanism.display_name == mechanism.name)
+                     ? std::vector<std::u16string_view>{mechanism.name,
+                                                        mechanism.description}
+                     : std::vector<std::u16string_view>{mechanism.display_name,
+                                                        mechanism.name,
+                                                        mechanism.description};
     auto* row = wrapper->AddChildView(std::make_unique<CombinedSelectorRowView>(
-        image_model,
-        std::vector<std::u16string_view>{mechanism.name, mechanism.description},
-        model->GetSelectionStatus(i), !model->dialog_model()->ui_disabled_,
-        delegate, i));
+        image_model, std::move(texts), model->GetSelectionStatus(i),
+        !model->dialog_model()->ui_disabled_, delegate, i));
     if (model->GetSelectionStatus(i) ==
         CombinedSelectorSheetModel::SelectionStatus::kSelected) {
       selected_view_ = row;
     }
   }
 
+  if (num_mechanisms > 1) {
+    wrapper->AddChildView(std::make_unique<views::Separator>());
+  }
+
   scroll_view->SetContents(std::move(wrapper));
   scroll_view->ClipHeightTo(kMaxRowHeight, 3 * kMaxRowHeight + 2 * kRowGap);
 }
diff --git a/chrome/browser/ui/webui/app_management/web_app_settings_page_handler.cc b/chrome/browser/ui/webui/app_management/web_app_settings_page_handler.cc
index 7d4a824..1ce9a383 100644
--- a/chrome/browser/ui/webui/app_management/web_app_settings_page_handler.cc
+++ b/chrome/browser/ui/webui/app_management/web_app_settings_page_handler.cc
@@ -279,7 +279,7 @@
 
   app->show_system_notifications_settings_link = false;
 #if BUILDFLAG(IS_MAC)
-  if (base::FeatureList::IsEnabled(features::kAppShimNotificationAttribution)) {
+  if (web_app::UseNotificationAttributionForWebAppShims()) {
     auto system_permission_status =
         AppShimRegistry::Get()->GetNotificationPermissionStatusForApp(app->id);
     app->show_system_notifications_settings_link =
diff --git a/chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_localized_strings_provider.cc b/chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_localized_strings_provider.cc
index 0f5c7a5d..178e98d 100644
--- a/chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_localized_strings_provider.cc
@@ -87,7 +87,10 @@
     {"dialogAccessibilityTitle",
      IDS_MULTIDEVICE_SETUP_DIALOG_ACCESSIBILITY_TITLE},
     {"startSetupPageFeatureListHeader",
-     IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_FEATURE_LIST_HEADER}};
+     IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_FEATURE_LIST_HEADER},
+    {"pauseAnimationAriaLabel", IDS_OOBE_PAUSE_ANIMATION_MESSAGE},
+    {"playAnimationAriaLabel", IDS_OOBE_PLAY_ANIMATION_MESSAGE},
+};
 
 struct LocalizedStringWithName {
   LocalizedStringWithName(const char* name,
diff --git a/chrome/browser/ui/webui/ash/settings/pages/about/about_section.cc b/chrome/browser/ui/webui/ash/settings/pages/about/about_section.cc
index 851e3ef5..76c6d26a 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/about/about_section.cc
+++ b/chrome/browser/ui/webui/ash/settings/pages/about/about_section.cc
@@ -255,8 +255,6 @@
       {"aboutDiagnostics", IDS_SETTINGS_ABOUT_PAGE_DIAGNOSTICS},
       {"aboutDiagnosticseDescription", IDS_OS_SETTINGS_DIAGNOSTICS_DESCRIPTION},
       {"aboutFirmwareUpdates", IDS_SETTINGS_ABOUT_PAGE_FIRMWARE_UPDATES},
-      {"aboutFirmwareUpdatesDisabledDescription",
-       IDS_OS_SETTINGS_FIRMWARE_DISABLED_DESCRIPTION},
       {"aboutFirmwareUpToDateDescription",
        IDS_OS_SETTINGS_FIRMWARE_UP_TO_DATE_DESCRIPTION},
       {"aboutFirmwareUpdateAvailableDescription",
diff --git a/chrome/browser/ui/webui/settings/about_handler.cc b/chrome/browser/ui/webui/settings/about_handler.cc
index 6c07b51..39feb9f 100644
--- a/chrome/browser/ui/webui/settings/about_handler.cc
+++ b/chrome/browser/ui/webui/settings/about_handler.cc
@@ -71,7 +71,6 @@
 #include "chrome/browser/ui/webui/ash/extended_updates/extended_updates_dialog.h"
 #include "chrome/browser/ui/webui/help/help_utils_chromeos.h"
 #include "chrome/browser/ui/webui/help/version_updater_chromeos.h"
-#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
 #include "chromeos/ash/components/dbus/update_engine/update_engine_client.h"
 #include "chromeos/ash/components/fwupd/firmware_update_manager.h"
 #include "chromeos/ash/components/network/network_state.h"
@@ -126,25 +125,6 @@
               : IDS_UPGRADE_NETWORK_LIST_CELLULAR_ALLOWED);
 }
 
-// Returns true if current user can change firmware, false otherwise.
-bool CanChangeFirmware(Profile* profile) {
-  if (policy::ManagementServiceFactory::GetForPlatform()->IsManaged()) {
-    bool value = false;
-    // On a managed machine we allow firmware changes only if enabled by policy
-    if (!ash::CrosSettings::Get()->GetBoolean(
-            ash::kDeviceUserInitiatedFirmwareUpdatesEnabled, &value)) {
-      // This can occur if the lookup for the policy's value fails,
-      // for example if the policy is not present on the current version.
-      // In this case, default to false.
-      LOG(ERROR) << "Failed to get device setting.";
-      return false;
-    }
-    return value;
-  }
-  return user_manager::UserManager::Get()->IsOwnerUser(
-      ash::BrowserContextHelper::Get()->GetUserByBrowserContext(profile));
-}
-
 // Returns true if current user can change channel, false otherwise.
 bool CanChangeChannel(Profile* profile) {
   if (policy::ManagementServiceFactory::GetForPlatform()->IsManaged()) {
@@ -359,10 +339,6 @@
       "getChannelInfo", base::BindRepeating(&AboutHandler::HandleGetChannelInfo,
                                             base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
-      "canChangeFirmware",
-      base::BindRepeating(&AboutHandler::HandleCanChangeFirmware,
-                          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
       "canChangeChannel",
       base::BindRepeating(&AboutHandler::HandleCanChangeChannel,
                           base::Unretained(this)));
@@ -642,13 +618,6 @@
                      weak_factory_.GetWeakPtr(), callback_id));
 }
 
-void AboutHandler::HandleCanChangeFirmware(const base::Value::List& args) {
-  CHECK_EQ(1U, args.size());
-  const std::string& callback_id = args[0].GetString();
-  ResolveJavascriptCallback(base::Value(callback_id),
-                            base::Value(CanChangeFirmware(profile_)));
-}
-
 void AboutHandler::HandleCanChangeChannel(const base::Value::List& args) {
   CHECK_EQ(1U, args.size());
   const std::string& callback_id = args[0].GetString();
diff --git a/chrome/browser/ui/webui/settings/about_handler.h b/chrome/browser/ui/webui/settings/about_handler.h
index d06d042..81cb41d 100644
--- a/chrome/browser/ui/webui/settings/about_handler.h
+++ b/chrome/browser/ui/webui/settings/about_handler.h
@@ -109,9 +109,6 @@
   // Retrieves channel info.
   void HandleGetChannelInfo(const base::Value::List& args);
 
-  // Checks whether we can update the firmware.
-  void HandleCanChangeFirmware(const base::Value::List& args);
-
   // Checks whether we can change the current channel.
   void HandleCanChangeChannel(const base::Value::List& args);
 
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 2efc32d..dcd87b75 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -2330,16 +2330,6 @@
        IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_SETTING_SUBLABEL},
       {"safetyHubAbusiveNotificationPermissionsSettingSublabel",
        IDS_SETTINGS_SAFETY_HUB_ABUSIVE_NOTIFICATION_PERMISSIONS_SETTING_SUBLABEL},
-      // TODO(crbug.com/328773301): Remove after
-      // SafetyHubAbusiveNotificationRevocation is launched.
-      {"safetyCheckUnusedSitePermissionsRemovedOnePermissionLabel",
-       IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_REMOVED_ONE_PERMISSION_LABEL},
-      {"safetyCheckUnusedSitePermissionsRemovedTwoPermissionsLabel",
-       IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_REMOVED_TWO_PERMISSIONS_LABEL},
-      {"safetyCheckUnusedSitePermissionsRemovedThreePermissionsLabel",
-       IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_REMOVED_THREE_PERMISSIONS_LABEL},
-      {"safetyCheckUnusedSitePermissionsRemovedFourOrMorePermissionsLabel",
-       IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_REMOVED_FOUR_OR_MORE_PERMISSIONS_LABEL},
   };
   html_source->AddLocalizedStrings(kLocalizedStrings);
 
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index e1c9649..3858552 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -459,11 +459,6 @@
                           is_restricted_notice_enabled);
 
   html_source->AddBoolean(
-      "safetyHubAbusiveNotificationRevocationEnabled",
-      base::FeatureList::IsEnabled(
-          safe_browsing::kSafetyHubAbusiveNotificationRevocation));
-
-  html_source->AddBoolean(
       "isRelatedWebsiteSetsV2UiEnabled",
       base::FeatureList::IsEnabled(
           privacy_sandbox::kPrivacySandboxRelatedWebsiteSetsUi));
diff --git a/chrome/browser/web_applications/os_integration/mac/web_app_shortcut_mac.h b/chrome/browser/web_applications/os_integration/mac/web_app_shortcut_mac.h
index f2c56c9a9..0d10eda 100644
--- a/chrome/browser/web_applications/os_integration/mac/web_app_shortcut_mac.h
+++ b/chrome/browser/web_applications/os_integration/mac/web_app_shortcut_mac.h
@@ -28,6 +28,10 @@
 // at runtime and the UseAdHocSigningForWebAppShims feature is enabled.
 bool UseAdHocSigningForWebAppShims();
 
+// Returns true if we're using ad-hoc signed app shims and the
+// kAppShimNotificationAttribution feature is enabled.
+bool UseNotificationAttributionForWebAppShims();
+
 }  // namespace web_app
 
 #endif  // CHROME_BROWSER_WEB_APPLICATIONS_OS_INTEGRATION_MAC_WEB_APP_SHORTCUT_MAC_H_
diff --git a/chrome/browser/web_applications/os_integration/mac/web_app_shortcut_mac.mm b/chrome/browser/web_applications/os_integration/mac/web_app_shortcut_mac.mm
index 260bdf73..587c125 100644
--- a/chrome/browser/web_applications/os_integration/mac/web_app_shortcut_mac.mm
+++ b/chrome/browser/web_applications/os_integration/mac/web_app_shortcut_mac.mm
@@ -11,6 +11,7 @@
 #import "base/apple/foundation_util.h"
 #include "base/check_is_test.h"
 #include "base/command_line.h"
+#include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/functional/callback_helpers.h"
@@ -97,6 +98,12 @@
   return true;
 }
 
+bool UseNotificationAttributionForWebAppShims() {
+  return base::FeatureList::IsEnabled(
+             features::kAppShimNotificationAttribution) &&
+         UseAdHocSigningForWebAppShims();
+}
+
 namespace internals {
 
 void CreatePlatformShortcuts_WithUseAdHocSigningForWebAppShims(
diff --git a/chrome/browser/web_applications/web_app_tab_helper.cc b/chrome/browser/web_applications/web_app_tab_helper.cc
index 3e3fdff..c6c95d9e 100644
--- a/chrome/browser/web_applications/web_app_tab_helper.cc
+++ b/chrome/browser/web_applications/web_app_tab_helper.cc
@@ -36,6 +36,10 @@
 #include "third_party/blink/public/common/renderer_preferences/renderer_preferences.h"
 #include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom-shared.h"
 
+#if BUILDFLAG(IS_MAC)
+#include "chrome/browser/web_applications/os_integration/mac/web_app_shortcut_mac.h"
+#endif
+
 namespace web_app {
 
 // static
@@ -74,8 +78,7 @@
 std::optional<webapps::AppId>
 WebAppTabHelper::GetAppIdForNotificationAttribution(
     content::WebContents* web_contents) {
-  if (!base::FeatureList::IsEnabled(
-          features::kAppShimNotificationAttribution)) {
+  if (!UseNotificationAttributionForWebAppShims()) {
     return std::nullopt;
   }
   const webapps::AppId* app_id = GetAppId(web_contents);
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_controller.cc b/chrome/browser/webauthn/authenticator_request_dialog_controller.cc
index 14568d06..0c7b766 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_controller.cc
+++ b/chrome/browser/webauthn/authenticator_request_dialog_controller.cc
@@ -2239,7 +2239,8 @@
           base::BindRepeating(
               base::IgnoreResult(
                   &AuthenticatorRequestDialogController::OnAccountPreselected),
-              base::Unretained(this), cred.cred_id));
+              base::Unretained(this), cred.cred_id),
+          base::UTF8ToUTF16(cred.user.display_name.value_or("")));
       mechanism.description =
           AuthenticatorRequestDialogModel::GetMechanismDescription(
               cred, model_->priority_phone_name, ui_presentation());
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.cc b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
index be6f06a..747a8112 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.cc
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
@@ -333,10 +333,12 @@
     std::u16string in_name,
     std::u16string in_short_name,
     const gfx::VectorIcon& in_icon,
-    base::RepeatingClosure in_callback)
+    base::RepeatingClosure in_callback,
+    std::u16string in_display_name)
     : type(std::move(in_type)),
       name(std::move(in_name)),
       short_name(std::move(in_short_name)),
+      display_name(std::move(in_display_name)),
       icon(in_icon),
       callback(std::move(in_callback)) {}
 AuthenticatorRequestDialogModel::Mechanism::~Mechanism() = default;
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.h b/chrome/browser/webauthn/authenticator_request_dialog_model.h
index 37edbb61..7d0127e 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.h
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.h
@@ -373,7 +373,8 @@
               std::u16string name,
               std::u16string short_name,
               const gfx::VectorIcon& icon,
-              base::RepeatingClosure callback);
+              base::RepeatingClosure callback,
+              std::u16string display_name = std::u16string());
     ~Mechanism();
     Mechanism(Mechanism&&);
     Mechanism(const Mechanism&) = delete;
@@ -381,7 +382,9 @@
 
     const Type type;
     const std::u16string name;
+    // TODO(crbug.com/422394117): This is not used anywhere. Remove it.
     const std::u16string short_name;
+    const std::u16string display_name;
     std::u16string description;
     const raw_ref<const gfx::VectorIcon> icon;
     const base::RepeatingClosure callback;
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index c4b049f..e6f953c 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1749127202-62a974c33d6d99b18b3da59f59b4057354da1602-8af105aa2c06befede8aa22809bfec6f75d7efa2.profdata
+chrome-android64-main-1749134476-b1da855c4ff9e119edb069076d817cbaf402517b-b5bb86078fdb00a47804d67896b2cc57fa66291f.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 04af227c..db13328 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1749131410-ddbb787b277fbd92608e4309af1f138437291a38-463eacc03950670d682596059adc9ffb9f5f6591.profdata
+chrome-mac-arm-main-1749146264-add6a36f799b1dc118f3f47c9620e3e877d1a4ef-46225ed4889da320c0d607a3a8e9ce0400bd3e88.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index c185f43..d66cdee9 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1749103132-399b5f4ed9e1997e9e8d26d32c909a6d5cdfb66a-0b02c2102a30672284a328d6a342b4fcbe77a5c6.profdata
+chrome-win32-main-1749124318-263a5edd244b6288d277f7d84fdad5959c5be459-26cbea720b9742a07237e7129eaadad0e8ba70e4.profdata
diff --git a/chrome/common/extensions/api/api_sources.gni b/chrome/common/extensions/api/api_sources.gni
index d8a7924..5f03de9 100644
--- a/chrome/common/extensions/api/api_sources.gni
+++ b/chrome/common/extensions/api/api_sources.gni
@@ -16,6 +16,7 @@
   "bookmarks.json",
   "cookies.json",
   "developer_private.idl",
+  "font_settings.json",
   "history.json",
   "identity.idl",
   "notifications.idl",
@@ -57,7 +58,6 @@
     "downloads_internal.idl",
     "enterprise_hardware_platform.idl",
     "enterprise_reporting_private.idl",
-    "font_settings.json",
     "gcm.json",
     "image_writer_private.idl",
     "instance_id.json",
diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h
index 56bfc2ac..2d373de 100644
--- a/chrome/common/extensions/extension_constants.h
+++ b/chrome/common/extensions/extension_constants.h
@@ -281,6 +281,8 @@
 // Path to preinstalled Google speech synthesis extension.
 inline constexpr char kGoogleSpeechSynthesisExtensionPath[] =
     "/usr/share/chromeos-assets/speech_synthesis/patts";
+inline constexpr char kGoogleSpeechSynthesisManifestV3ExtensionPath[] =
+    "/usr/share/chromeos-assets/speech_synthesis/patts/mv3";
 // The extension id of the Google speech synthesis extension.
 inline constexpr char kGoogleSpeechSynthesisExtensionId[] =
     "gjjabgpgjpampikjhjpfhneeoapjbjaf";
diff --git a/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc b/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc
index 2896d8d6..1f71ccc 100644
--- a/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc
+++ b/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc
@@ -19,7 +19,6 @@
 
 #include "base/check_deref.h"
 #include "base/containers/fixed_flat_map.h"
-#include "base/debug/stack_trace.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/metrics_hashes.h"
 #include "base/notreached.h"
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index f216987..9f2c3eb 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1504,6 +1504,10 @@
         [ "../browser/metrics/cros_pre_consent_metrics_manager_browsertest.cc" ]
   }
 
+  if (!is_android || enable_desktop_android_extensions) {
+    deps += [ "//chrome/browser/ui/prefs:browser_tests" ]
+  }
+
   if (enable_extensions) {
     deps += [
       "//chrome/browser/extensions",
@@ -1523,6 +1527,7 @@
       "../browser/extensions/api/extension_action/extension_action_apitest.cc",
       "../browser/extensions/api/extension_action/test_icon_image_observer.cc",
       "../browser/extensions/api/extension_action/test_icon_image_observer.h",
+      "../browser/extensions/api/font_settings/font_settings_apitest.cc",
       "../browser/extensions/api/history/history_apitest.cc",
       "../browser/extensions/api/i18n/i18n_apitest.cc",
       "../browser/extensions/api/idle/idle_apitest.cc",
@@ -2337,7 +2342,6 @@
       "//chrome/browser/ui/page_action:icon_type",
       "//chrome/browser/ui/page_info",
       "//chrome/browser/ui/permission_bubble:browser_tests",
-      "//chrome/browser/ui/prefs:browser_tests",
       "//chrome/browser/ui/send_tab_to_self:browser_tests",
       "//chrome/browser/ui/web_applications",
       "//chrome/browser/ui/webui/app_service_internals:browser_tests",
@@ -4362,7 +4366,6 @@
         "../browser/extensions/api/favicon/favicon_apitest.cc",
         "../browser/extensions/api/feedback_private/feedback_browsertest.cc",
         "../browser/extensions/api/file_system/file_system_apitest.cc",
-        "../browser/extensions/api/font_settings/font_settings_apitest.cc",
         "../browser/extensions/api/gcm/gcm_apitest.cc",
         "../browser/extensions/api/identity/gaia_remote_consent_flow_browsertest.cc",
         "../browser/extensions/api/identity/identity_apitest.cc",
@@ -6174,6 +6177,7 @@
     "../browser/first_party_sets/first_party_sets_overrides_policy_handler_unittest.cc",
     "../browser/first_party_sets/first_party_sets_policy_service_factory_unittest.cc",
     "../browser/first_party_sets/first_party_sets_policy_service_unittest.cc",
+    "../browser/font_family_cache_unittest.cc",
     "../browser/font_pref_change_notifier_unittest.cc",
     "../browser/geolocation/geolocation_permission_context_delegate_unittest.cc",
     "../browser/history/history_tab_helper_unittest.cc",
@@ -7770,7 +7774,6 @@
       "../browser/enterprise/reporting/real_time_report_generator_unittest.cc",
       "../browser/enterprise/signals/client_certificate_fetcher_unittest.cc",
       "../browser/first_run/first_run_unittest.cc",
-      "../browser/font_family_cache_unittest.cc",
       "../browser/hid/chrome_hid_delegate_unittest.cc",
       "../browser/hid/hid_chooser_context_unittest.cc",
       "../browser/hid/hid_connection_tracker_unittest.cc",
@@ -8998,6 +9001,7 @@
   if (enable_extensions_core) {
     sources += [
       "../browser/extensions/api/bookmarks/bookmarks_api_unittest.cc",
+      "../browser/extensions/api/browsing_data/browsing_data_unittest.cc",
       "../browser/extensions/api/chrome_extensions_api_client_unittest.cc",
       "../browser/extensions/api/declarative/rules_registry_service_unittest.cc",
       "../browser/extensions/api/declarative/rules_registry_with_cache_unittest.cc",
@@ -9122,7 +9126,6 @@
       "../browser/extensions/activity_log/fullstream_ui_policy_unittest.cc",
       "../browser/extensions/api/activity_log_private/activity_log_private_api_unittest.cc",
       "../browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_unittest.cc",
-      "../browser/extensions/api/browsing_data/browsing_data_unittest.cc",
       "../browser/extensions/api/cookies/cookies_helpers_unittest.cc",
       "../browser/extensions/api/cookies/cookies_unittest.cc",
       "../browser/extensions/api/declarative_content/content_condition_unittest.cc",
diff --git a/chrome/test/base/android/android_browser_test.cc b/chrome/test/base/android/android_browser_test.cc
index a619549a..fb3c6ad 100644
--- a/chrome/test/base/android/android_browser_test.cc
+++ b/chrome/test/base/android/android_browser_test.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
 #include "chrome/test/base/chrome_test_utils.h"
 #include "chrome/test/base/test_launcher_utils.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/buildflags/buildflags.h"
 
@@ -22,6 +23,11 @@
 }  // namespace
 
 AndroidBrowserTest::AndroidBrowserTest() {
+  // chrome::DIR_TEST_DATA isn't going to be setup until after we call
+  // ContentMain. However that is after tests' constructors or SetUp methods,
+  // which sometimes need it. So just override it.
+  chrome_test_utils::OverrideChromeTestDataDir();
+
   CreateTestServer(base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
 #if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
   // Allow unpacked extensions without developer mode for testing.
@@ -29,6 +35,12 @@
       extensions_features::kExtensionDisableUnsupportedDeveloper);
 #endif
   g_current_test = this;
+
+  create_services_subscription_ =
+      BrowserContextDependencyManager::GetInstance()
+          ->RegisterCreateServicesCallbackForTesting(base::BindRepeating(
+              &AndroidBrowserTest::SetUpBrowserContextKeyedServices,
+              base::Unretained(this)));
 }
 
 AndroidBrowserTest::~AndroidBrowserTest() {
diff --git a/chrome/test/base/android/android_browser_test.h b/chrome/test/base/android/android_browser_test.h
index 592339e..7008bd0 100644
--- a/chrome/test/base/android/android_browser_test.h
+++ b/chrome/test/base/android/android_browser_test.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_TEST_BASE_ANDROID_ANDROID_BROWSER_TEST_H_
 #define CHROME_TEST_BASE_ANDROID_ANDROID_BROWSER_TEST_H_
 
+#include "base/callback_list.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/profiles/profile.h"
@@ -52,6 +53,13 @@
   // successful. To set initial prefs, see SetUpLocalStatePrefService.
   [[nodiscard]] virtual bool SetUpUserDataDirectory();
 
+  // Called just before BrowserContextKeyedService creation is started
+  // for each Profile creation.
+  // Test fixtures inheriting AndroidBrowserTest can inject some fake/test
+  // BrowserContextKeyedService as necessary for testing.
+  virtual void SetUpBrowserContextKeyedServices(
+      content::BrowserContext* context) {}
+
   // Tests can override this to customize the initial local_state.
   virtual void SetUpLocalStatePrefService(PrefService* local_state) {}
 
@@ -78,6 +86,9 @@
   base::ScopedTempDir temp_user_data_dir_;
 
   base::test::ScopedFeatureList feature_list_;
+
+  // Used to set up test factories for each browser context.
+  base::CallbackListSubscription create_services_subscription_;
 };
 
 #endif  // CHROME_TEST_BASE_ANDROID_ANDROID_BROWSER_TEST_H_
diff --git a/chrome/test/base/chrome_test_utils.cc b/chrome/test/base/chrome_test_utils.cc
index 149846f..15117a9 100644
--- a/chrome/test/base/chrome_test_utils.cc
+++ b/chrome/test/base/chrome_test_utils.cc
@@ -4,7 +4,9 @@
 
 #include "chrome/test/base/chrome_test_utils.h"
 
+#include "base/path_service.h"
 #include "build/build_config.h"
+#include "chrome/common/chrome_paths.h"
 
 #if BUILDFLAG(IS_ANDROID)
 #include "chrome/browser/ui/android/tab_model/tab_model.h"
@@ -45,4 +47,11 @@
   return base::FilePath(FILE_PATH_LITERAL("chrome/test/data"));
 }
 
+void OverrideChromeTestDataDir() {
+  base::FilePath src_dir;
+  CHECK(base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &src_dir));
+  CHECK(base::PathService::Override(chrome::DIR_TEST_DATA,
+                                    src_dir.Append(GetChromeTestDataDir())));
+}
+
 }  // namespace chrome_test_utils
diff --git a/chrome/test/base/chrome_test_utils.h b/chrome/test/base/chrome_test_utils.h
index 2e3bbd93..c145668 100644
--- a/chrome/test/base/chrome_test_utils.h
+++ b/chrome/test/base/chrome_test_utils.h
@@ -34,6 +34,10 @@
 // Returns the test data path used by the embedded test server.
 base::FilePath GetChromeTestDataDir();
 
+// Overrides the path chrome::DIR_TEST_DATA. Used early in test startup so the
+// value is available in constructors and SetUp methods.
+void OverrideChromeTestDataDir();
+
 }  // namespace chrome_test_utils
 
 #endif  // CHROME_TEST_BASE_CHROME_TEST_UTILS_H_
diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc
index 4c17d7d..75343e7 100644
--- a/chrome/test/base/in_process_browser_test.cc
+++ b/chrome/test/base/in_process_browser_test.cc
@@ -322,14 +322,11 @@
 
 void InProcessBrowserTest::Initialize() {
   g_current_test = this;
-  base::FilePath src_dir;
-  CHECK(base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &src_dir));
 
   // chrome::DIR_TEST_DATA isn't going to be setup until after we call
   // ContentMain. However that is after tests' constructors or SetUp methods,
   // which sometimes need it. So just override it.
-  CHECK(base::PathService::Override(chrome::DIR_TEST_DATA,
-                                    src_dir.Append(GetChromeTestDataDir())));
+  chrome_test_utils::OverrideChromeTestDataDir();
 
 #if BUILDFLAG(IS_MAC)
   bundle_swizzler_ = std::make_unique<ScopedBundleSwizzlerMac>();
diff --git a/chrome/test/chromedriver/chrome/navigation_tracker.cc b/chrome/test/chromedriver/chrome/navigation_tracker.cc
index 2a043c0e..2529af8e 100644
--- a/chrome/test/chromedriver/chrome/navigation_tracker.cc
+++ b/chrome/test/chromedriver/chrome/navigation_tracker.cc
@@ -11,7 +11,6 @@
 
 #include <unordered_map>
 
-#include "base/debug/stack_trace.h"
 #include "base/logging.h"
 #include "base/sequence_checker_impl.h"
 #include "base/strings/string_util.h"
diff --git a/chrome/test/data/extensions/api_test/font_settings/incognito/launch.js b/chrome/test/data/extensions/api_test/font_settings/incognito/launch.js
index 54e68fc3..5013978 100644
--- a/chrome/test/data/extensions/api_test/font_settings/incognito/launch.js
+++ b/chrome/test/data/extensions/api_test/font_settings/incognito/launch.js
@@ -52,10 +52,16 @@
     var message = 'getFontList should return an array of objects with ' +
         'fontId and displayName properties.';
     fs.getFontList(chrome.test.callbackPass(function(value) {
-      chrome.test.assertTrue(value.length > 0,
-                             "Font list is not expected to be empty.");
-      chrome.test.assertEq('string', typeof(value[0].fontId), message);
-      chrome.test.assertEq('string', typeof(value[0].displayName), message);
+      if (/Android/.test(navigator.userAgent)) {
+        // Android does not support a mechanism to get "all installed fonts"
+        // like Windows/Mac/Linux.
+        chrome.test.assertTrue(value.length === 0, 'Font list should be empty');
+      } else {
+        chrome.test.assertTrue(value.length > 0,
+          "Font list is not expected to be empty.");
+        chrome.test.assertEq('string', typeof (value[0].fontId), message);
+        chrome.test.assertEq('string', typeof (value[0].displayName), message);
+      }
     }));
   },
 
@@ -100,12 +106,11 @@
 
    fs.clearFont({
       genericFamily: genericFamily,
-    }, chrome.test.callbackFail(SET_FROM_INCOGNITO_ERROR));
+   }, chrome.test.callbackFail(SET_FROM_INCOGNITO_ERROR));
   },
 
   function clearDefaultFontSize() {
     fs.clearDefaultFontSize({},
-                            chrome.test.callbackFail(SET_FROM_INCOGNITO_ERROR));
+      chrome.test.callbackFail(SET_FROM_INCOGNITO_ERROR));
   }
 ]);
-
diff --git a/chrome/test/data/extensions/api_test/font_settings/standard/test.js b/chrome/test/data/extensions/api_test/font_settings/standard/test.js
index af7c247..06f6e6d 100644
--- a/chrome/test/data/extensions/api_test/font_settings/standard/test.js
+++ b/chrome/test/data/extensions/api_test/font_settings/standard/test.js
@@ -102,10 +102,16 @@
     var message = 'getFontList should return an array of objects with ' +
         'fontId and displayName properties.';
     fs.getFontList(chrome.test.callbackPass(function(value) {
-      chrome.test.assertTrue(value.length > 0,
-                             'Font list is not expected to be empty.');
-      chrome.test.assertEq('string', typeof(value[0].fontId), message);
-      chrome.test.assertEq('string', typeof(value[0].displayName), message);
+      if (/Android/.test(navigator.userAgent)) {
+        // Android does not support a mechanism to get "all installed fonts"
+        // like Windows/Mac/Linux.
+        chrome.test.assertTrue(value.length === 0, 'Font list should be empty');
+      } else {
+        chrome.test.assertTrue(value.length > 0,
+          "Font list is not expected to be empty.");
+        chrome.test.assertEq('string', typeof (value[0].fontId), message);
+        chrome.test.assertEq('string', typeof (value[0].displayName), message);
+      }
     }));
   },
 
diff --git a/chrome/test/data/webui/chromeos/settings/os_about_page/test_about_page_browser_proxy.ts b/chrome/test/data/webui/chromeos/settings/os_about_page/test_about_page_browser_proxy.ts
index 94e798d2..5bf9e9e 100644
--- a/chrome/test/data/webui/chromeos/settings/os_about_page/test_about_page_browser_proxy.ts
+++ b/chrome/test/data/webui/chromeos/settings/os_about_page/test_about_page_browser_proxy.ts
@@ -26,7 +26,6 @@
     isLts: false,
   };
   private canChangeChannel_ = true;
-  private canChangeFirmware_ = true;
   private regulatoryInfo_: RegulatoryInfo|null = null;
   private tpmFirmwareUpdateStatus_: TpmFirmwareUpdateStatusChangedEvent = {
     updateAvailable: false,
@@ -53,7 +52,6 @@
       'openHelpPage',
       'openFeedbackDialog',
       'canChangeChannel',
-      'canChangeFirmware',
       'getChannelInfo',
       'getVersionInfo',
       'getRegulatoryInfo',
@@ -134,10 +132,6 @@
     this.canChangeChannel_ = canChangeChannel;
   }
 
-  setCanChangeFirmware(canChangeFirmware: boolean): void {
-    this.canChangeFirmware_ = canChangeFirmware;
-  }
-
   setChannels(current: BrowserChannel, target: BrowserChannel): void {
     this.channelInfo_.currentChannel = current;
     this.channelInfo_.targetChannel = target;
@@ -181,11 +175,6 @@
     return Promise.resolve(this.canChangeChannel_);
   }
 
-  canChangeFirmware(): Promise<boolean> {
-    this.methodCalled('canChangeFirmware');
-    return Promise.resolve(this.canChangeFirmware_);
-  }
-
   checkInternetConnection(): Promise<boolean> {
     this.methodCalled('checkInternetConnection');
     return Promise.resolve(this.hasInternetConnection_);
diff --git a/chrome/test/data/webui/settings/safety_hub_unused_site_permissions_module_test.ts b/chrome/test/data/webui/settings/safety_hub_unused_site_permissions_module_test.ts
index 3f925844..9a660bd 100644
--- a/chrome/test/data/webui/settings/safety_hub_unused_site_permissions_module_test.ts
+++ b/chrome/test/data/webui/settings/safety_hub_unused_site_permissions_module_test.ts
@@ -16,7 +16,6 @@
 import {isMac} from 'chrome://resources/js/platform.js';
 import {TestPluralStringProxy} from 'chrome://webui-test/test_plural_string_proxy.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 
 import {TestMetricsBrowserProxy} from './test_metrics_browser_proxy.js';
 import {TestSafetyHubBrowserProxy} from './test_safety_hub_browser_proxy.js';
@@ -556,166 +555,3 @@
     assertUndoToast(false);
   });
 });
-
-// TODO(crbug.com/328773301): Remove after
-// SafetyHubAbusiveNotificationRevocationDisabled is launched.
-suite('SafetyHubAbusiveNotificationRevocationDisabled', function() {
-  let browserProxy: TestSafetyHubBrowserProxy;
-  let pluralString: TestPluralStringProxy;
-  let metricsBrowserProxy: TestMetricsBrowserProxy;
-
-  let testElement: SettingsSafetyHubUnusedSitePermissionsModuleElement;
-
-  const permissions = [
-    ContentSettingsTypes.GEOLOCATION,
-    ContentSettingsTypes.MIC,
-    ContentSettingsTypes.CAMERA,
-    ContentSettingsTypes.NOTIFICATIONS,
-  ];
-
-  const mockData = [1, 2, 3, 4].map(
-      i => ({
-        origin: `https://www.example${i}.com:443`,
-        permissions: permissions.slice(0, i),
-        expiration: '13317004800000000',  // Represents 2023-01-01T00:00:00.
-      }));
-
-  function assertInitialUi() {
-    const expectedSiteCount = mockData.length;
-    assertEquals(getSiteList().length, expectedSiteCount);
-    assertUndoToast(false);
-  }
-
-  /**
-   * Asserts the Undo toast is shown with a correct origin-containing string.
-   * @param stringId The id to retrieve the correct toast string. Provided only
-   *     if shouldBeOpen is true.
-   * @param index The index of the element whose origin is in the toast string.
-   *     Provided only if shouldBeOpen is true. The default value is 0.
-   */
-  function assertUndoToast(
-      shouldBeOpen: boolean, stringId?: string, index?: number) {
-    const undoToast = testElement.$.undoToast;
-    if (!shouldBeOpen) {
-      assertFalse(undoToast.open);
-      return;
-    }
-    assertTrue(undoToast.open);
-
-    if (stringId) {
-      if (!index) {
-        index = 0;
-      }
-      const expectedText = testElement.i18n(stringId, mockData[index]!.origin);
-      const actualText = undoToast.querySelector('div')!.textContent!.trim();
-      assertEquals(expectedText, actualText);
-    }
-  }
-
-  /**
-   * Assert expected plural string is populated. Whenever getPluralString is
-   * called, TestPluralStringProxy stacks them in args. If getPluralString is
-   * called multiple times, passing 'index' will make the corresponding callback
-   * checked.
-   */
-  async function assertPluralString(
-      messageName: string, itemCount: number, index: number = 0) {
-    await pluralString.whenCalled('getPluralString');
-    const params = pluralString.getArgs('getPluralString')[index];
-    await flushTasks();
-    assertEquals(messageName, params.messageName);
-    assertEquals(itemCount, params.itemCount);
-    pluralString.resetResolver('getPluralString');
-  }
-
-  function getSiteList(): NodeListOf<HTMLElement> {
-    return testElement.$.module.shadowRoot!.querySelectorAll('.site-entry');
-  }
-
-  async function createPage() {
-    document.body.innerHTML = window.trustedTypes!.emptyHTML;
-    testElement =
-        document.createElement('settings-safety-hub-unused-site-permissions');
-    Router.getInstance().navigateTo(routes.SAFETY_HUB);
-    document.body.appendChild(testElement);
-    // Wait until the element has asked for the list of revoked permissions
-    // that will be shown for review.
-    await browserProxy.whenCalled('getRevokedUnusedSitePermissionsList');
-    await flushTasks();
-  }
-
-  setup(async function() {
-    browserProxy = new TestSafetyHubBrowserProxy();
-    browserProxy.setUnusedSitePermissions(mockData);
-    SafetyHubBrowserProxyImpl.setInstance(browserProxy);
-    metricsBrowserProxy = new TestMetricsBrowserProxy();
-    MetricsBrowserProxyImpl.setInstance(metricsBrowserProxy);
-    pluralString = new TestPluralStringProxy();
-    SettingsPluralStringProxyImpl.setInstance(pluralString);
-    loadTimeData.overrideValues({
-      safetyHubAbusiveNotificationRevocationEnabled: false,
-    });
-    resetRouterForTesting();
-    await createPage();
-    metricsBrowserProxy.reset();
-    assertInitialUi();
-  });
-
-  test('Subheader Strings', async function() {
-    // Check header string for plural case.
-    let entries = getSiteList();
-    assertEquals(4, entries.length);
-    await assertPluralString(
-        'safetyHubUnusedSitePermissionsSecondaryLabel', 4, 1);
-
-    // Check header string for singular case.
-    const oneElementMockData = mockData.slice(0, 1);
-    webUIListenerCallback(
-        SafetyHubEvent.UNUSED_PERMISSIONS_MAYBE_CHANGED, oneElementMockData);
-    await flushTasks();
-
-    entries = getSiteList();
-    assertEquals(1, entries.length);
-    await assertPluralString(
-        'safetyHubUnusedSitePermissionsSecondaryLabel', 1, 1);
-  });
-
-  test('Unused Site Permission strings', function() {
-    const siteList = getSiteList();
-    assertEquals(4, siteList.length);
-
-    // Check that the text describing the permissions is correct.
-    assertEquals(
-        mockData[0]!.origin,
-        getSiteList()[0]!.querySelector(
-                             '.site-representation')!.textContent!.trim());
-    assertEquals(
-        'Removed location',
-        getSiteList()[0]!.querySelector(
-                             '.cr-secondary-text')!.textContent!.trim());
-
-    assertEquals(
-        mockData[1]!.origin,
-        siteList[1]!.querySelector(
-                        '.site-representation')!.textContent!.trim());
-    assertEquals(
-        'Removed location, microphone',
-        siteList[1]!.querySelector('.cr-secondary-text')!.textContent!.trim());
-
-    assertEquals(
-        mockData[2]!.origin,
-        siteList[2]!.querySelector(
-                        '.site-representation')!.textContent!.trim());
-    assertEquals(
-        'Removed location, microphone, camera',
-        siteList[2]!.querySelector('.cr-secondary-text')!.textContent!.trim());
-
-    assertEquals(
-        mockData[3]!.origin,
-        siteList[3]!.querySelector(
-                        '.site-representation')!.textContent!.trim());
-    assertEquals(
-        'Removed location, microphone, and 2 more',
-        siteList[3]!.querySelector('.cr-secondary-text')!.textContent!.trim());
-  });
-});
diff --git a/chrome/test/interaction/interaction_test_util_browser_browsertest.cc b/chrome/test/interaction/interaction_test_util_browser_browsertest.cc
index 4c6a4bc1..7c0b591 100644
--- a/chrome/test/interaction/interaction_test_util_browser_browsertest.cc
+++ b/chrome/test/interaction/interaction_test_util_browser_browsertest.cc
@@ -101,7 +101,8 @@
                                       kTitleElementId);
 
 IN_PROC_BROWSER_TEST_F(InteractionTestUtilBrowserTest,
-                       CompareScreenshot_Surface) {
+                       // TODO(crbug.com/422631121): Re-enable this test
+                       DISABLED_CompareScreenshot_Surface) {
   views::Widget* widget = nullptr;
 
   RunTestSequence(
diff --git a/chrome/updater/apply_updater_branding.gni b/chrome/updater/apply_updater_branding.gni
index c8c7527..a87aad7b 100644
--- a/chrome/updater/apply_updater_branding.gni
+++ b/chrome/updater/apply_updater_branding.gni
@@ -37,12 +37,12 @@
     "-e",
     "CRASH_UPLOAD_URL=\"$crash_upload_url\"",
     "-e",
-    "DEVICE_MANAGEMENT_SERVER_URL=\"$device_management_server_url\"",
-    "-e",
     "HELP_CENTER_URL=\"$help_center_url\"",
     "-e",
     "APP_LOGO_URL=\"$app_logo_url\"",
     "-e",
+    "UPDATER_EVENT_LOGGING_URL=\"$updater_event_logging_url\"",
+    "-e",
     "KEYSTONE_NAME=\"$keystone_app_name\"",
     "-e",
     "PRIVILEGED_HELPER_NAME=\"$privileged_helper_name\"",
diff --git a/chrome/updater/branding.gni b/chrome/updater/branding.gni
index 43539ce..50e9e0c 100644
--- a/chrome/updater/branding.gni
+++ b/chrome/updater/branding.gni
@@ -107,4 +107,4 @@
 
 # Chrome and Chromium share the same endpoints for now.
 update_check_url = "https://update.googleapis.com/service/update2/json"
-device_management_server_url = "https://m.google.com/devicemanagement/data/api"
+updater_event_logging_url = "https://play.googleapis.com/log"
diff --git a/chrome/updater/configurator.cc b/chrome/updater/configurator.cc
index 2dc00834..efbdf61 100644
--- a/chrome/updater/configurator.cc
+++ b/chrome/updater/configurator.cc
@@ -18,6 +18,7 @@
 #include "base/rand_util.h"
 #include "base/sequence_checker.h"
 #include "base/strings/to_string.h"
+#include "base/time/default_clock.h"
 #include "base/time/time.h"
 #include "base/version.h"
 #include "build/build_config.h"
@@ -30,6 +31,7 @@
 #include "chrome/updater/policy/service.h"
 #include "chrome/updater/prefs.h"
 #include "chrome/updater/updater_scope.h"
+#include "chrome/updater/usage_stats_permissions.h"
 #include "chrome/updater/util/util.h"
 #include "components/crash/core/common/crash_key.h"
 #include "components/crx_file/crx_verifier.h"
@@ -42,6 +44,7 @@
 #include "components/update_client/unzip/in_process_unzipper.h"
 #include "components/update_client/unzipper.h"
 #include "components/version_info/version_info.h"
+#include "event_logger.h"
 #include "url/gurl.h"
 
 #if BUILDFLAG(IS_WIN)
@@ -81,6 +84,17 @@
           base::MakeRefCounted<update_client::InProcessPatcherFactory>()),
       crx_cache_(base::MakeRefCounted<update_client::CrxCache>(
           GetCrxCacheDirectory(scope))),
+      event_logger_(RemoteEventLoggingAllowed(
+                        scope,
+                        external_constants->GetEventLoggingPermissionProvider())
+                        ? UpdaterEventLogger::Create(
+                              std::make_unique<RemoteLoggingDelegate>(
+                                  scope,
+                                  external_constants->EventLoggingURL(),
+                                  IsCloudManaged(),
+                                  base::WrapRefCounted(this),
+                                  std::make_unique<base::DefaultClock>()))
+                        : nullptr),
       is_managed_device_([] {
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
         return base::IsManagedOrEnterpriseDevice();
@@ -143,11 +157,6 @@
   return external_constants_->CrashUploadURL();
 }
 
-GURL Configurator::DeviceManagementURL() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return external_constants_->DeviceManagementURL();
-}
-
 std::string Configurator::GetProdId() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return "updater";
@@ -242,6 +251,11 @@
   return persisted_data_;
 }
 
+scoped_refptr<UpdaterEventLogger> Configurator::GetEventLogger() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return event_logger_;
+}
+
 bool Configurator::IsPerUserInstall() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return !IsSystemInstall();
diff --git a/chrome/updater/configurator.h b/chrome/updater/configurator.h
index 7d14e39..b2b146a 100644
--- a/chrome/updater/configurator.h
+++ b/chrome/updater/configurator.h
@@ -14,6 +14,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/sequence_checker.h"
 #include "base/time/time.h"
+#include "chrome/updater/event_logger.h"
 #include "components/update_client/configurator.h"
 
 class GURL;
@@ -83,8 +84,8 @@
   bool IsConnectionMetered() const override;
 
   scoped_refptr<PersistedData> GetUpdaterPersistedData() const;
+  scoped_refptr<UpdaterEventLogger> GetEventLogger() const;
   virtual GURL CrashUploadURL() const;
-  virtual GURL DeviceManagementURL() const;
 
   base::TimeDelta ServerKeepAliveTime() const;
   scoped_refptr<PolicyService> GetPolicyService() const;
@@ -105,6 +106,7 @@
   scoped_refptr<update_client::UnzipperFactory> unzip_factory_;
   scoped_refptr<update_client::PatcherFactory> patch_factory_;
   scoped_refptr<update_client::CrxCache> crx_cache_;
+  scoped_refptr<UpdaterEventLogger> event_logger_;
   const std::optional<bool> is_managed_device_;
 };
 
diff --git a/chrome/updater/constants.h b/chrome/updater/constants.h
index a84b5a6..3b390e9f 100644
--- a/chrome/updater/constants.h
+++ b/chrome/updater/constants.h
@@ -252,9 +252,8 @@
 // Developer override keys.
 inline constexpr char kDevOverrideKeyUrl[] = "url";
 inline constexpr char kDevOverrideKeyCrashUploadUrl[] = "crash_upload_url";
-inline constexpr char kDevOverrideKeyDeviceManagementUrl[] =
-    "device_management_url";
 inline constexpr char kDevOverrideKeyAppLogoUrl[] = "app_logo_url";
+inline constexpr char kDevOverrideKeyEventLoggingUrl[] = "event_logging_url";
 inline constexpr char kDevOverrideKeyUseCUP[] = "use_cup";
 inline constexpr char kDevOverrideKeyInitialDelay[] = "initial_delay";
 inline constexpr char kDevOverrideKeyServerKeepAliveSeconds[] =
diff --git a/chrome/updater/event_logger.cc b/chrome/updater/event_logger.cc
index debd344..51b789d7 100644
--- a/chrome/updater/event_logger.cc
+++ b/chrome/updater/event_logger.cc
@@ -31,6 +31,7 @@
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/updater_version.h"
 #include "components/update_client/network.h"
+#include "event_logger.h"
 #include "mojo/public/cpp/bindings/callback_helpers.h"
 #include "url/gurl.h"
 
diff --git a/chrome/updater/event_logger.h b/chrome/updater/event_logger.h
index 2f6e19c..58f6e3e 100644
--- a/chrome/updater/event_logger.h
+++ b/chrome/updater/event_logger.h
@@ -17,7 +17,6 @@
 #include "base/sequence_checker.h"
 #include "base/time/time.h"
 #include "chrome/enterprise_companion/telemetry_logger/telemetry_logger.h"
-#include "chrome/updater/configurator.h"
 #include "chrome/updater/persisted_data.h"
 #include "chrome/updater/protos/omaha_usage_stats_event.pb.h"
 #include "chrome/updater/updater_scope.h"
@@ -34,19 +33,13 @@
 class Omaha4UsageStatsMetadata;
 }  // namespace proto
 
-class UpdaterEventLogger
-    : public base::RefCountedThreadSafe<UpdaterEventLogger> {
- public:
-  void LogNetworkEvent(const proto::NetworkEvent& event);
+class Configurator;
 
- private:
-  friend class base::RefCountedThreadSafe<UpdaterEventLogger>;
-  virtual ~UpdaterEventLogger() = default;
-};
+using UpdaterEventLogger =
+    ::enterprise_companion::telemetry_logger::TelemetryLogger<
+        proto::Omaha4Metric>;
 
-class RemoteLoggingDelegate final
-    : enterprise_companion::telemetry_logger::TelemetryLogger<
-          proto::Omaha4Metric>::Delegate {
+class RemoteLoggingDelegate final : public UpdaterEventLogger::Delegate {
  public:
   RemoteLoggingDelegate(UpdaterScope scope,
                         const GURL& event_logging_url,
@@ -56,7 +49,7 @@
 
   ~RemoteLoggingDelegate() override;
 
-  // Overrides for TelemetryLogger.
+  // Overrides for TelemetryLogger::Delegate.
   bool StoreNextAllowedAttemptTime(base::Time time) override;
   std::optional<base::Time> GetNextAllowedAttemptTime() const override;
   void DoPostRequest(
diff --git a/chrome/updater/external_constants.h b/chrome/updater/external_constants.h
index c3c459b..c890016 100644
--- a/chrome/updater/external_constants.h
+++ b/chrome/updater/external_constants.h
@@ -38,12 +38,12 @@
   // The URL to send crash reports to.
   virtual GURL CrashUploadURL() const = 0;
 
-  // The URL to fetch device management policies.
-  virtual GURL DeviceManagementURL() const = 0;
-
   // The URL for the app logos.
   virtual GURL AppLogoURL() const = 0;
 
+  // The URL for remote event logging.
+  virtual GURL EventLoggingURL() const = 0;
+
   // True if client update protocol signing of update checks is enabled.
   virtual bool UseCUP() const = 0;
 
diff --git a/chrome/updater/external_constants_builder.cc b/chrome/updater/external_constants_builder.cc
index 316f588da4..7ffc2a02 100644
--- a/chrome/updater/external_constants_builder.cc
+++ b/chrome/updater/external_constants_builder.cc
@@ -75,17 +75,6 @@
   return *this;
 }
 
-ExternalConstantsBuilder& ExternalConstantsBuilder::SetDeviceManagementURL(
-    const std::string& url) {
-  overrides_.Set(kDevOverrideKeyDeviceManagementUrl, url);
-  return *this;
-}
-
-ExternalConstantsBuilder& ExternalConstantsBuilder::ClearDeviceManagementURL() {
-  overrides_.Remove(kDevOverrideKeyDeviceManagementUrl);
-  return *this;
-}
-
 ExternalConstantsBuilder& ExternalConstantsBuilder::SetAppLogoURL(
     const std::string& url) {
   overrides_.Set(kDevOverrideKeyAppLogoUrl, url);
@@ -238,10 +227,6 @@
   if (!overrides_.contains(kDevOverrideKeyCrashUploadUrl)) {
     SetCrashUploadURL(verifier->CrashUploadURL().possibly_invalid_spec());
   }
-  if (!overrides_.contains(kDevOverrideKeyDeviceManagementUrl)) {
-    SetDeviceManagementURL(
-        verifier->DeviceManagementURL().possibly_invalid_spec());
-  }
   if (!overrides_.contains(kDevOverrideKeyAppLogoUrl)) {
     SetAppLogoURL(verifier->AppLogoURL().possibly_invalid_spec());
   }
diff --git a/chrome/updater/external_constants_builder.h b/chrome/updater/external_constants_builder.h
index 40ab41b..a86dc7d 100644
--- a/chrome/updater/external_constants_builder.h
+++ b/chrome/updater/external_constants_builder.h
@@ -45,9 +45,6 @@
   ExternalConstantsBuilder& SetCrashUploadURL(const std::string& url);
   ExternalConstantsBuilder& ClearCrashUploadURL();
 
-  ExternalConstantsBuilder& SetDeviceManagementURL(const std::string& url);
-  ExternalConstantsBuilder& ClearDeviceManagementURL();
-
   ExternalConstantsBuilder& SetAppLogoURL(const std::string& url);
   ExternalConstantsBuilder& ClearAppLogoURL();
 
diff --git a/chrome/updater/external_constants_builder_unittest.cc b/chrome/updater/external_constants_builder_unittest.cc
index b4f9a259..c2eb0b0 100644
--- a/chrome/updater/external_constants_builder_unittest.cc
+++ b/chrome/updater/external_constants_builder_unittest.cc
@@ -69,7 +69,6 @@
   ExternalConstantsBuilder builder;
   builder.SetUpdateURL(std::vector<std::string>{"https://localhost/1/www"})
       .SetCrashUploadURL("https://localhost/1/crash")
-      .SetDeviceManagementURL("https://localhost/1/dm")
       .SetAppLogoURL("https://localhost/1/applogo/")
       .SetUseCUP(false)
       .SetInitialDelay(base::Seconds(123))
@@ -92,7 +91,6 @@
   EXPECT_EQ(urls[0], GURL("https://localhost/1/www"));
 
   EXPECT_EQ(verifier->CrashUploadURL(), GURL("https://localhost/1/crash"));
-  EXPECT_EQ(verifier->DeviceManagementURL(), GURL("https://localhost/1/dm"));
   EXPECT_EQ(verifier->AppLogoURL(), GURL("https://localhost/1/applogo/"));
   EXPECT_EQ(verifier->InitialDelay(), base::Seconds(123));
   EXPECT_EQ(verifier->ServerKeepAliveTime(), base::Seconds(2));
@@ -123,8 +121,6 @@
   EXPECT_EQ(urls[1], GURL("https://localhost/update2"));
 
   EXPECT_EQ(verifier->CrashUploadURL(), GURL(CRASH_UPLOAD_URL));
-  EXPECT_EQ(verifier->DeviceManagementURL(),
-            GURL(DEVICE_MANAGEMENT_SERVER_URL));
   EXPECT_EQ(verifier->AppLogoURL(), GURL(APP_LOGO_URL));
   EXPECT_EQ(verifier->InitialDelay(), kInitialDelay);
   EXPECT_EQ(verifier->ServerKeepAliveTime(), kServerKeepAliveTime);
@@ -137,13 +133,11 @@
                   .SetUpdateURL(std::vector<std::string>{
                       "https://localhost/1/localhost/update", "https://www"})
                   .SetCrashUploadURL("https://localhost/1/crash")
-                  .SetDeviceManagementURL("https://localhost/1/dm")
                   .SetAppLogoURL("https://localhost/1/applogo/")
                   .SetUseCUP(false)
                   .SetInitialDelay(base::Seconds(123.4))
                   .ClearUpdateURL()
                   .ClearCrashUploadURL()
-                  .ClearDeviceManagementURL()
                   .ClearAppLogoURL()
                   .ClearUseCUP()
                   .ClearInitialDelay()
@@ -165,8 +159,6 @@
   EXPECT_EQ(urls[0], GURL(UPDATE_CHECK_URL));
 
   EXPECT_EQ(verifier->CrashUploadURL(), GURL(CRASH_UPLOAD_URL));
-  EXPECT_EQ(verifier->DeviceManagementURL(),
-            GURL(DEVICE_MANAGEMENT_SERVER_URL));
   EXPECT_EQ(verifier->AppLogoURL(), GURL(APP_LOGO_URL));
   EXPECT_EQ(verifier->InitialDelay(), kInitialDelay);
   EXPECT_EQ(verifier->ServerKeepAliveTime(), kServerKeepAliveTime);
@@ -183,7 +175,6 @@
       ExternalConstantsBuilder()
           .SetUpdateURL(std::vector<std::string>{"https://localhost/update"})
           .SetCrashUploadURL("https://localhost/crash")
-          .SetDeviceManagementURL("https://localhost/dm")
           .SetAppLogoURL("https://localhost/logo/")
           .SetUseCUP(true)
           .SetInitialDelay(base::Seconds(123.4))
@@ -192,7 +183,6 @@
           .SetDictPolicies(dict_policies)
           .SetUpdateURL(std::vector<std::string>{"https://localhost/1/www"})
           .SetCrashUploadURL("https://localhost/1/crash")
-          .SetDeviceManagementURL("https://localhost/1/dm")
           .SetAppLogoURL("https://localhost/1/applogo/")
           .SetUseCUP(false)
           .SetInitialDelay(base::Seconds(937.6))
@@ -212,7 +202,6 @@
   EXPECT_EQ(urls[0], GURL("https://localhost/1/www"));
 
   EXPECT_EQ(verifier->CrashUploadURL(), GURL("https://localhost/1/crash"));
-  EXPECT_EQ(verifier->DeviceManagementURL(), GURL("https://localhost/1/dm"));
   EXPECT_EQ(verifier->AppLogoURL(), GURL("https://localhost/1/applogo/"));
   EXPECT_EQ(verifier->InitialDelay(), base::Seconds(937.6));
   EXPECT_EQ(verifier->ServerKeepAliveTime(), base::Seconds(3));
@@ -232,7 +221,6 @@
   EXPECT_TRUE(
       builder.SetUpdateURL(std::vector<std::string>{"https://localhost/update"})
           .SetCrashUploadURL("https://localhost/crash")
-          .SetDeviceManagementURL("https://localhost/dm")
           .SetAppLogoURL("https://localhost/logo/")
           .SetUseCUP(false)
           .SetInitialDelay(base::Seconds(123.4))
@@ -254,7 +242,6 @@
   EXPECT_EQ(urls[0], GURL("https://localhost/1/www"));
 
   EXPECT_EQ(verifier->CrashUploadURL(), GURL("https://localhost/crash"));
-  EXPECT_EQ(verifier->DeviceManagementURL(), GURL("https://localhost/dm"));
   EXPECT_EQ(verifier->AppLogoURL(), GURL("https://localhost/logo/"));
   EXPECT_EQ(verifier->InitialDelay(), base::Seconds(123.4));
   EXPECT_EQ(verifier->ServerKeepAliveTime(), base::Seconds(3));
@@ -271,7 +258,6 @@
                   .SetServerKeepAliveTime(base::Seconds(4))
                   .ClearUpdateURL()
                   .ClearCrashUploadURL()
-                  .ClearDeviceManagementURL()
                   .ClearAppLogoURL()
                   .SetDictPolicies(dict_policies2)
                   .ClearMachineManaged()
@@ -290,8 +276,6 @@
   EXPECT_EQ(urls2[0], GURL(UPDATE_CHECK_URL));  // Cleared; should be default.
 
   EXPECT_EQ(verifier2->CrashUploadURL(), GURL(CRASH_UPLOAD_URL));
-  EXPECT_EQ(verifier2->DeviceManagementURL(),
-            GURL(DEVICE_MANAGEMENT_SERVER_URL));
   EXPECT_EQ(verifier2->AppLogoURL(), GURL(APP_LOGO_URL));
   EXPECT_EQ(verifier2->InitialDelay(),
             base::Seconds(92.3));  // Updated; update should be seen.
@@ -311,14 +295,12 @@
   EXPECT_TRUE(
       builder.SetUpdateURL(std::vector<std::string>{"https://localhost/update"})
           .SetCrashUploadURL("https://localhost/crash")
-          .SetDeviceManagementURL("https://localhost/dm")
           .SetAppLogoURL("https://localhost/logo/")
           .SetUseCUP(false)
           .SetInitialDelay(base::Seconds(123.4))
           .SetServerKeepAliveTime(base::Seconds(3))
           .SetUpdateURL(std::vector<std::string>{"https://localhost/1/www"})
           .SetCrashUploadURL("https://localhost/1/crash")
-          .SetDeviceManagementURL("https://localhost/1/dm")
           .SetAppLogoURL("https://localhost/1/applogo/")
           .SetDictPolicies(dict_policies)
           .SetMachineManaged(std::make_optional(false))
@@ -336,7 +318,6 @@
   EXPECT_EQ(urls[0], GURL("https://localhost/1/www"));
 
   EXPECT_EQ(verifier->CrashUploadURL(), GURL("https://localhost/1/crash"));
-  EXPECT_EQ(verifier->DeviceManagementURL(), GURL("https://localhost/1/dm"));
   EXPECT_EQ(verifier->AppLogoURL(), GURL("https://localhost/1/applogo/"));
   EXPECT_EQ(verifier->InitialDelay(), base::Seconds(123.4));
   EXPECT_EQ(verifier->ServerKeepAliveTime(), base::Seconds(3));
@@ -367,7 +348,6 @@
   ASSERT_EQ(urls.size(), 1ul);
   EXPECT_EQ(urls[0], GURL("https://localhost/1/www"));
   EXPECT_EQ(verifier2->CrashUploadURL(), GURL("https://localhost/1/crash"));
-  EXPECT_EQ(verifier2->DeviceManagementURL(), GURL("https://localhost/1/dm"));
   EXPECT_EQ(verifier2->AppLogoURL(), GURL("https://localhost/1/applogo/"));
   EXPECT_EQ(verifier2->InitialDelay(), base::Seconds(123.4));
   EXPECT_EQ(verifier2->ServerKeepAliveTime(), base::Seconds(3));
diff --git a/chrome/updater/external_constants_default.cc b/chrome/updater/external_constants_default.cc
index 690899d..52eb003 100644
--- a/chrome/updater/external_constants_default.cc
+++ b/chrome/updater/external_constants_default.cc
@@ -32,12 +32,12 @@
 
   GURL CrashUploadURL() const override { return GURL(CRASH_UPLOAD_URL); }
 
-  GURL DeviceManagementURL() const override {
-    return GURL(DEVICE_MANAGEMENT_SERVER_URL);
-  }
-
   GURL AppLogoURL() const override { return GURL(APP_LOGO_URL); }
 
+  GURL EventLoggingURL() const override {
+    return GURL(UPDATER_EVENT_LOGGING_URL);
+  }
+
   bool UseCUP() const override { return true; }
 
   base::TimeDelta InitialDelay() const override { return kInitialDelay; }
diff --git a/chrome/updater/external_constants_override.cc b/chrome/updater/external_constants_override.cc
index 7e952a1..e43bf2d 100644
--- a/chrome/updater/external_constants_override.cc
+++ b/chrome/updater/external_constants_override.cc
@@ -119,18 +119,6 @@
   return CheckURL({GURL(crash_upload_url_value->GetString())});
 }
 
-GURL ExternalConstantsOverrider::DeviceManagementURL() const {
-  if (!override_values_.contains(kDevOverrideKeyDeviceManagementUrl)) {
-    return next_provider_->DeviceManagementURL();
-  }
-  const base::Value* device_management_url_value =
-      override_values_.Find(kDevOverrideKeyDeviceManagementUrl);
-  CHECK(device_management_url_value->is_string())
-      << "Unexpected type of override[" << kDevOverrideKeyDeviceManagementUrl
-      << "]: " << base::Value::GetTypeName(device_management_url_value->type());
-  return CheckURL({GURL(device_management_url_value->GetString())});
-}
-
 GURL ExternalConstantsOverrider::AppLogoURL() const {
   if (!override_values_.contains(kDevOverrideKeyAppLogoUrl)) {
     return next_provider_->AppLogoURL();
@@ -143,6 +131,18 @@
   return CheckURL({GURL(app_logo_url_value->GetString())});
 }
 
+GURL ExternalConstantsOverrider::EventLoggingURL() const {
+  if (!override_values_.contains(kDevOverrideKeyEventLoggingUrl)) {
+    return next_provider_->EventLoggingURL();
+  }
+  const base::Value* event_logging_url_value =
+      override_values_.Find(kDevOverrideKeyEventLoggingUrl);
+  CHECK(event_logging_url_value->is_string())
+      << "Unexpected type of override[" << kDevOverrideKeyEventLoggingUrl
+      << "]: " << base::Value::GetTypeName(event_logging_url_value->type());
+  return CheckURL({GURL(event_logging_url_value->GetString())});
+}
+
 bool ExternalConstantsOverrider::UseCUP() const {
   if (!override_values_.contains(kDevOverrideKeyUseCUP)) {
     return next_provider_->UseCUP();
diff --git a/chrome/updater/external_constants_override.h b/chrome/updater/external_constants_override.h
index 9757fea..4764c299 100644
--- a/chrome/updater/external_constants_override.h
+++ b/chrome/updater/external_constants_override.h
@@ -49,8 +49,8 @@
   // Overrides of ExternalConstants:
   std::vector<GURL> UpdateURL() const override;
   GURL CrashUploadURL() const override;
-  GURL DeviceManagementURL() const override;
   GURL AppLogoURL() const override;
+  GURL EventLoggingURL() const override;
   bool UseCUP() const override;
   base::TimeDelta InitialDelay() const override;
   base::TimeDelta ServerKeepAliveTime() const override;
diff --git a/chrome/updater/external_constants_override_unittest.cc b/chrome/updater/external_constants_override_unittest.cc
index b478acc..972f9e45 100644
--- a/chrome/updater/external_constants_override_unittest.cc
+++ b/chrome/updater/external_constants_override_unittest.cc
@@ -34,9 +34,6 @@
 
   EXPECT_EQ(overrider->CrashUploadURL(), GURL(CRASH_UPLOAD_URL));
   EXPECT_TRUE(overrider->CrashUploadURL().is_valid());
-  EXPECT_EQ(overrider->DeviceManagementURL(),
-            GURL(DEVICE_MANAGEMENT_SERVER_URL));
-  EXPECT_TRUE(overrider->DeviceManagementURL().is_valid());
   EXPECT_EQ(overrider->AppLogoURL(), GURL(APP_LOGO_URL));
   EXPECT_TRUE(overrider->AppLogoURL().is_valid());
 
@@ -59,7 +56,6 @@
   overrides.Set(kDevOverrideKeyUrl, std::move(url_list));
   overrides.Set(kDevOverrideKeyCrashUploadUrl,
                 "https://localhost/2/crash_test");
-  overrides.Set(kDevOverrideKeyDeviceManagementUrl, "https://localhost/2/dm");
   overrides.Set(kDevOverrideKeyAppLogoUrl, "https://localhost/2/applogo/");
   overrides.Set(kDevOverrideKeyInitialDelay, 137.1);
   overrides.Set(kDevOverrideKeyServerKeepAliveSeconds, 1);
@@ -82,8 +78,6 @@
   EXPECT_EQ(overrider->CrashUploadURL(),
             GURL("https://localhost/2/crash_test"));
   EXPECT_TRUE(overrider->CrashUploadURL().is_valid());
-  EXPECT_EQ(overrider->DeviceManagementURL(), GURL("https://localhost/2/dm"));
-  EXPECT_TRUE(overrider->DeviceManagementURL().is_valid());
   EXPECT_EQ(overrider->AppLogoURL(), GURL("https://localhost/2/applogo/"));
   EXPECT_TRUE(overrider->AppLogoURL().is_valid());
 
diff --git a/chrome/updater/policy/service.cc b/chrome/updater/policy/service.cc
index a544969..3f2c58d 100644
--- a/chrome/updater/policy/service.cc
+++ b/chrome/updater/policy/service.cc
@@ -150,9 +150,11 @@
 void PolicyService::FetchPolicies(policy::PolicyFetchReason reason,
                                   base::OnceCallback<void(int)> callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  IsCloudManaged(base::BindOnce(&PolicyService::DoFetchPolicies,
-                                base::WrapRefCounted(this), reason,
-                                std::move(callback)));
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE, {base::MayBlock(), base::WithBaseSyncPrimitives()},
+      base::BindOnce(&IsCloudManaged),
+      base::BindOnce(&PolicyService::DoFetchPolicies,
+                     base::WrapRefCounted(this), reason, std::move(callback)));
 }
 
 void PolicyService::DoFetchPolicies(policy::PolicyFetchReason reason,
@@ -648,20 +650,6 @@
   return are_updates_suppressed;
 }
 
-void PolicyService::IsCloudManaged(
-    base::OnceCallback<void(bool)> callback) const {
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE, {base::MayBlock(), base::WithBaseSyncPrimitives()},
-      base::BindOnce([] {
-        scoped_refptr<device_management_storage::DMStorage> dm_storage =
-            device_management_storage::GetDefaultDMStorage();
-        return dm_storage && (dm_storage->IsValidDMToken() ||
-                              (!dm_storage->GetEnrollmentToken().empty() &&
-                               !dm_storage->IsDeviceDeregistered()));
-      }),
-      std::move(callback));
-}
-
 template <typename T, typename U>
 PolicyStatus<U> PolicyService::QueryPolicy(
     PolicyQueryFunction<T> policy_query_function,
@@ -769,4 +757,12 @@
   return policy_service_proxy_configuration;
 }
 
+bool IsCloudManaged() {
+  scoped_refptr<device_management_storage::DMStorage> dm_storage =
+      device_management_storage::GetDefaultDMStorage();
+  return dm_storage && (dm_storage->IsValidDMToken() ||
+                        (!dm_storage->GetEnrollmentToken().empty() &&
+                         !dm_storage->IsDeviceDeregistered()));
+}
+
 }  // namespace updater
diff --git a/chrome/updater/policy/service.h b/chrome/updater/policy/service.h
index 9beb7cfe..2dbfaba 100644
--- a/chrome/updater/policy/service.h
+++ b/chrome/updater/policy/service.h
@@ -171,10 +171,6 @@
   std::string GetAllPoliciesAsString() const;
   bool AreUpdatesSuppressedNow(base::Time now = base::Time::Now()) const;
 
-  // Queries whether the machine appears to be cloud managed by Chrome
-  // Enterprise Core (formerly Chrome Enterprise Cloud Management).
-  void IsCloudManaged(base::OnceCallback<void(bool)> callback) const;
-
   void SetManagersForTesting(
       std::vector<scoped_refptr<PolicyManagerInterface>> managers);
 
@@ -255,6 +251,11 @@
   std::optional<std::string> proxy_url;
 };
 
+// Queries whether the machine appears to be cloud managed by Chrome
+// Enterprise Core (formerly Chrome Enterprise Cloud Management). Performs
+// blocking IO.
+bool IsCloudManaged();
+
 }  // namespace updater
 
 #endif  // CHROME_UPDATER_POLICY_SERVICE_H_
diff --git a/chrome/updater/test/integration_test_commands.h b/chrome/updater/test/integration_test_commands.h
index afe6481..29b2a249 100644
--- a/chrome/updater/test/integration_test_commands.h
+++ b/chrome/updater/test/integration_test_commands.h
@@ -35,7 +35,6 @@
  public:
   virtual void EnterTestMode(const GURL& update_url,
                              const GURL& crash_upload_url,
-                             const GURL& device_management_url,
                              const GURL& app_logo_url,
                              base::TimeDelta idle_timeout,
                              base::TimeDelta server_keep_alive_time,
diff --git a/chrome/updater/test/integration_test_commands_system.cc b/chrome/updater/test/integration_test_commands_system.cc
index 0c04d46..49c311dc 100644
--- a/chrome/updater/test/integration_test_commands_system.cc
+++ b/chrome/updater/test/integration_test_commands_system.cc
@@ -147,7 +147,6 @@
 
   void EnterTestMode(const GURL& update_url,
                      const GURL& crash_upload_url,
-                     const GURL& device_management_url,
                      const GURL& app_logo_url,
                      base::TimeDelta idle_timeout,
                      base::TimeDelta server_keep_alive_time,
@@ -156,7 +155,6 @@
         "enter_test_mode",
         {Param("update_url", update_url.spec()),
          Param("crash_upload_url", crash_upload_url.spec()),
-         Param("device_management_url", device_management_url.spec()),
          Param("app_logo_url", app_logo_url.spec()),
          Param("idle_timeout", base::NumberToString(idle_timeout.InSeconds())),
          Param("server_keep_alive_time",
diff --git a/chrome/updater/test/integration_test_commands_user.cc b/chrome/updater/test/integration_test_commands_user.cc
index 9dfef6f..95d182e 100644
--- a/chrome/updater/test/integration_test_commands_user.cc
+++ b/chrome/updater/test/integration_test_commands_user.cc
@@ -85,14 +85,13 @@
 
   void EnterTestMode(const GURL& update_url,
                      const GURL& crash_upload_url,
-                     const GURL& device_management_url,
                      const GURL& app_logo_url,
                      base::TimeDelta idle_timeout,
                      base::TimeDelta server_keep_alive_time,
                      base::TimeDelta ceca_connection_timeout) const override {
-    updater::test::EnterTestMode(
-        update_url, crash_upload_url, device_management_url, app_logo_url,
-        idle_timeout, server_keep_alive_time, ceca_connection_timeout);
+    updater::test::EnterTestMode(update_url, crash_upload_url, app_logo_url,
+                                 idle_timeout, server_keep_alive_time,
+                                 ceca_connection_timeout);
   }
 
   void ExitTestMode() const override {
diff --git a/chrome/updater/test/integration_tests.cc b/chrome/updater/test/integration_tests.cc
index 69d8332..c12a2b8 100644
--- a/chrome/updater/test/integration_tests.cc
+++ b/chrome/updater/test/integration_tests.cc
@@ -313,9 +313,9 @@
     ASSERT_TRUE(WaitForUpdaterExit());
     ASSERT_NO_FATAL_FAILURE(Clean());
     ASSERT_NO_FATAL_FAILURE(ExpectClean());
-    ASSERT_NO_FATAL_FAILURE(EnterTestMode(
-        GURL("http://localhost:1234"), GURL("http://localhost:1235"),
-        GURL("http://localhost:1236"), {}, base::Minutes(5)));
+    ASSERT_NO_FATAL_FAILURE(EnterTestMode(GURL("http://localhost:1234"),
+                                          GURL("http://localhost:1235"), {},
+                                          base::Minutes(5)));
     ASSERT_NO_FATAL_FAILURE(SetMachineManaged(false));
 #if BUILDFLAG(IS_LINUX)
     // On LUCI the XDG_RUNTIME_DIR and DBUS_SESSION_BUS_ADDRESS environment
@@ -419,14 +419,13 @@
   void EnterTestMode(
       const GURL& update_url,
       const GURL& crash_upload_url,
-      const GURL& device_management_url,
       const GURL& app_logo_url,
       base::TimeDelta idle_timeout,
       base::TimeDelta server_keep_alive_time = base::Seconds(2),
       base::TimeDelta ceca_connection_timeout = base::Seconds(10)) {
-    test_commands_->EnterTestMode(
-        update_url, crash_upload_url, device_management_url, app_logo_url,
-        idle_timeout, server_keep_alive_time, ceca_connection_timeout);
+    test_commands_->EnterTestMode(update_url, crash_upload_url, app_logo_url,
+                                  idle_timeout, server_keep_alive_time,
+                                  ceca_connection_timeout);
   }
 
   void ExitTestMode() { test_commands_->ExitTestMode(); }
@@ -2336,9 +2335,9 @@
     GTEST_SKIP() << "System server startup is complicated on Windows.";
   }
 #endif
-  ASSERT_NO_FATAL_FAILURE(EnterTestMode(
-      GURL("http://localhost:1234"), GURL("http://localhost:1234"),
-      GURL("http://localhost:1234"), {}, base::Seconds(1)));
+  ASSERT_NO_FATAL_FAILURE(EnterTestMode(GURL("http://localhost:1234"),
+                                        GURL("http://localhost:1234"), {},
+                                        base::Seconds(1)));
   ASSERT_NO_FATAL_FAILURE(Install());
   ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
   ASSERT_NO_FATAL_FAILURE(RunServer(kErrorIdle, true));
@@ -4699,7 +4698,6 @@
   ScopedServer test_logo_server(test_commands_);
   EnterTestMode(test_update_server.update_url(),
                 test_update_server.crash_upload_url(),
-                test_update_server.device_management_url(),
                 test_logo_server.app_logo_url(), base::Minutes(5));
 
   const std::string kAppId("googletest");
diff --git a/chrome/updater/test/integration_tests_helper.cc b/chrome/updater/test/integration_tests_helper.cc
index 71fc9aa9..2d18030e 100644
--- a/chrome/updater/test/integration_tests_helper.cc
+++ b/chrome/updater/test/integration_tests_helper.cc
@@ -294,12 +294,10 @@
                        "idle_timeout",
                        WithSwitch(
                            "app_logo_url",
-                           WithSwitch(
-                               "device_management_url",
-                               WithSwitch(
-                                   "crash_upload_url",
-                                   WithSwitch("update_url",
-                                              Wrap(&EnterTestMode))))))))},
+                            WithSwitch(
+                                "crash_upload_url",
+                                WithSwitch("update_url",
+                                           Wrap(&EnterTestMode)))))))},
           {"exit_test_mode", WithSystemScope(Wrap(&ExitTestMode))},
           {"set_dict_policies", WithSwitch("values", Wrap(&SetDictPolicies))},
           {"set_platform_policies",
diff --git a/chrome/updater/test/integration_tests_impl.cc b/chrome/updater/test/integration_tests_impl.cc
index 3a6ed1db..2843598 100644
--- a/chrome/updater/test/integration_tests_impl.cc
+++ b/chrome/updater/test/integration_tests_impl.cc
@@ -495,7 +495,6 @@
 
 void EnterTestMode(const GURL& update_url,
                    const GURL& crash_upload_url,
-                   const GURL& device_management_url,
                    const GURL& app_logo_url,
                    base::TimeDelta idle_timeout,
                    base::TimeDelta server_keep_alive_time,
@@ -504,7 +503,6 @@
       ExternalConstantsBuilder()
           .SetUpdateURL(std::vector<std::string>{update_url.spec()})
           .SetCrashUploadURL(crash_upload_url.spec())
-          .SetDeviceManagementURL(device_management_url.spec())
           .SetAppLogoURL(app_logo_url.spec())
           .SetUseCUP(false)
           .SetInitialDelay(base::Milliseconds(100))
diff --git a/chrome/updater/test/integration_tests_impl.h b/chrome/updater/test/integration_tests_impl.h
index 94429894a..87f00c6d 100644
--- a/chrome/updater/test/integration_tests_impl.h
+++ b/chrome/updater/test/integration_tests_impl.h
@@ -154,7 +154,6 @@
 // Places the updater into test mode (redirect server URLs and disable CUP).
 void EnterTestMode(const GURL& update_url,
                    const GURL& crash_upload_url,
-                   const GURL& device_management_url,
                    const GURL& app_logo_url,
                    base::TimeDelta idle_timeout,
                    base::TimeDelta server_keep_alive_time,
diff --git a/chrome/updater/test/server.cc b/chrome/updater/test/server.cc
index 66374f6..00c2c44 100644
--- a/chrome/updater/test/server.cc
+++ b/chrome/updater/test/server.cc
@@ -77,9 +77,9 @@
 
 void ScopedServer::ConfigureTestMode(IntegrationTestCommands* commands) {
   CHECK(commands);
-  commands->EnterTestMode(update_url(), crash_upload_url(),
-                          device_management_url(), {}, base::Minutes(5),
-                          base::Seconds(2), base::Seconds(10));
+  commands->EnterTestMode(update_url(), crash_upload_url(), {},
+                          base::Minutes(5), base::Seconds(2),
+                          base::Seconds(10));
 }
 
 void ScopedServer::ExpectOnce(request::MatcherGroup request_matcher_group,
diff --git a/chrome/updater/update_service_impl_impl.cc b/chrome/updater/update_service_impl_impl.cc
index 91cf51b..66737c8f 100644
--- a/chrome/updater/update_service_impl_impl.cc
+++ b/chrome/updater/update_service_impl_impl.cc
@@ -726,12 +726,15 @@
   if (!config_->GetUpdaterPersistedData()
            ->GetProductVersion(enterprise_companion::kCompanionAppId)
            .IsValid()) {
-    config_->GetPolicyService()->IsCloudManaged(base::BindOnce(
-        &UpdateServiceImplImpl::MaybeInstallEnterpriseCompanionAppOTA,
-        base::WrapRefCounted(this),
-        base::BindOnce(&PolicyService::FetchPolicies,
-                       config_->GetPolicyService(), reason,
-                       std::move(callback))));
+    base::ThreadPool::PostTaskAndReplyWithResult(
+        FROM_HERE, {base::MayBlock(), base::WithBaseSyncPrimitives()},
+        base::BindOnce(&IsCloudManaged),
+        base::BindOnce(
+            &UpdateServiceImplImpl::MaybeInstallEnterpriseCompanionAppOTA,
+            base::WrapRefCounted(this),
+            base::BindOnce(&PolicyService::FetchPolicies,
+                           config_->GetPolicyService(), reason,
+                           std::move(callback))));
   } else {
     config_->GetPolicyService()->FetchPolicies(reason, std::move(callback));
   }
diff --git a/chrome/updater/updater_branding.h.in b/chrome/updater/updater_branding.h.in
index fa282e9d..3af03317 100644
--- a/chrome/updater/updater_branding.h.in
+++ b/chrome/updater/updater_branding.h.in
@@ -11,10 +11,10 @@
 #define COMPANY_SHORTNAME_UPPERCASE_STRING "@COMPANY_SHORTNAME_UPPERCASE@"
 #define CRASH_UPLOAD_URL "@CRASH_UPLOAD_URL@"
 #define CRASH_PRODUCT_NAME "@CRASH_PRODUCT_NAME@"
-#define DEVICE_MANAGEMENT_SERVER_URL "@DEVICE_MANAGEMENT_SERVER_URL@"
 #define KEYSTONE_NAME "@KEYSTONE_NAME@"
 #define HELP_CENTER_URL "@HELP_CENTER_URL@"
 #define APP_LOGO_URL "@APP_LOGO_URL@"
+#define UPDATER_EVENT_LOGGING_URL "@UPDATER_EVENT_LOGGING_URL@"
 #define MAC_BROWSER_BUNDLE_IDENTIFIER_STRING "@MAC_BROWSER_BUNDLE_IDENTIFIER@"
 #define MAC_BUNDLE_IDENTIFIER_STRING "@MAC_BUNDLE_IDENTIFIER@"
 #define PRIVILEGED_HELPER_NAME "@PRIVILEGED_HELPER_NAME@"
diff --git a/chromecast/browser/cast_web_service.cc b/chromecast/browser/cast_web_service.cc
index 7e331789..552b391 100644
--- a/chromecast/browser/cast_web_service.cc
+++ b/chromecast/browser/cast_web_service.cc
@@ -35,8 +35,7 @@
     content::StoragePartition::REMOVE_DATA_MASK_COOKIES |
     content::StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS |
     content::StoragePartition::REMOVE_DATA_MASK_INDEXEDDB |
-    content::StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE |
-    content::StoragePartition::REMOVE_DATA_MASK_WEBSQL;
+    content::StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE;
 
 }  // namespace
 
diff --git a/chromecast/starboard/chromecast/events/starboard_event_source.cc b/chromecast/starboard/chromecast/events/starboard_event_source.cc
index 596d3e1..ee06a85 100644
--- a/chromecast/starboard/chromecast/events/starboard_event_source.cc
+++ b/chromecast/starboard/chromecast/events/starboard_event_source.cc
@@ -158,11 +158,13 @@
 StarboardEventSource::StarboardEventSource(ui::PlatformWindowDelegate* delegate)
     : task_runner_(GetCurrentSequencedTaskRunner()), delegate_(delegate) {
   DCHECK(delegate_);
+  LOG(INFO) << "Subscribing to CastStarboardApiAdapter. this=" << this;
   CastStarboardApiAdapter::GetInstance()->Subscribe(
       this, &StarboardEventSource::SbEventHandle);
 }
 
 StarboardEventSource::~StarboardEventSource() {
+  LOG(INFO) << "Unsubscribing from CastStarboardApiAdapter. this=" << this;
   CastStarboardApiAdapter::GetInstance()->Unsubscribe(this);
 }
 
diff --git a/chromecast/starboard/graphics/BUILD.gn b/chromecast/starboard/graphics/BUILD.gn
index d659472..98ea100 100644
--- a/chromecast/starboard/graphics/BUILD.gn
+++ b/chromecast/starboard/graphics/BUILD.gn
@@ -37,6 +37,7 @@
   ]
   deps = [
     ":GL_starboard",
+    "//base",
     "//chromecast/public",
     "//chromecast/starboard:starboard_buildflags",
     "//chromecast/starboard/chromecast/starboard_adapter",
diff --git a/chromecast/starboard/graphics/cast_egl_platform_starboard.cc b/chromecast/starboard/graphics/cast_egl_platform_starboard.cc
index 5097397ac7..a5a0b503 100644
--- a/chromecast/starboard/graphics/cast_egl_platform_starboard.cc
+++ b/chromecast/starboard/graphics/cast_egl_platform_starboard.cc
@@ -5,9 +5,8 @@
 #include <dlfcn.h>
 #include <unistd.h>
 
-#include <cassert>
-#include <iostream>
-
+#include "base/check.h"
+#include "base/logging.h"
 #include "chromecast/public/cast_egl_platform.h"
 #include "chromecast/public/cast_egl_platform_shlib.h"
 #include "chromecast/public/graphics_types.h"
@@ -44,7 +43,7 @@
     graphics_lib_ =
         dlopen(kGraphicsLibraryName, RTLD_NOW | RTLD_LOCAL | RTLD_DEEPBIND);
     if (!graphics_lib_) {
-      std::cerr << "Failed to dlopen " << kGraphicsLibraryName << std::endl;
+      LOG(ERROR) << "Failed to dlopen " << kGraphicsLibraryName;
       return false;
     }
 
@@ -52,8 +51,8 @@
         dlsym(graphics_lib_, "Sb_eglGetProcAddress"));
 
     if (!get_proc_address_) {
-      std::cerr << "Failed to dlsym Sb_eglGetProcAddress from "
-                << kGraphicsLibraryName << std::endl;
+      LOG(ERROR) << "Failed to dlsym Sb_eglGetProcAddress from "
+                 << kGraphicsLibraryName;
       return false;
     }
     return true;
@@ -78,6 +77,7 @@
     // and CreateWindow, so there is no downside to creating |window_| early.
     if (!SbWindowIsValid(window_)) {
       auto* sb_adapter = CastStarboardApiAdapter::GetInstance();
+      LOG(INFO) << "Subscribing to CastStarboardApiAdapter. this=" << this;
       sb_adapter->Subscribe(this, nullptr);
       SbWindowOptions options{};
       options.name = "cast";
@@ -86,6 +86,7 @@
       window_ = sb_adapter->GetWindow(&options);
       ndt_ = reinterpret_cast<NativeDisplayType>(
           sb_adapter->GetEglNativeDisplayType());
+      LOG(INFO) << "Unsubscribing from CastStarboardApiAdapter. this=" << this;
       sb_adapter->Unsubscribe(this);
     }
 
@@ -98,7 +99,7 @@
 #if BUILDFLAG(REMOVE_STARBOARD_HEADERS)
     return nullptr;
 #else
-    assert(SbWindowIsValid(window_));
+    CHECK(SbWindowIsValid(window_));
     auto* result =
         static_cast<NativeWindowType>(SbWindowGetPlatformHandle(window_));
 
diff --git a/chromecast/starboard/media/cdm/starboard_decryptor_cast.cc b/chromecast/starboard/media/cdm/starboard_decryptor_cast.cc
index 576dde2c..31d2493 100644
--- a/chromecast/starboard/media/cdm/starboard_decryptor_cast.cc
+++ b/chromecast/starboard/media/cdm/starboard_decryptor_cast.cc
@@ -4,14 +4,12 @@
 
 #include "chromecast/starboard/media/cdm/starboard_decryptor_cast.h"
 
-#include <cast_starboard_api_adapter.h>
-
 #include <cstdint>
 #include <cstring>
 #include <optional>
+#include <utility>
 
 #include "base/check_op.h"
-#include "base/compiler_specific.h"
 #include "base/containers/span.h"
 #include "base/functional/bind.h"
 #include "base/hash/hash.h"
@@ -20,6 +18,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
 #include "chromecast/media/base/decrypt_context_impl.h"
+#include "chromecast/starboard/chromecast/starboard_adapter/public/cast_starboard_api_adapter.h"
 #include "chromecast/starboard/media/cdm/starboard_drm_key_tracker.h"
 #include "chromecast/starboard/media/media/starboard_api_wrapper.h"
 #include "google_apis/google_api_keys.h"
@@ -489,7 +488,8 @@
 
   LOG(INFO) << "Provisioning succeeded. Updating session in starboard.";
   std::vector<uint8_t> response_vec(response.size());
-  UNSAFE_TODO(memcpy(response_vec.data(), response.c_str(), response.size()));
+  base::span<uint8_t>(response_vec)
+      .copy_from_nonoverlapping(base::as_byte_span(response));
 
   // This will be called if we successfully update the session.
   auto success_cb =
diff --git a/chromecast/starboard/media/cdm/starboard_drm_wrapper.cc b/chromecast/starboard/media/cdm/starboard_drm_wrapper.cc
index 3a46b55..14e15b7 100644
--- a/chromecast/starboard/media/cdm/starboard_drm_wrapper.cc
+++ b/chromecast/starboard/media/cdm/starboard_drm_wrapper.cc
@@ -401,6 +401,7 @@
   // Use of base::Unretained is safe because this object should never be
   // destructed in production code (it's a private destructor, and the only code
   // that destroys it is the test-only function SetSingletonForTesting).
+  LOG(INFO) << "Subscribing to CastStarboardApiAdapter. this=" << this;
   chromecast::CastStarboardApiAdapter::GetInstance()->Subscribe(this, nullptr);
   base::AtExitManager::RegisterTask(base::BindOnce(
       &StarboardDrmWrapper::DestroySbDrmSystem, base::Unretained(this)));
@@ -440,6 +441,7 @@
 
   LOG(INFO) << "Destroying SbDrmSystem because core_runtime is shutting down.";
   starboard_->DrmDestroySystem(drm_system_);
+  LOG(INFO) << "Unsubscribing from CastStarboardApiAdapter. this=" << this;
   chromecast::CastStarboardApiAdapter::GetInstance()->Unsubscribe(this);
 
   // We need to destroy owned_starboard_ here, so that it unsubscribes from
diff --git a/chromecast/starboard/media/cdm/starboard_drm_wrapper_test.cc b/chromecast/starboard/media/cdm/starboard_drm_wrapper_test.cc
index 4bd6df2..04879f6 100644
--- a/chromecast/starboard/media/cdm/starboard_drm_wrapper_test.cc
+++ b/chromecast/starboard/media/cdm/starboard_drm_wrapper_test.cc
@@ -365,7 +365,6 @@
       error_message, session_id,
       std::vector<uint8_t>(content.begin(), content.end()), url);
   StarboardDrmKeyId key_id = {};
-  // Double-check that a memcpy is safe.
   base::span<uint8_t>(key_id.identifier)
       .take_first<key.size()>()
       .copy_from(base::as_byte_span(key));
diff --git a/chromecast/starboard/media/media/BUILD.gn b/chromecast/starboard/media/media/BUILD.gn
index fa9574e..1f9fab3 100644
--- a/chromecast/starboard/media/media/BUILD.gn
+++ b/chromecast/starboard/media/media/BUILD.gn
@@ -77,7 +77,7 @@
       "starboard_api_wrapper_base.cc",
       "starboard_api_wrapper_base.h",
     ]
-    if (sb_api_version == 15) {
+    if (sb_api_version == 15 || sb_api_version == 16) {
       sources += [ "starboard_api_wrapper_15.cc" ]
     } else {
       assert(sb_api_version == 13 || sb_api_version == 14,
diff --git a/chromecast/starboard/media/media/cast_media_starboard.cc b/chromecast/starboard/media/media/cast_media_starboard.cc
index 5a526a96..251d0c6 100644
--- a/chromecast/starboard/media/media/cast_media_starboard.cc
+++ b/chromecast/starboard/media/media/cast_media_starboard.cc
@@ -106,7 +106,7 @@
                          kStarboardMediaSupportTypeProbably;
 
   LOG(INFO) << "Video codec=" << codec << ", profile=" << profile
-            << ", level=" << level << " is "
+            << ", level=" << level << " (MIME type " << mime << ") is "
             << (supported ? "supported" : "not supported") << " by starboard.";
   return supported;
 }
@@ -131,8 +131,9 @@
                              mime.c_str(), /*key_system=*/"") ==
                          kStarboardMediaSupportTypeProbably;
 
-  LOG(INFO) << "Audio codec=" << config.codec << " is "
-            << (supported ? "supported" : "not supported") << " by starboard.";
+  LOG(INFO) << "Audio codec=" << config.codec << " (MIME type " << mime
+            << ") is " << (supported ? "supported" : "not supported")
+            << " by starboard.";
   return supported;
 }
 
diff --git a/chromecast/starboard/media/media/starboard_api_wrapper_base.cc b/chromecast/starboard/media/media/starboard_api_wrapper_base.cc
index b80d146..100ec21 100644
--- a/chromecast/starboard/media/media/starboard_api_wrapper_base.cc
+++ b/chromecast/starboard/media/media/starboard_api_wrapper_base.cc
@@ -232,6 +232,7 @@
 StarboardApiWrapperBase::~StarboardApiWrapperBase() {
   if (initialized_) {
     initialized_ = false;
+    LOG(INFO) << "Unsubscribing from CastStarboardApiAdapter. this=" << this;
     chromecast::CastStarboardApiAdapter::GetInstance()->Unsubscribe(this);
   }
 }
@@ -242,6 +243,7 @@
   // check MIME type compatibility in another process.
   if (!initialized_) {
     initialized_ = true;
+    LOG(INFO) << "Subscribing to CastStarboardApiAdapter. this=" << this;
     chromecast::CastStarboardApiAdapter::GetInstance()->Subscribe(this,
                                                                   nullptr);
   }
diff --git a/clank b/clank
index d7c11e7..2828bd1 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit d7c11e788469c544db99807d9d272a651b39437f
+Subproject commit 2828bd1ff4886faee721d4943c8d68b9e2315640
diff --git a/components/autofill/core/browser/crowdsourcing/determine_possible_field_types.cc b/components/autofill/core/browser/crowdsourcing/determine_possible_field_types.cc
index 4a3c5885..5446d1c6 100644
--- a/components/autofill/core/browser/crowdsourcing/determine_possible_field_types.cc
+++ b/components/autofill/core/browser/crowdsourcing/determine_possible_field_types.cc
@@ -178,7 +178,7 @@
   FieldTypeSet types;
   for (const EntityInstance& entity : entities) {
     for (const AttributeInstance& attribute : entity.attributes()) {
-      for (FieldType field_type : attribute.GetSupportedTypes()) {
+      for (FieldType field_type : attribute.type().field_subtypes()) {
         bool is_empty = comparator.HasOnlySkippableCharacters(attribute.GetInfo(
             field_type, comparator.app_locale(), std::nullopt));
         if (!is_empty) {
@@ -213,7 +213,7 @@
   FieldTypeSet types;
   for (const EntityInstance& entity : entities) {
     for (const AttributeInstance& attribute : entity.attributes()) {
-      for (FieldType field_type : attribute.GetSupportedTypes()) {
+      for (FieldType field_type : attribute.type().field_subtypes()) {
         bool matches = comparator.Compare(
             value,
             attribute.GetInfo(field_type, comparator.app_locale(),
@@ -245,7 +245,7 @@
 
   for (const EntityInstance& entity : entities) {
     for (const AttributeInstance& attribute : entity.attributes()) {
-      for (const FieldType field_type : attribute.GetSupportedTypes()) {
+      for (const FieldType field_type : attribute.type().field_subtypes()) {
         if (!IsDateFieldType(field_type)) {
           continue;
         }
diff --git a/components/autofill/core/browser/data_model/autofill_ai/entity_instance.cc b/components/autofill/core/browser/data_model/autofill_ai/entity_instance.cc
index abd4f700..04a1432 100644
--- a/components/autofill/core/browser/data_model/autofill_ai/entity_instance.cc
+++ b/components/autofill/core/browser/data_model/autofill_ai/entity_instance.cc
@@ -52,112 +52,128 @@
 AttributeInstance::~AttributeInstance() = default;
 
 std::u16string AttributeInstance::GetInfo(
-    FieldType type,
+    FieldType field_type,
     const std::string& app_locale,
     base::optional_ref<const std::u16string> format_string) const {
-  type = GetNormalizedType(type);
-  if (type == UNKNOWN_TYPE) {
+  field_type = GetNormalizedFieldType(field_type);
+  if (field_type == UNKNOWN_TYPE) {
     return u"";
   }
-  CHECK(GetSupportedTypes().contains(type));
+  CHECK(type_.field_subtypes().contains(field_type));
   return std::visit(
-      absl::Overload{
-          [&](const CountryInfo& country) {
-            return country.GetCountryName(app_locale);
-          },
-          [&](const DateInfo& date) {
-            // TODO(crbug.com/396325496): Consider falling back
-            // to a locale-specific format by relying on
-            // `app_locale`.
-            return date.GetDate(format_string ? *format_string : u"YYYY-MM-DD");
-          },
-          [&](const NameInfo&) { return GetRawInfo(/*pass_key=*/{}, type); },
-          [&](const StateInfo&) { return GetRawInfo(/*pass_key=*/{}, type); },
-          [&](const std::u16string&) {
-            return GetRawInfo(/*pass_key=*/{}, type);
-          }},
+      absl::Overload{[&](const CountryInfo& country) {
+                       return country.GetCountryName(app_locale);
+                     },
+                     [&](const DateInfo& date) {
+                       // TODO(crbug.com/396325496): Consider falling back
+                       // to a locale-specific format by relying on
+                       // `app_locale`.
+                       return date.GetDate(format_string ? *format_string
+                                                         : u"YYYY-MM-DD");
+                     },
+                     [&](const NameInfo&) {
+                       return GetRawInfo(/*pass_key=*/{}, field_type);
+                     },
+                     [&](const StateInfo&) {
+                       return GetRawInfo(/*pass_key=*/{}, field_type);
+                     },
+                     [&](const std::u16string&) {
+                       return GetRawInfo(/*pass_key=*/{}, field_type);
+                     }},
       info_);
 }
 
 std::u16string AttributeInstance::GetRawInfo(GetRawInfoPassKey,
-                                             FieldType type) const {
-  type = GetNormalizedType(type);
-  if (type == UNKNOWN_TYPE) {
+                                             FieldType field_type) const {
+  field_type = GetNormalizedFieldType(field_type);
+  if (field_type == UNKNOWN_TYPE) {
     return u"";
   }
-  CHECK(GetSupportedTypes().contains(type));
+  CHECK(type_.field_subtypes().contains(field_type));
   return std::visit(
       absl::Overload{
           [&](const CountryInfo& country) {
             return base::UTF8ToUTF16(country.GetCountryCode());
           },
           [&](const DateInfo& date) { return date.GetDate(u"YYYY-MM-DD"); },
-          [&](const NameInfo& name) { return name.GetRawInfo(type); },
+          [&](const NameInfo& name) {
+            if (!name.GetSupportedTypes().contains(field_type)) {
+              return std::u16string();
+            }
+            return name.GetRawInfo(field_type);
+          },
           [&](const StateInfo& state) { return state.value(); },
           [&](const std::u16string& value) { return value; }},
       info_);
 }
 
 VerificationStatus AttributeInstance::GetVerificationStatus(
-    FieldType type) const {
-  type = GetNormalizedType(type);
-  if (type == UNKNOWN_TYPE) {
+    FieldType field_type) const {
+  field_type = GetNormalizedFieldType(field_type);
+  if (field_type == UNKNOWN_TYPE) {
     return VerificationStatus::kNoStatus;
   }
-  CHECK(GetSupportedTypes().contains(type));
+  CHECK(type_.field_subtypes().contains(field_type));
   return std::visit(
       absl::Overload{
           [&](const CountryInfo&) { return VerificationStatus::kNoStatus; },
           [&](const DateInfo&) { return VerificationStatus::kNoStatus; },
           [&](const NameInfo& name) {
-            return name.GetVerificationStatus(type);
+            if (!name.GetSupportedTypes().contains(field_type)) {
+              return VerificationStatus::kNoStatus;
+            }
+            return name.GetVerificationStatus(field_type);
           },
           [&](const StateInfo&) { return VerificationStatus::kNoStatus; },
           [&](const std::u16string&) { return VerificationStatus::kNoStatus; }},
       info_);
 }
 
-void AttributeInstance::SetInfo(FieldType type,
+void AttributeInstance::SetInfo(FieldType field_type,
                                 const std::u16string& value,
                                 const std::string& app_locale,
                                 std::u16string_view format_string,
                                 VerificationStatus status) {
-  type = GetNormalizedType(type);
-  if (type == UNKNOWN_TYPE) {
+  field_type = GetNormalizedFieldType(field_type);
+  if (field_type == UNKNOWN_TYPE) {
     return;
   }
-  CHECK(GetSupportedTypes().contains(type));
-  std::visit(absl::Overload{
-                 [&](CountryInfo& country) {
-                   // We assume that the given `value` is either a valid
-                   // country code or a valid country name localized to the
-                   // provided `app_locale`.
-                   if (!country.SetCountryFromCountryCode(value) &&
-                       !country.SetCountryFromCountryName(value, app_locale)) {
-                     // In case `value` turns out to be neither of the two
-                     // options mentioned above, we reset the country value to
-                     // indicate failure.
-                     country = CountryInfo();
-                   }
-                 },
-                 [&](DateInfo& date) { date.SetDate(value, format_string); },
-                 [&](NameInfo& name) {
-                   name.SetInfoWithVerificationStatus(type, value, app_locale,
-                                                      status);
-                 },
-                 [&](const StateInfo&) { SetRawInfo(type, value, status); },
-                 [&](std::u16string&) { SetRawInfo(type, value, status); }},
-             info_);
+  CHECK(type_.field_subtypes().contains(field_type));
+  std::visit(
+      absl::Overload{
+          [&](CountryInfo& country) {
+            // We assume that the given `value` is either a valid
+            // country code or a valid country name localized to the
+            // provided `app_locale`.
+            if (!country.SetCountryFromCountryCode(value) &&
+                !country.SetCountryFromCountryName(value, app_locale)) {
+              // In case `value` turns out to be neither of the two
+              // options mentioned above, we reset the country value to
+              // indicate failure.
+              country = CountryInfo();
+            }
+          },
+          [&](DateInfo& date) { date.SetDate(value, format_string); },
+          [&](NameInfo& name) {
+            if (!name.GetSupportedTypes().contains(field_type)) {
+              return;
+            }
+            name.SetInfoWithVerificationStatus(field_type, value, app_locale,
+                                               status);
+          },
+          [&](const StateInfo&) { SetRawInfo(field_type, value, status); },
+          [&](std::u16string&) { SetRawInfo(field_type, value, status); }},
+      info_);
 }
 
-void AttributeInstance::SetRawInfo(FieldType type,
+void AttributeInstance::SetRawInfo(FieldType field_type,
                                    const std::u16string& value,
                                    VerificationStatus status) {
-  type = GetNormalizedType(type);
-  if (type == UNKNOWN_TYPE) {
+  field_type = GetNormalizedFieldType(field_type);
+  if (field_type == UNKNOWN_TYPE) {
     return;
   }
-  CHECK(GetSupportedTypes().contains(type));
+  CHECK(type_.field_subtypes().contains(field_type));
   std::visit(absl::Overload{
                  [&](CountryInfo& country) {
                    if (!country.SetCountryFromCountryCode(value)) {
@@ -169,45 +185,23 @@
                  },
                  [&](DateInfo& date) { date.SetDate(value, u"YYYY-MM-DD"); },
                  [&](NameInfo& name) {
-                   name.SetRawInfoWithVerificationStatus(type, value, status);
+                   if (!name.GetSupportedTypes().contains(field_type)) {
+                     return;
+                   }
+                   name.SetRawInfoWithVerificationStatus(field_type, value,
+                                                         status);
                  },
                  [&](StateInfo& state) { state = StateInfo(value); },
                  [&](std::u16string& old_value) { old_value = value; }},
              info_);
 }
 
-FieldTypeSet AttributeInstance::GetSupportedTypes() const {
-  return std::visit(
-      absl::Overload{
-          [&](const CountryInfo&) { return FieldTypeSet{type_.field_type()}; },
-          [&](const DateInfo&) { return FieldTypeSet{type_.field_type()}; },
-          [&](const NameInfo& name) { return name.GetSupportedTypes(); },
-          [&](const StateInfo&) { return FieldTypeSet{type_.field_type()}; },
-          [&](const std::u16string&) {
-            return FieldTypeSet{type_.field_type()};
-          }},
-      info_);
-}
-
-FieldTypeSet AttributeInstance::GetDatabaseStoredTypes(
-    base::PassKey<EntityTable> pass_key) const {
-  return std::visit(
-      absl::Overload{
-          [&](const CountryInfo&) { return FieldTypeSet{type_.field_type()}; },
-          [&](const DateInfo&) { return FieldTypeSet{type_.field_type()}; },
-          [&](const NameInfo&) { return NameInfo::kDatabaseStoredTypes; },
-          [&](const StateInfo&) { return FieldTypeSet{type_.field_type()}; },
-          [&](const std::u16string&) {
-            return FieldTypeSet{type_.field_type()};
-          }},
-      info_);
-}
-
-FieldType AttributeInstance::GetNormalizedType(FieldType info_type) const {
-  if (GetSupportedTypes().contains(info_type)) {
-    return info_type;
+FieldType AttributeInstance::GetNormalizedFieldType(
+    FieldType field_type) const {
+  if (type_.field_subtypes().contains(field_type)) {
+    return field_type;
   }
-  if (info_type == type_.field_type()) {
+  if (field_type == type_.field_type()) {
     // In some cases, a field might have `AutofillField::Type()` being the one
     // corresponding to a structured attribute (e.g., PASSPORT_NAME_TAG). This
     // should not usually happen but for now can, only in case a field couldn't
@@ -215,11 +209,11 @@
     // that case, we assume the type is the top-level type of the attribute.
     return std::visit(
         absl::Overload{
-            [&](const CountryInfo&) { return type().field_type(); },
-            [&](const DateInfo&) { return type().field_type(); },
+            [&](const CountryInfo&) { return type_.field_type(); },
+            [&](const DateInfo&) { return type_.field_type(); },
             [&](const NameInfo&) { return NAME_FULL; },
-            [&](const StateInfo&) { return type().field_type(); },
-            [&](const std::u16string&) { return type().field_type(); }},
+            [&](const StateInfo&) { return type_.field_type(); },
+            [&](const std::u16string&) { return type_.field_type(); }},
         info_);
   }
   // In case the field classification is totally unrelated to the
@@ -228,7 +222,7 @@
   // attribute with the given type. If the type is not structured we just return
   // the corresponding field type of the attribute, just like we would do
   // regardless of the type passed.
-  return IsTagType(type().field_type()) ? UNKNOWN_TYPE : type().field_type();
+  return IsTagType(type_.field_type()) ? UNKNOWN_TYPE : type_.field_type();
 }
 
 void AttributeInstance::FinalizeInfo() {
diff --git a/components/autofill/core/browser/data_model/autofill_ai/entity_instance.h b/components/autofill/core/browser/data_model/autofill_ai/entity_instance.h
index 6f91cb7..d8195ceb 100644
--- a/components/autofill/core/browser/data_model/autofill_ai/entity_instance.h
+++ b/components/autofill/core/browser/data_model/autofill_ai/entity_instance.h
@@ -106,7 +106,7 @@
   //
   // For more control over over which, see GetInfo().
   std::u16string GetCompleteInfo(const std::string& app_locale) const {
-    return GetInfo(type().field_type(), app_locale, std::nullopt);
+    return GetInfo(type_.field_type(), app_locale, std::nullopt);
   }
 
   // Returns the value stored in this attribute instance for a specific `type`,
@@ -116,7 +116,7 @@
   // defaults to u"YYYY-MM-DD". See AutofillField::format_string() for the
   // grammar of format strings.
   std::u16string GetInfo(
-      FieldType type,
+      FieldType field_type,
       const std::string& app_locale,
       base::optional_ref<const std::u16string> format_string) const;
 
@@ -129,11 +129,12 @@
 
   // Same as `GetInfo` but returns the value as stored with no formatting
   // whatsoever.
-  std::u16string GetRawInfo(GetRawInfoPassKey pass_key, FieldType type) const;
+  std::u16string GetRawInfo(GetRawInfoPassKey pass_key,
+                            FieldType field_type) const;
 
   // Returns the verification status of a value stored in this attribute
   // instance for a specific `type`.
-  VerificationStatus GetVerificationStatus(FieldType type) const;
+  VerificationStatus GetVerificationStatus(FieldType field_type) const;
 
   // Populates the attribute with a value for a specific `type`, according to a
   // given `app_locale`.
@@ -144,7 +145,7 @@
   // the `format_string`, the function is a no-op, e.g.,
   // SetInfo(..., u"16/12/2022", ..., u"DD", ...) is a no-op.
   // See AutofillField::format_string() for the grammar of format strings.
-  void SetInfo(FieldType type,
+  void SetInfo(FieldType field_type,
                const std::u16string& value,
                const std::string& app_locale,
                std::u16string_view format_string,
@@ -155,19 +156,10 @@
   // function might perform additional steps (e.g., name formatting). This
   // function should only be used by database logic and settings page logic.
   // TODO(crbug.com/389625753): Investigate merging SetInfo* and SetRawInfo*.
-  void SetRawInfo(FieldType type,
+  void SetRawInfo(FieldType field_type,
                   const std::u16string& value,
                   VerificationStatus status);
 
-  // Returns the set of `FieldType`s for which the setter/getter functions above
-  // may be called.
-  FieldTypeSet GetSupportedTypes() const;
-
-  // Returns the types which are stored in the database for this attribute
-  // to be able to correctly reconstruct it at database loading time.
-  FieldTypeSet GetDatabaseStoredTypes(
-      base::PassKey<EntityTable> pass_key) const;
-
   // This is a no-op for unstructured attributes, and for structured attributes
   // the function propagates changes in a component to its subcomponents. This
   // should be called when constructing a structured attribute object from
@@ -182,7 +174,7 @@
   // This function checks that `info_type` is supported by the attribute and
   // otherwise tries to convert it into one that is. Returns the supported type
   // if found and UNKNOWN_TYPE otherwise.
-  FieldType GetNormalizedType(FieldType info_type) const;
+  FieldType GetNormalizedFieldType(FieldType field_type) const;
 
   AttributeType type_;
   InfoStructure info_;
@@ -259,7 +251,7 @@
   // Returns the instance of `a` if it is present.
   base::optional_ref<const AttributeInstance> attribute(AttributeType a) const
       LIFETIME_BOUND {
-    CHECK_EQ(a.entity_type(), type());
+    CHECK_EQ(a.entity_type(), type_);
     auto it = attributes_.find(a);
     return it != attributes_.end() ? &*it : nullptr;
   }
diff --git a/components/autofill/core/browser/data_model/autofill_ai/entity_type.cc b/components/autofill/core/browser/data_model/autofill_ai/entity_type.cc
index 6575f084..c4f3f6f 100644
--- a/components/autofill/core/browser/data_model/autofill_ai/entity_type.cc
+++ b/components/autofill/core/browser/data_model/autofill_ai/entity_type.cc
@@ -7,6 +7,7 @@
 #include <optional>
 
 #include "base/types/cxx23_to_underlying.h"
+#include "components/autofill/core/browser/data_model/addresses/contact_info.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/strings/grit/components_strings.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -36,6 +37,14 @@
   return 0 <= type && type < kTable.size() ? kTable[type] : std::nullopt;
 }
 
+FieldTypeSet AttributeType::storable_field_types(
+    base::PassKey<EntityTable> pass_key) const {
+  if (data_type() == DataType::kName) {
+    return NameInfo::kDatabaseStoredTypes;
+  }
+  return {field_type()};
+}
+
 std::u16string AttributeType::GetNameForI18n() const {
   return l10n_util::GetStringUTF16([&] {
     switch (name()) {
diff --git a/components/autofill/core/browser/data_model/autofill_ai/entity_type.h b/components/autofill/core/browser/data_model/autofill_ai/entity_type.h
index 35da794..f402a06 100644
--- a/components/autofill/core/browser/data_model/autofill_ai/entity_type.h
+++ b/components/autofill/core/browser/data_model/autofill_ai/entity_type.h
@@ -11,6 +11,7 @@
 
 #include "base/containers/span.h"
 #include "base/notreached.h"
+#include "base/types/pass_key.h"
 #include "components/autofill/core/browser/data_model/autofill_ai/entity_type_names.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/common/dense_set.h"
@@ -29,6 +30,7 @@
 // entity_schema.json.
 class EntityType;
 class AttributeType;
+class EntityTable;
 
 // An attribute type is the blueprint for an attribute instance, which in turn
 // represents a string value with additional metadata.
@@ -75,8 +77,17 @@
 
   constexpr DataType data_type() const;
 
-  // Maps this AttributeType to the corresponding Autofill AI `FieldType`.
+  // There are three kinds of AttributeType / FieldType associations:
+  // - `field_type()` is the one that best describes the full attribute.
+  //   Except for name types, `FromFieldType(field_type()) == field_type()`.
+  // - `field_subtypes()` additionally include more fine-granular ones.
+  //   Except for name types, `field_subtypes() == {field_type}`.
+  //   For name types, `field_subtypes()` includes `NAME_FIRST` etc.
+  // - `storable_field_types()` are the ones that may be physically stored in
+  //   the database.
   constexpr FieldType field_type() const;
+  constexpr FieldTypeSet field_subtypes() const;
+  FieldTypeSet storable_field_types(base::PassKey<EntityTable> pass_key) const;
 
   // Returns whether the attribute should be obfuscated in preview and
   // suggestion labels.
@@ -177,6 +188,13 @@
   NOTREACHED();
 }
 
+constexpr FieldTypeSet AttributeType::field_subtypes() const {
+  if (data_type() == DataType::kName) {
+    return FieldTypesOfGroup(FieldTypeGroup::kName);
+  }
+  return {field_type()};
+}
+
 template <>
 struct DenseSetTraits<AttributeType> {
   using T = AttributeType;
diff --git a/components/autofill/core/browser/data_model/autofill_ai/entity_type_unittest.cc b/components/autofill/core/browser/data_model/autofill_ai/entity_type_unittest.cc
index 6cc17cd..b69ce04 100644
--- a/components/autofill/core/browser/data_model/autofill_ai/entity_type_unittest.cc
+++ b/components/autofill/core/browser/data_model/autofill_ai/entity_type_unittest.cc
@@ -18,6 +18,13 @@
   AttributeType a = AttributeType(AttributeTypeName::kPassportName);
   EXPECT_EQ(a.entity_type(), EntityType(EntityTypeName::kPassport));
   EXPECT_EQ(a.field_type(), PASSPORT_NAME_TAG);
+  EXPECT_THAT(a.field_subtypes(),
+              UnorderedElementsAre(
+                  NAME_HONORIFIC_PREFIX, NAME_FIRST, NAME_MIDDLE, NAME_LAST,
+                  NAME_LAST_PREFIX, NAME_LAST_CORE, NAME_LAST_FIRST,
+                  NAME_LAST_SECOND, NAME_LAST_CONJUNCTION, NAME_MIDDLE_INITIAL,
+                  NAME_FULL, NAME_SUFFIX, ALTERNATIVE_FAMILY_NAME,
+                  ALTERNATIVE_GIVEN_NAME, ALTERNATIVE_FULL_NAME));
   EXPECT_EQ(a, AttributeType::FromFieldType(PASSPORT_NAME_TAG));
 }
 
diff --git a/components/autofill/core/browser/metrics/payments/card_metadata_metrics.cc b/components/autofill/core/browser/metrics/payments/card_metadata_metrics.cc
index 05fefb2..102aa87a 100644
--- a/components/autofill/core/browser/metrics/payments/card_metadata_metrics.cc
+++ b/components/autofill/core/browser/metrics/payments/card_metadata_metrics.cc
@@ -254,38 +254,38 @@
     const CardMetadataLoggingContext& context) {
   switch (event) {
     case CardMetadataLoggingEvent::kShown: {
-      LogBenefitFormEventForAllBenefitSourcesWithBenefitAvailable(
+      LogBenefitFormEventForAllBenefitSourcesWithBenefitAvailableDeprecated(
           context.instrument_ids_to_available_benefit_sources,
           FORM_EVENT_SUGGESTION_FOR_CARD_WITH_BENEFIT_AVAILABLE_SHOWN_ONCE);
       break;
     }
     case CardMetadataLoggingEvent::kSelected:
       if (context.SelectedCardHasBenefitAvailable()) {
-        LogBenefitFormEventToBenefitSourceHistogram(
+        LogBenefitFormEventToBenefitSourceHistogramDeprecated(
             context.selected_benefit_source,
             FORM_EVENT_SUGGESTION_FOR_SERVER_CARD_WITH_BENEFIT_AVAILABLE_SELECTED_ONCE);
       }
-      LogBenefitFormEventForAllBenefitSourcesWithBenefitAvailable(
+      LogBenefitFormEventForAllBenefitSourcesWithBenefitAvailableDeprecated(
           context.instrument_ids_to_available_benefit_sources,
           FORM_EVENT_SUGGESTION_FOR_SERVER_CARD_SELECTED_AFTER_CARD_WITH_BENEFIT_AVAILABLE_SHOWN_ONCE);
       break;
     case CardMetadataLoggingEvent::kFilled:
       if (context.SelectedCardHasBenefitAvailable()) {
-        LogBenefitFormEventToBenefitSourceHistogram(
+        LogBenefitFormEventToBenefitSourceHistogramDeprecated(
             context.selected_benefit_source,
             FORM_EVENT_SUGGESTION_FOR_SERVER_CARD_WITH_BENEFIT_AVAILABLE_FILLED_ONCE);
       }
-      LogBenefitFormEventForAllBenefitSourcesWithBenefitAvailable(
+      LogBenefitFormEventForAllBenefitSourcesWithBenefitAvailableDeprecated(
           context.instrument_ids_to_available_benefit_sources,
           FORM_EVENT_SUGGESTION_FOR_SERVER_CARD_FILLED_AFTER_CARD_WITH_BENEFIT_AVAILABLE_SHOWN_ONCE);
       break;
     case CardMetadataLoggingEvent::kSubmitted:
       if (context.SelectedCardHasBenefitAvailable()) {
-        LogBenefitFormEventToBenefitSourceHistogram(
+        LogBenefitFormEventToBenefitSourceHistogramDeprecated(
             context.selected_benefit_source,
             FORM_EVENT_SUGGESTION_FOR_SERVER_CARD_WITH_BENEFIT_AVAILABLE_SUBMITTED_ONCE);
       }
-      LogBenefitFormEventForAllBenefitSourcesWithBenefitAvailable(
+      LogBenefitFormEventForAllBenefitSourcesWithBenefitAvailableDeprecated(
           context.instrument_ids_to_available_benefit_sources,
           FORM_EVENT_SUGGESTION_FOR_SERVER_CARD_SUBMITTED_AFTER_CARD_WITH_BENEFIT_AVAILABLE_SHOWN_ONCE);
       break;
@@ -326,7 +326,7 @@
       "Autofill.PaymentMethods.CardBenefitsIsEnabled.Startup", enabled);
 }
 
-void LogBenefitFormEventToBenefitSourceHistogram(
+void LogBenefitFormEventToBenefitSourceHistogramDeprecated(
     const std::string& benefit_source,
     FormEvent event) {
   base::UmaHistogramEnumeration(
@@ -336,7 +336,7 @@
       event, NUM_FORM_EVENTS);
 }
 
-void LogBenefitFormEventForAllBenefitSourcesWithBenefitAvailable(
+void LogBenefitFormEventForAllBenefitSourcesWithBenefitAvailableDeprecated(
     const base::flat_map<int64_t, std::string>&
         instrument_ids_to_available_benefit_sources,
     FormEvent event) {
@@ -347,7 +347,8 @@
   for (const auto& [instrument_id, benefit_source] :
        instrument_ids_to_available_benefit_sources) {
     if (!benefit_sources_shown.contains(benefit_source)) {
-      LogBenefitFormEventToBenefitSourceHistogram(benefit_source, event);
+      LogBenefitFormEventToBenefitSourceHistogramDeprecated(benefit_source,
+                                                            event);
       benefit_sources_shown.insert(benefit_source);
     }
   }
diff --git a/components/autofill/core/browser/metrics/payments/card_metadata_metrics.h b/components/autofill/core/browser/metrics/payments/card_metadata_metrics.h
index 699ec64d..cb2bbdf 100644
--- a/components/autofill/core/browser/metrics/payments/card_metadata_metrics.h
+++ b/components/autofill/core/browser/metrics/payments/card_metadata_metrics.h
@@ -156,13 +156,17 @@
 void LogIsCreditCardBenefitsEnabledAtStartup(bool enabled);
 
 // Log the given `event` for card benefits on a benefit source level.
-void LogBenefitFormEventToBenefitSourceHistogram(
+// TODO(crbug.com/417228483): Remove this function after adding benefit form
+// event enums to a new histogram with a new enum class.
+void LogBenefitFormEventToBenefitSourceHistogramDeprecated(
     const std::string& benefit_source,
     FormEvent event);
 
 // Log the given `event` for every card benefit source with benefits available
 // shown.
-void LogBenefitFormEventForAllBenefitSourcesWithBenefitAvailable(
+// TODO(crbug.com/417228483): Remove this function after adding benefit form
+// event enums to a new histogram with a new enum class.
+void LogBenefitFormEventForAllBenefitSourcesWithBenefitAvailableDeprecated(
     const base::flat_map<int64_t, std::string>&
         instrument_ids_to_available_benefit_sources,
     FormEvent event);
diff --git a/components/autofill/core/browser/webdata/autofill_ai/entity_table.cc b/components/autofill/core/browser/webdata/autofill_ai/entity_table.cc
index 67e37ed..c6edc81 100644
--- a/components/autofill/core/browser/webdata/autofill_ai/entity_table.cc
+++ b/components/autofill/core/browser/webdata/autofill_ai/entity_table.cc
@@ -235,7 +235,8 @@
 
 bool EntityTable::AddAttribute(const EntityInstance& entity,
                                const AttributeInstance& attribute) {
-  for (FieldType type : attribute.GetDatabaseStoredTypes(/*pass_key=*/{})) {
+  for (FieldType type :
+       attribute.type().storable_field_types(/*pass_key=*/{})) {
     sql::Statement s;
     InsertBuilder(db(), s, attributes::kTableName,
                   {attributes::kEntityGuid, attributes::kAttributeType,
diff --git a/components/autofill/core/browser/webdata/payments/payments_autofill_table.cc b/components/autofill/core/browser/webdata/payments/payments_autofill_table.cc
index eb03fc0..2b4e3e1 100644
--- a/components/autofill/core/browser/webdata/payments/payments_autofill_table.cc
+++ b/components/autofill/core/browser/webdata/payments/payments_autofill_table.cc
@@ -261,7 +261,7 @@
                                  const os_crypt_async::Encryptor& encryptor) {
   std::string encrypted_data;
   std::ignore = encryptor.EncryptString(value, &encrypted_data);
-  s->BindBlob(column_index, encrypted_data);
+  s->BindBlob(column_index, std::move(encrypted_data));
 }
 
 void BindEncryptedU16StringToColumn(
@@ -271,7 +271,7 @@
     const os_crypt_async::Encryptor& encryptor) {
   std::string encrypted_data;
   std::ignore = encryptor.EncryptString16(value, &encrypted_data);
-  s->BindBlob(column_index, encrypted_data);
+  s->BindBlob(column_index, std::move(encrypted_data));
 }
 
 void BindCreditCardToStatement(const CreditCard& credit_card,
diff --git a/components/commerce/core/BUILD.gn b/components/commerce/core/BUILD.gn
index 7650c84..537f086 100644
--- a/components/commerce/core/BUILD.gn
+++ b/components/commerce/core/BUILD.gn
@@ -288,6 +288,8 @@
   sources = [
     "mock_cluster_manager.cc",
     "mock_cluster_manager.h",
+    "mock_discount_infos_storage.cc",
+    "mock_discount_infos_storage.h",
     "mock_shopping_service.cc",
     "mock_shopping_service.h",
     "shopping_service_test_base.cc",
@@ -304,6 +306,7 @@
     ":pref_names",
     ":shopping_service",
     ":utils",
+    "//base",
     "//base/test:test_support",
     "//components/bookmarks/browser",
     "//components/bookmarks/test",
@@ -337,6 +340,7 @@
   sources = [
     "bookmark_update_manager_unittest.cc",
     "commerce_info_cache_unittest.cc",
+    "discount_infos_storage_unittest.cc",
     "feature_utils_unittest.cc",
     "metrics/scheduled_metrics_manager_unittest.cc",
     "pdp_metrics_unittest.cc",
@@ -351,6 +355,7 @@
     ":account_checker_test_support",
     ":commerce_constants",
     ":commerce_info_cache",
+    ":discount_infos_db_content_proto",
     ":discounts_db_content_proto",
     ":feature_utils",
     ":metrics",
diff --git a/components/commerce/core/discount_infos_storage.cc b/components/commerce/core/discount_infos_storage.cc
index 229b07d..fa9ae4f 100644
--- a/components/commerce/core/discount_infos_storage.cc
+++ b/components/commerce/core/discount_infos_storage.cc
@@ -28,4 +28,124 @@
 }
 DiscountInfosStorage::~DiscountInfosStorage() = default;
 
+void DiscountInfosStorage::LoadDiscountsWithPrefix(const GURL& url,
+                                         DiscountInfoCallback callback) {
+  proto_db_->LoadContentWithPrefix(
+      url.spec(),
+      base::BindOnce(&DiscountInfosStorage::OnLoadDiscounts,
+                     weak_ptr_factory_.GetWeakPtr(), url, std::move(callback)));
+}
+
+void DiscountInfosStorage::SaveDiscounts(
+    const GURL& url,
+    const std::vector<DiscountInfo>& infos) {
+  DiscountInfosContent proto;
+  proto.set_key(url.spec());
+  for (const DiscountInfo& info : infos) {
+    if (info.expiry_time_sec.has_value() && info.discount_code.has_value()) {
+      discount_infos_db::DiscountInfoContent* discount_proto =
+          proto.add_discounts();
+      discount_proto->set_id(info.id);
+      discount_infos_db::DiscountInfoContent_Type type =
+          discount_infos_db::DiscountInfoContent_Type_TYPE_UNSPECIFIED;
+      if (info.type == DiscountType::kFreeListingWithCode) {
+        type =
+            discount_infos_db::DiscountInfoContent_Type_FREE_LISTING_WITH_CODE;
+      } else if (info.type == DiscountType::kCrawledPromotion) {
+        type = discount_infos_db::DiscountInfoContent_Type_CRAWLED_PROMOTION;
+      }
+      discount_proto->set_type(type);
+      discount_proto->set_language_code(info.language_code);
+      discount_proto->set_description_detail(info.description_detail);
+      if (info.terms_and_conditions.has_value()) {
+        discount_proto->set_terms_and_conditions(
+            info.terms_and_conditions.value());
+      }
+      discount_proto->set_expiry_time_sec(info.expiry_time_sec.value());
+      discount_proto->set_discount_code(info.discount_code.value());
+      discount_proto->set_offer_id(info.offer_id);
+    }
+  }
+  if (proto.discounts().size() > 0) {
+    proto_db_->InsertContent(url.spec(), proto,
+                             base::BindOnce([](bool succeeded) {}));
+  } else {
+    DeleteDiscountsForUrl(url.spec());
+  }
+}
+
+void DiscountInfosStorage::DeleteDiscountsForUrl(const std::string& url) {
+  proto_db_->DeleteOneEntry(url, base::BindOnce([](bool succeeded) {}));
+}
+
+void DiscountInfosStorage::OnLoadDiscounts(const GURL& url,
+                                           DiscountInfoCallback callback,
+                                           bool succeeded,
+                                           DiscountInfosKeyAndValues data) {
+  if (!succeeded || data.size() == 0) {
+    std::move(callback).Run(url, {});
+    return;
+  }
+  std::vector<DiscountInfo> unexpired_infos;
+  for (const auto& [key, value] : data) {
+    std::vector<DiscountInfo> valid_infos =
+        GetUnexpiredDiscountsFromProto(value);
+
+    if (valid_infos.size() == 0) {
+      // Delete the entry in the db if no unexpired discounts found.
+      DeleteDiscountsForUrl(key);
+    } else {
+      // Update local database if expired discounts found.
+      if ((int)(valid_infos.size()) != value.discounts().size()) {
+        SaveDiscounts(GURL(key), valid_infos);
+      }
+      unexpired_infos.insert(unexpired_infos.end(), valid_infos.begin(),
+                             valid_infos.end());
+    }
+  }
+  std::move(callback).Run(url, std::move(unexpired_infos));
+}
+
+std::vector<DiscountInfo> DiscountInfosStorage::GetUnexpiredDiscountsFromProto(
+    const DiscountInfosContent& proto) {
+  std::vector<DiscountInfo> infos;
+  for (const discount_infos_db::DiscountInfoContent& content :
+       proto.discounts()) {
+    // First check whether the discount is expired.
+    if ((base::Time::Now() - base::Time::UnixEpoch()).InSeconds() >
+        content.expiry_time_sec()) {
+      continue;
+    }
+
+    DiscountInfo info;
+    info.id = content.id();
+    info.type = DiscountType(content.type());
+    info.language_code = content.language_code();
+    info.description_detail = content.description_detail();
+    if (content.has_terms_and_conditions()) {
+      info.terms_and_conditions = content.terms_and_conditions();
+    }
+    info.expiry_time_sec = content.expiry_time_sec();
+    if (content.has_discount_code()) {
+      info.discount_code = content.discount_code();
+    }
+    info.offer_id = content.offer_id();
+    infos.push_back(info);
+  }
+
+  return infos;
+}
+
+void DiscountInfosStorage::OnHistoryDeletions(
+    history::HistoryService* history_service,
+    const history::DeletionInfo& deletion_info) {
+  if (deletion_info.IsAllHistory()) {
+    proto_db_->DeleteAllContent(base::BindOnce([](bool succeeded) {}));
+    return;
+  }
+
+  for (const history::URLRow& row : deletion_info.deleted_rows()) {
+    DeleteDiscountsForUrl(row.url().spec());
+  }
+}
 }  // namespace commerce
diff --git a/components/commerce/core/discount_infos_storage.h b/components/commerce/core/discount_infos_storage.h
index f82aa75..539ea79 100644
--- a/components/commerce/core/discount_infos_storage.h
+++ b/components/commerce/core/discount_infos_storage.h
@@ -34,7 +34,31 @@
   DiscountInfosStorage& operator=(const DiscountInfosStorage&) = delete;
   ~DiscountInfosStorage() override;
 
+  // Load all discounts with prefix matching the given url.
+  virtual void LoadDiscountsWithPrefix(const GURL& url,
+                                       DiscountInfoCallback callback);
+
+  // Save discounts for the given url.
+  virtual void SaveDiscounts(const GURL& url,
+                             const std::vector<DiscountInfo>& infos);
+
+  // history::HistoryServiceObserver:
+  void OnHistoryDeletions(history::HistoryService* history_service,
+                          const history::DeletionInfo& deletion_info) override;
+
  private:
+  void DeleteDiscountsForUrl(const std::string& url);
+
+  void OnLoadDiscounts(const GURL& url,
+                       DiscountInfoCallback callback,
+                       bool succeeded,
+                       DiscountInfosKeyAndValues data);
+
+  // When loading from local db, discard expired discounts and only convert &
+  // return unexpired ones.
+  std::vector<DiscountInfo> GetUnexpiredDiscountsFromProto(
+      const DiscountInfosContent& proto);
+
   raw_ptr<SessionProtoStorage<DiscountInfosContent>> proto_db_;
 
   base::ScopedObservation<history::HistoryService,
diff --git a/components/commerce/core/discount_infos_storage_unittest.cc b/components/commerce/core/discount_infos_storage_unittest.cc
new file mode 100644
index 0000000..3acfb0a
--- /dev/null
+++ b/components/commerce/core/discount_infos_storage_unittest.cc
@@ -0,0 +1,498 @@
+// Copyright 2025 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/commerce/core/discount_infos_storage.h"
+
+#include "base/check.h"
+#include "base/functional/callback.h"
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
+#include "base/time/time.h"
+#include "components/commerce/core/commerce_types.h"
+#include "components/commerce/core/proto/discount_infos_db_content.pb.h"
+#include "components/session_proto_db/session_proto_storage.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::InSequence;
+
+namespace {
+
+const char kDeleteUrl1[] = "http://example.com/delete1";
+const char kDeleteUrl2[] = "http://example.com/delete2";
+
+const char kProductUrl1[] = "http://example.com/product1";
+const char kProductUrl2[] = "http://example.com/product2";
+const char kMerchantUrl[] = "http://example.com/";
+
+const char kDiscountLanguageCode[] = "en-US";
+const char kDiscountDetail[] = "details";
+const char kDiscountTerms[] = "terms";
+const char kDiscountCode[] = "discount code";
+const uint64_t kDiscountIdInDb1 = 333;
+const uint64_t kDiscountIdInDb2 = 444;
+const uint64_t kDiscountIdInDb3 = 555;
+const uint64_t kDiscountOfferId = 123456;
+const double kDiscountExpiryTime = 1000000000000;
+
+std::vector<SessionProtoStorage<commerce::DiscountInfosContent>::KeyAndValue>
+MockDbLoadResponseMultipleMatches(const std::string& url1,
+                                  bool discount1_expired,
+                                  bool discount2_expired,
+                                  const std::string& url2,
+                                  bool discount3_expired) {
+  double expired_timestamp =
+      (base::Time::Now() - base::Time::UnixEpoch()).InSeconds() - 100;
+  double unexpired_timestamp =
+      (base::Time::Now() - base::Time::UnixEpoch()).InSeconds() + 100;
+  commerce::DiscountInfosContent proto1;
+  proto1.set_key(url1);
+  discount_infos_db::DiscountInfoContent* discount_proto1 =
+      proto1.add_discounts();
+  discount_proto1->set_id(kDiscountIdInDb1);
+  discount_proto1->set_type(
+      discount_infos_db::DiscountInfoContent_Type_FREE_LISTING_WITH_CODE);
+  discount_proto1->set_language_code(kDiscountLanguageCode);
+  discount_proto1->set_description_detail(kDiscountDetail);
+  discount_proto1->set_terms_and_conditions(kDiscountTerms);
+  discount_proto1->set_expiry_time_sec(discount1_expired ? expired_timestamp
+                                                         : unexpired_timestamp);
+  discount_proto1->set_discount_code(kDiscountCode);
+  discount_proto1->set_offer_id(kDiscountOfferId);
+
+  discount_infos_db::DiscountInfoContent* discount_proto2 =
+      proto1.add_discounts();
+  discount_proto2->set_id(kDiscountIdInDb2);
+  discount_proto2->set_type(
+      discount_infos_db::DiscountInfoContent_Type_FREE_LISTING_WITH_CODE);
+  discount_proto2->set_language_code(kDiscountLanguageCode);
+  discount_proto2->set_description_detail(kDiscountDetail);
+  discount_proto2->set_expiry_time_sec(discount2_expired ? expired_timestamp
+                                                         : unexpired_timestamp);
+  discount_proto2->set_discount_code(kDiscountCode);
+  discount_proto2->set_offer_id(kDiscountOfferId);
+
+  commerce::DiscountInfosContent proto2;
+  proto2.set_key(url2);
+  discount_infos_db::DiscountInfoContent* discount_proto3 =
+      proto2.add_discounts();
+  discount_proto3->set_id(kDiscountIdInDb3);
+  discount_proto3->set_type(
+      discount_infos_db::DiscountInfoContent_Type_FREE_LISTING_WITH_CODE);
+  discount_proto3->set_language_code(kDiscountLanguageCode);
+  discount_proto3->set_description_detail(kDiscountDetail);
+  discount_proto3->set_expiry_time_sec(discount3_expired ? expired_timestamp
+                                                         : unexpired_timestamp);
+  discount_proto3->set_discount_code(kDiscountCode);
+  discount_proto3->set_offer_id(kDiscountOfferId);
+
+  return std::vector<
+      SessionProtoStorage<commerce::DiscountInfosContent>::KeyAndValue>{
+      {url1, proto1}, {url2, proto2}};
+}
+
+std::vector<SessionProtoStorage<commerce::DiscountInfosContent>::KeyAndValue>
+MockDbLoadResponseSingleMatch(const std::string& url,
+                              bool discount1_expired,
+                              bool discount2_expired) {
+  double expired_timestamp =
+      (base::Time::Now() - base::Time::UnixEpoch()).InSeconds() - 100;
+  double unexpired_timestamp =
+      (base::Time::Now() - base::Time::UnixEpoch()).InSeconds() + 100;
+  commerce::DiscountInfosContent proto;
+  proto.set_key(url);
+  discount_infos_db::DiscountInfoContent* discount_proto1 =
+      proto.add_discounts();
+  discount_proto1->set_id(kDiscountIdInDb1);
+  discount_proto1->set_type(
+      discount_infos_db::DiscountInfoContent_Type_FREE_LISTING_WITH_CODE);
+  discount_proto1->set_language_code(kDiscountLanguageCode);
+  discount_proto1->set_description_detail(kDiscountDetail);
+  discount_proto1->set_terms_and_conditions(kDiscountTerms);
+  discount_proto1->set_expiry_time_sec(discount1_expired ? expired_timestamp
+                                                         : unexpired_timestamp);
+  discount_proto1->set_discount_code(kDiscountCode);
+  discount_proto1->set_offer_id(kDiscountOfferId);
+
+  discount_infos_db::DiscountInfoContent* discount_proto2 =
+      proto.add_discounts();
+  discount_proto2->set_id(kDiscountIdInDb2);
+  discount_proto2->set_type(
+      discount_infos_db::DiscountInfoContent_Type_FREE_LISTING_WITH_CODE);
+  discount_proto2->set_language_code(kDiscountLanguageCode);
+  discount_proto2->set_description_detail(kDiscountDetail);
+  discount_proto2->set_expiry_time_sec(discount2_expired ? expired_timestamp
+                                                         : unexpired_timestamp);
+  discount_proto2->set_discount_code(kDiscountCode);
+  discount_proto2->set_offer_id(kDiscountOfferId);
+
+  return std::vector<
+      SessionProtoStorage<commerce::DiscountInfosContent>::KeyAndValue>{
+      {url, proto}};
+}
+
+class MockProtoStorage
+    : public SessionProtoStorage<commerce::DiscountInfosContent> {
+ public:
+  MockProtoStorage() = default;
+  ~MockProtoStorage() override = default;
+
+  MOCK_METHOD(void,
+              LoadContentWithPrefix,
+              (const std::string& key_prefix,
+               SessionProtoStorage<commerce::DiscountInfosContent>::LoadCallback
+                   callback),
+              (override));
+  MOCK_METHOD(
+      void,
+      InsertContent,
+      (const std::string& key,
+       const commerce::DiscountInfosContent& value,
+       SessionProtoStorage<commerce::DiscountInfosContent>::OperationCallback
+           callback),
+      (override));
+  MOCK_METHOD(
+      void,
+      DeleteOneEntry,
+      (const std::string& key,
+       SessionProtoStorage<commerce::DiscountInfosContent>::OperationCallback
+           callback),
+      (override));
+  MOCK_METHOD(void,
+              DeleteAllContent,
+              (SessionProtoStorage<
+                  commerce::DiscountInfosContent>::OperationCallback callback),
+              (override));
+  MOCK_METHOD(void,
+              LoadAllEntries,
+              (SessionProtoStorage<commerce::DiscountInfosContent>::LoadCallback
+                   callback),
+              (override));
+  MOCK_METHOD(void,
+              LoadOneEntry,
+              (const std::string& key,
+               SessionProtoStorage<commerce::DiscountInfosContent>::LoadCallback
+                   callback),
+              (override));
+  MOCK_METHOD(
+      void,
+      UpdateEntries,
+      ((std::unique_ptr<
+           std::vector<std::pair<std::string, commerce::DiscountInfosContent>>>
+            entries_to_update),
+       std::unique_ptr<std::vector<std::string>> keys_to_remove,
+       SessionProtoStorage<commerce::DiscountInfosContent>::OperationCallback
+           callback),
+      (override));
+  MOCK_METHOD(
+      void,
+      PerformMaintenance,
+      (const std::vector<std::string>& keys_to_keep,
+       const std::string& key_substring_to_match,
+       SessionProtoStorage<commerce::DiscountInfosContent>::OperationCallback
+           callback),
+      (override));
+  MOCK_METHOD(
+      void,
+      DeleteContentWithPrefix,
+      (const std::string& key_prefix,
+       SessionProtoStorage<commerce::DiscountInfosContent>::OperationCallback
+           callback),
+      (override));
+  MOCK_METHOD(void, Destroy, (), (const, override));
+
+  void MockLoadAllResponseSingleMatch(const std::string& url,
+                                      bool succeeded,
+                                      bool discount1_expired,
+                                      bool discount2_expired) {
+    ON_CALL(*this, LoadContentWithPrefix)
+        .WillByDefault(
+            [url, succeeded, discount1_expired, discount2_expired](
+                std::string key_prefix,
+                SessionProtoStorage<
+                    commerce::DiscountInfosContent>::LoadCallback callback) {
+              std::move(callback).Run(
+                  succeeded, MockDbLoadResponseSingleMatch(
+                                 url, discount1_expired, discount2_expired));
+            });
+  }
+  void MockLoadAllResponseMultipleMatches(bool succeeded,
+                                          const std::string& url1,
+                                          bool discount1_expired,
+                                          bool discount2_expired,
+                                          const std::string& url2,
+                                          bool discount3_expired) {
+    ON_CALL(*this, LoadContentWithPrefix)
+        .WillByDefault(
+            [succeeded, url1, discount1_expired, discount2_expired, url2,
+             discount3_expired](
+                std::string key_prefix,
+                SessionProtoStorage<
+                    commerce::DiscountInfosContent>::LoadCallback callback) {
+              std::move(callback).Run(
+                  succeeded, MockDbLoadResponseMultipleMatches(
+                                 url1, discount1_expired, discount2_expired,
+                                 url2, discount3_expired));
+            });
+  }
+};
+
+}  // namespace
+
+namespace commerce {
+
+class DiscountInfosStorageTest : public testing::Test {
+ public:
+  DiscountInfosStorageTest() = default;
+  ~DiscountInfosStorageTest() override = default;
+
+  void SetUp() override {
+    proto_db_ = std::make_unique<MockProtoStorage>();
+    storage_ = std::make_unique<DiscountInfosStorage>(proto_db_.get(), nullptr);
+  }
+
+ protected:
+  base::test::TaskEnvironment task_environment_;
+  std::unique_ptr<MockProtoStorage> proto_db_;
+  std::unique_ptr<DiscountInfosStorage> storage_;
+};
+
+TEST_F(DiscountInfosStorageTest,
+       TestLoadDiscountsWithPrefix_AllDiscountsUnexpired) {
+  proto_db_->MockLoadAllResponseSingleMatch(kProductUrl1, /*succeeded=*/true,
+                                            /*discount1_expired=*/false,
+                                            /*discount2_expired=*/false);
+
+  EXPECT_CALL(*proto_db_, LoadContentWithPrefix);
+
+  base::RunLoop run_loop;
+  storage_->LoadDiscountsWithPrefix(
+      GURL(kProductUrl1),
+      base::BindOnce(
+          [](base::RunLoop* run_loop, const GURL& url,
+             const std::vector<DiscountInfo> results) {
+            ASSERT_EQ(2, (int)results.size());
+            ASSERT_EQ(kDiscountIdInDb1, results[0].id);
+            ASSERT_EQ(DiscountType::kFreeListingWithCode, results[0].type);
+            ASSERT_EQ(kDiscountLanguageCode, results[0].language_code);
+            ASSERT_EQ(kDiscountDetail, results[0].description_detail);
+            ASSERT_EQ(kDiscountTerms, results[0].terms_and_conditions);
+            ASSERT_EQ(kDiscountCode, results[0].discount_code);
+            ASSERT_EQ(kDiscountOfferId, results[0].offer_id);
+
+            ASSERT_EQ(kDiscountIdInDb2, results[1].id);
+            ASSERT_EQ(std::nullopt, results[1].terms_and_conditions);
+
+            run_loop->Quit();
+          },
+          &run_loop));
+  run_loop.Run();
+}
+
+TEST_F(DiscountInfosStorageTest,
+       TestLoadDiscountsWithPrefix_MultipleMatches_AllDiscountsUnexpired) {
+  proto_db_->MockLoadAllResponseMultipleMatches(
+      /*succeeded=*/true, kProductUrl1, /*discount1_expired=*/false,
+      /*discount2_expired=*/false, kProductUrl2, /*discount3_expired=*/false);
+
+  EXPECT_CALL(*proto_db_, LoadContentWithPrefix);
+  EXPECT_CALL(*proto_db_, InsertContent).Times(0);
+  EXPECT_CALL(*proto_db_, DeleteOneEntry).Times(0);
+
+  base::RunLoop run_loop;
+  storage_->LoadDiscountsWithPrefix(
+      GURL(kMerchantUrl),
+      base::BindOnce(
+          [](base::RunLoop* run_loop, const GURL& url,
+             const std::vector<DiscountInfo> results) {
+            ASSERT_EQ(3, (int)results.size());
+
+            ASSERT_EQ(kDiscountIdInDb1, results[0].id);
+            ASSERT_EQ(DiscountType::kFreeListingWithCode, results[0].type);
+            ASSERT_EQ(kDiscountLanguageCode, results[0].language_code);
+            ASSERT_EQ(kDiscountDetail, results[0].description_detail);
+            ASSERT_EQ(kDiscountTerms, results[0].terms_and_conditions);
+            ASSERT_EQ(kDiscountCode, results[0].discount_code);
+            ASSERT_EQ(kDiscountOfferId, results[0].offer_id);
+
+            ASSERT_EQ(kDiscountIdInDb2, results[1].id);
+            ASSERT_EQ(std::nullopt, results[1].terms_and_conditions);
+
+            ASSERT_EQ(kDiscountIdInDb3, results[2].id);
+            ASSERT_EQ(std::nullopt, results[2].terms_and_conditions);
+
+            run_loop->Quit();
+          },
+          &run_loop));
+  run_loop.Run();
+}
+
+TEST_F(DiscountInfosStorageTest,
+       TestLoadDiscountsWithPrefix_MultipleMatches_AllDiscountsExpired) {
+  proto_db_->MockLoadAllResponseMultipleMatches(
+      /*succeeded=*/true, kProductUrl1, /*discount1_expired=*/true,
+      /*discount2_expired=*/true, kProductUrl2, /*discount3_expired=*/true);
+  {
+    InSequence s;
+    EXPECT_CALL(*proto_db_, LoadContentWithPrefix);
+    EXPECT_CALL(*proto_db_, DeleteOneEntry(kProductUrl1, _));
+    EXPECT_CALL(*proto_db_, DeleteOneEntry(kProductUrl2, _));
+  }
+  base::RunLoop run_loop;
+  storage_->LoadDiscountsWithPrefix(
+      GURL(kMerchantUrl), base::BindOnce(
+                              [](base::RunLoop* run_loop, const GURL& url,
+                                 const std::vector<DiscountInfo> results) {
+                                ASSERT_EQ(kMerchantUrl, url.spec());
+                                ASSERT_EQ(0, (int)results.size());
+
+                                run_loop->Quit();
+                              },
+                              &run_loop));
+  run_loop.Run();
+}
+
+TEST_F(DiscountInfosStorageTest,
+       TestLoadDiscountsWithPrefix_PartDiscountsExpired) {
+  proto_db_->MockLoadAllResponseSingleMatch(kProductUrl1, /*succeeded=*/true,
+                                            /*discount1_expired=*/false,
+                                            /*discount2_expired=*/true);
+
+  {
+    InSequence s;
+    EXPECT_CALL(*proto_db_, LoadContentWithPrefix);
+    EXPECT_CALL(*proto_db_, InsertContent(kProductUrl1, _, _));
+  }
+
+  base::RunLoop run_loop;
+  storage_->LoadDiscountsWithPrefix(
+      GURL(kProductUrl1),
+      base::BindOnce(
+          [](base::RunLoop* run_loop, const GURL& url,
+             const std::vector<DiscountInfo> results) {
+            ASSERT_EQ(1, (int)results.size());
+            ASSERT_EQ(kDiscountIdInDb1, results[0].id);
+            ASSERT_EQ(DiscountType::kFreeListingWithCode, results[0].type);
+            ASSERT_EQ(kDiscountLanguageCode, results[0].language_code);
+            ASSERT_EQ(kDiscountDetail, results[0].description_detail);
+            ASSERT_EQ(kDiscountTerms, results[0].terms_and_conditions);
+            ASSERT_EQ(kDiscountCode, results[0].discount_code);
+            ASSERT_EQ(kDiscountOfferId, results[0].offer_id);
+
+            run_loop->Quit();
+          },
+          &run_loop));
+  run_loop.Run();
+}
+
+TEST_F(DiscountInfosStorageTest,
+       TestLoadDiscountsWithPrefix_MultipleMatches_PartDiscountsExpired) {
+  proto_db_->MockLoadAllResponseMultipleMatches(
+      /*succeeded=*/true, kProductUrl1, /*discount1_expired=*/true,
+      /*discount2_expired=*/false, kProductUrl2, /*discount3_expired=*/true);
+
+  {
+    InSequence s;
+    EXPECT_CALL(*proto_db_, LoadContentWithPrefix);
+    EXPECT_CALL(*proto_db_, InsertContent(kProductUrl1, _, _));
+    EXPECT_CALL(*proto_db_, DeleteOneEntry(kProductUrl2, _));
+  }
+
+  base::RunLoop run_loop;
+  storage_->LoadDiscountsWithPrefix(
+      GURL(kMerchantUrl),
+      base::BindOnce(
+          [](base::RunLoop* run_loop, const GURL& url,
+             const std::vector<DiscountInfo> results) {
+            ASSERT_EQ(1, (int)results.size());
+            ASSERT_EQ(kDiscountIdInDb2, results[0].id);
+            ASSERT_EQ(DiscountType::kFreeListingWithCode, results[0].type);
+            ASSERT_EQ(kDiscountLanguageCode, results[0].language_code);
+            ASSERT_EQ(kDiscountDetail, results[0].description_detail);
+            ASSERT_EQ(std::nullopt, results[0].terms_and_conditions);
+            ASSERT_EQ(kDiscountCode, results[0].discount_code);
+            ASSERT_EQ(kDiscountOfferId, results[0].offer_id);
+
+            run_loop->Quit();
+          },
+          &run_loop));
+  run_loop.Run();
+}
+
+TEST_F(DiscountInfosStorageTest, TestLoadDiscountsWithPrefix_AllDiscountsExpired) {
+  proto_db_->MockLoadAllResponseSingleMatch(kProductUrl1, /*succeeded=*/true,
+                                            /*discount1_expired=*/true,
+                                            /*discount2_expired=*/true);
+  EXPECT_CALL(*proto_db_, DeleteOneEntry).Times(1);
+
+  base::RunLoop run_loop;
+  storage_->LoadDiscountsWithPrefix(
+      GURL(kProductUrl1), base::BindOnce(
+                              [](base::RunLoop* run_loop, const GURL& url,
+                                 const std::vector<DiscountInfo> results) {
+                                ASSERT_EQ(0, (int)results.size());
+
+                                run_loop->Quit();
+                              },
+                              &run_loop));
+  run_loop.Run();
+}
+
+TEST_F(DiscountInfosStorageTest, TestOnURLsDeleted_DeleteAll) {
+  EXPECT_CALL(*proto_db_, DeleteAllContent).Times(1);
+  EXPECT_CALL(*proto_db_, DeleteOneEntry).Times(0);
+
+  storage_->OnHistoryDeletions(nullptr, history::DeletionInfo::ForAllHistory());
+}
+
+TEST_F(DiscountInfosStorageTest, TestOnURLsDeleted_DeleteUrls) {
+  EXPECT_CALL(*proto_db_, DeleteAllContent).Times(0);
+  EXPECT_CALL(*proto_db_, DeleteOneEntry(kDeleteUrl1, _)).Times(1);
+  EXPECT_CALL(*proto_db_, DeleteOneEntry(kDeleteUrl2, _)).Times(1);
+
+  storage_->OnHistoryDeletions(
+      nullptr,
+      history::DeletionInfo::ForUrls({history::URLRow(GURL(kDeleteUrl1)),
+                                      history::URLRow(GURL(kDeleteUrl2))},
+                                     {}));
+}
+
+TEST_F(DiscountInfosStorageTest, TestSaveDiscounts) {
+  EXPECT_CALL(*proto_db_, InsertContent).Times(1);
+
+  DiscountInfo discount_info;
+  discount_info.id = kDiscountIdInDb1;
+  discount_info.type = DiscountType::kFreeListingWithCode;
+  discount_info.language_code = kDiscountLanguageCode;
+  discount_info.description_detail = kDiscountDetail;
+  discount_info.terms_and_conditions = kDiscountTerms;
+  discount_info.discount_code = kDiscountCode;
+  discount_info.offer_id = kDiscountOfferId;
+  discount_info.expiry_time_sec = kDiscountExpiryTime;
+  storage_->SaveDiscounts(GURL(kProductUrl1), {discount_info});
+}
+
+TEST_F(DiscountInfosStorageTest, TestSaveDiscounts_EmptyDiscountInfo) {
+  EXPECT_CALL(*proto_db_, InsertContent).Times(0);
+  EXPECT_CALL(*proto_db_, DeleteOneEntry(kProductUrl1, _)).Times(1);
+
+  storage_->SaveDiscounts(GURL(kProductUrl1), {});
+}
+
+TEST_F(DiscountInfosStorageTest, TestSaveDiscounts_NoExpiryTime) {
+  EXPECT_CALL(*proto_db_, InsertContent).Times(0);
+  EXPECT_CALL(*proto_db_, DeleteOneEntry(kProductUrl1, _)).Times(1);
+
+  DiscountInfo discount_info;
+  discount_info.id = kDiscountIdInDb1;
+  discount_info.type = DiscountType::kFreeListingWithCode;
+  discount_info.language_code = kDiscountLanguageCode;
+  discount_info.description_detail = kDiscountDetail;
+  discount_info.terms_and_conditions = kDiscountTerms;
+  discount_info.discount_code = kDiscountCode;
+  discount_info.offer_id = kDiscountOfferId;
+  storage_->SaveDiscounts(GURL(kProductUrl1), {discount_info});
+}
+}  // namespace commerce
diff --git a/components/commerce/core/mock_discount_infos_storage.cc b/components/commerce/core/mock_discount_infos_storage.cc
new file mode 100644
index 0000000..56033ac
--- /dev/null
+++ b/components/commerce/core/mock_discount_infos_storage.cc
@@ -0,0 +1,14 @@
+// Copyright 2025 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/commerce/core/mock_discount_infos_storage.h"
+
+namespace commerce {
+
+MockDiscountInfosStorage::MockDiscountInfosStorage()
+    : DiscountInfosStorage(nullptr, nullptr) {
+}
+MockDiscountInfosStorage::~MockDiscountInfosStorage() = default;
+
+}  // namespace commerce
diff --git a/components/commerce/core/mock_discount_infos_storage.h b/components/commerce/core/mock_discount_infos_storage.h
new file mode 100644
index 0000000..47bbbd0
--- /dev/null
+++ b/components/commerce/core/mock_discount_infos_storage.h
@@ -0,0 +1,32 @@
+// Copyright 2025 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_COMMERCE_CORE_MOCK_DISCOUNT_INFOS_STORAGE_H_
+#define COMPONENTS_COMMERCE_CORE_MOCK_DISCOUNT_INFOS_STORAGE_H_
+
+#include "components/commerce/core/discount_infos_storage.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace commerce {
+
+class MockDiscountInfosStorage : public DiscountInfosStorage {
+ public:
+  MockDiscountInfosStorage();
+  MockDiscountInfosStorage(const MockDiscountInfosStorage&) = delete;
+  ~MockDiscountInfosStorage() override;
+
+  MOCK_METHOD(void,
+              LoadDiscountsWithPrefix,
+              (const GURL& url, DiscountInfoCallback callback),
+              (override));
+
+  MOCK_METHOD(void,
+              SaveDiscounts,
+              (const GURL& url, const std::vector<DiscountInfo>& infos),
+              (override));
+};
+
+}  // namespace commerce
+
+#endif  // COMPONENTS_COMMERCE_CORE_MOCK_DISCOUNT_INFOS_STORAGE_H_
diff --git a/components/commerce/core/proto/discount_infos_db_content.proto b/components/commerce/core/proto/discount_infos_db_content.proto
index e111644..3945b47 100644
--- a/components/commerce/core/proto/discount_infos_db_content.proto
+++ b/components/commerce/core/proto/discount_infos_db_content.proto
@@ -22,6 +22,7 @@
   enum Type {
     TYPE_UNSPECIFIED = 0;
     FREE_LISTING_WITH_CODE = 1;
+    CRAWLED_PROMOTION = 2;
   }
   // Type of the discount.
   optional Type type = 2;
diff --git a/components/commerce/core/shopping_service.cc b/components/commerce/core/shopping_service.cc
index bc0ee4e..29f3904 100644
--- a/components/commerce/core/shopping_service.cc
+++ b/components/commerce/core/shopping_service.cc
@@ -920,6 +920,17 @@
   GetDiscountInfoFromOptGuide(url, std::move(callback));
 }
 
+void ShoppingService::GetAvailableDiscountInfoForUrl(
+    const GURL& url,
+    DiscountInfoCallback callback) {
+  if (!discount_infos_storage_) {
+    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), url, std::vector<DiscountInfo>()));
+    return;
+  }
+  discount_infos_storage_->LoadDiscountsWithPrefix(url, std::move(callback));
+}
+
 void ShoppingService::GetProductSpecificationsForUrls(
     const std::vector<GURL>& urls,
     ProductSpecificationsCallback callback) {
@@ -1536,6 +1547,10 @@
   std::vector<DiscountInfo> discount_infos =
       OptGuideResultToDiscountInfos(metadata);
 
+  if (discount_infos_storage_) {
+    discount_infos_storage_->SaveDiscounts(url, discount_infos);
+  }
+
   std::move(callback).Run(url, std::move(discount_infos));
 }
 
diff --git a/components/commerce/core/shopping_service.h b/components/commerce/core/shopping_service.h
index a125bc4..0696cd3 100644
--- a/components/commerce/core/shopping_service.h
+++ b/components/commerce/core/shopping_service.h
@@ -291,6 +291,12 @@
   virtual void GetDiscountInfoForUrl(const GURL& url,
                                      DiscountInfoCallback callback);
 
+  // This API fetches available valid discounts information on the provided
+  // |url| and passes the payload back to the caller via |callback|.
+  // Call will run after the fetch is completed.
+  virtual void GetAvailableDiscountInfoForUrl(const GURL& url,
+                                          DiscountInfoCallback callback);
+
   virtual void GetProductSpecificationsForUrls(
       const std::vector<GURL>& urls,
       ProductSpecificationsCallback callback);
diff --git a/components/commerce/core/shopping_service_test_base.cc b/components/commerce/core/shopping_service_test_base.cc
index 610179f..504b41b 100644
--- a/components/commerce/core/shopping_service_test_base.cc
+++ b/components/commerce/core/shopping_service_test_base.cc
@@ -9,6 +9,7 @@
 #include "base/command_line.h"
 #include "base/containers/contains.h"
 #include "base/containers/flat_map.h"
+#include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
 #include "base/notreached.h"
 #include "base/run_loop.h"
@@ -17,6 +18,7 @@
 #include "components/bookmarks/test/test_bookmark_client.h"
 #include "components/commerce/core/commerce_feature_list.h"
 #include "components/commerce/core/mock_account_checker.h"
+#include "components/commerce/core/mock_discount_infos_storage.h"
 #include "components/commerce/core/pref_names.h"
 #include "components/commerce/core/proto/discounts.pb.h"
 #include "components/commerce/core/proto/merchant_trust.pb.h"
@@ -488,14 +490,21 @@
       sync_service_.get(),
       base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
           test_url_loader_factory_.get()),
-      nullptr, nullptr, product_spec_service_.get(), nullptr, nullptr, nullptr,
-      nullptr, nullptr, std::make_unique<testing::NiceMock<MockWebExtractor>>(),
+      nullptr, nullptr, product_spec_service_.get(), nullptr, nullptr,
+      nullptr, nullptr, nullptr,
+      std::make_unique<testing::NiceMock<MockWebExtractor>>(),
       tab_restore_service_.get());
+
+  auto discounts_storage =
+      std::make_unique<testing::NiceMock<MockDiscountInfosStorage>>();
+  discount_infos_storage_ = discounts_storage.get();
+  shopping_service_->discount_infos_storage_ = std::move(discounts_storage);
 }
 
 void ShoppingServiceTestBase::TestBody() {}
 
 void ShoppingServiceTestBase::TearDown() {
+  discount_infos_storage_ = nullptr;
   // Reset the enabled/disabled features after each test.
   test_features_.Reset();
 }
diff --git a/components/commerce/core/shopping_service_test_base.h b/components/commerce/core/shopping_service_test_base.h
index 9ff7a94..ba32109 100644
--- a/components/commerce/core/shopping_service_test_base.h
+++ b/components/commerce/core/shopping_service_test_base.h
@@ -11,11 +11,13 @@
 #include <string>
 #include <vector>
 
+#include "base/memory/raw_ptr.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/values.h"
 #include "components/commerce/core/commerce_info_cache.h"
 #include "components/commerce/core/compare/product_specifications_server_proxy.h"
+#include "components/commerce/core/mock_discount_infos_storage.h"
 #include "components/commerce/core/product_specifications/mock_product_specifications_service.h"
 #include "components/commerce/core/shopping_service.h"
 #include "components/commerce/core/web_extractor.h"
@@ -303,6 +305,8 @@
 
   std::unique_ptr<MockTabRestoreService> tab_restore_service_;
 
+  raw_ptr<MockDiscountInfosStorage> discount_infos_storage_;
+
   std::unique_ptr<ShoppingService> shopping_service_;
 };
 
diff --git a/components/commerce/core/shopping_service_unittest.cc b/components/commerce/core/shopping_service_unittest.cc
index 0712a30..983bae2 100644
--- a/components/commerce/core/shopping_service_unittest.cc
+++ b/components/commerce/core/shopping_service_unittest.cc
@@ -19,6 +19,7 @@
 #include "components/commerce/core/commerce_utils.h"
 #include "components/commerce/core/feature_utils.h"
 #include "components/commerce/core/mock_account_checker.h"
+#include "components/commerce/core/mock_discount_infos_storage.h"
 #include "components/commerce/core/pref_names.h"
 #include "components/commerce/core/proto/shopping_page_types.pb.h"
 #include "components/commerce/core/shopping_service_test_base.h"
@@ -2035,6 +2036,32 @@
   run_loop[2].Run();
 }
 
+TEST_P(ShoppingServiceTest, TestDiscountInfoResponse_ForMerchant) {
+  test_features_.InitWithFeatures({kDiscountAutofill}, {});
+
+  EXPECT_CALL(*discount_infos_storage_, LoadDiscountsWithPrefix).Times(1);
+
+  ON_CALL(*discount_infos_storage_, LoadDiscountsWithPrefix)
+      .WillByDefault([](const GURL& url, DiscountInfoCallback callback) {
+        commerce::DiscountInfo info;
+        info.id = 111;
+        info.value_in_text = "10% off";
+        info.type = commerce::DiscountType::kFreeListingWithCode;
+        info.expiry_time_sec = 1000000;
+        info.is_merchant_wide = false;
+        std::move(callback).Run(url, {info});
+      });
+  base::RunLoop run_loop;
+  shopping_service_->GetAvailableDiscountInfoForUrl(
+      GURL(kDiscountsUrl1),
+      base::BindOnce([](const GURL& url,
+                        const std::vector<DiscountInfo> discounts) {
+        ASSERT_EQ(1, (int)discounts.size());
+        ASSERT_TRUE(discounts[0].expiry_time_sec.has_value());
+      }).Then(run_loop.QuitClosure()));
+  run_loop.Run();
+}
+
 TEST_P(ShoppingServiceTest, TestDiscountInfoResponse) {
   test_features_.InitWithFeatures({kEnableDiscountInfoApi}, {});
 
@@ -2060,6 +2087,8 @@
                           OptimizationGuideDecision::kTrue,
                           opt_guide_->BuildDiscountsResponse(infos));
 
+  EXPECT_CALL(*discount_infos_storage_, SaveDiscounts).Times(1);
+
   base::RunLoop run_loop;
   shopping_service_->GetDiscountInfoForUrl(
       GURL(kDiscountsUrl1),
@@ -2113,6 +2142,8 @@
                           OptimizationGuideDecision::kTrue,
                           opt_guide_->BuildDiscountsResponse(infos));
 
+  EXPECT_CALL(*discount_infos_storage_, SaveDiscounts).Times(1);
+
   base::RunLoop run_loop;
   shopping_service_->GetDiscountInfoForUrl(
       GURL(kDiscountsUrl1),
@@ -2155,6 +2186,8 @@
                           OptimizationGuideDecision::kTrue,
                           opt_guide_->BuildDiscountsResponse(infos));
 
+  EXPECT_CALL(*discount_infos_storage_, SaveDiscounts).Times(1);
+
   base::RunLoop run_loop;
   shopping_service_->GetDiscountInfoForUrl(
       GURL(kDiscountsUrl1), base::BindOnce(
@@ -2193,6 +2226,8 @@
                           OptimizationGuideDecision::kTrue,
                           opt_guide_->BuildDiscountsResponse(infos));
 
+  EXPECT_CALL(*discount_infos_storage_, SaveDiscounts).Times(1);
+
   base::RunLoop run_loop;
   shopping_service_->GetDiscountInfoForUrl(
       GURL(kDiscountsUrl1),
diff --git a/components/content_settings/browser/ui/cookie_controls_controller.cc b/components/content_settings/browser/ui/cookie_controls_controller.cc
index 768a07b..9569c89b 100644
--- a/components/content_settings/browser/ui/cookie_controls_controller.cc
+++ b/components/content_settings/browser/ui/cookie_controls_controller.cc
@@ -155,7 +155,7 @@
   DCHECK(web_contents);
   if (!tab_observer_ || GetWebContents() != web_contents) {
     tab_observer_ = std::make_unique<TabObserver>(this, web_contents);
-    SetUserChangedCookieBlockingForSite(false);
+    SetStateChangedViaBypass(false);
   }
   if (observers_.empty()) {
     return;
@@ -366,14 +366,13 @@
   ApplyMetadataChanges(settings_map_, url, std::move(metadata));
 }
 
-bool CookieControlsController::HasUserChangedCookieBlockingForSite() {
-  return user_changed_cookie_blocking_;
+bool CookieControlsController::StateChangedViaBypass() {
+  return user_changed_ub_state_;
 }
 
-void CookieControlsController::SetUserChangedCookieBlockingForSite(
-    bool changed) {
+void CookieControlsController::SetStateChangedViaBypass(bool changed) {
   // Avoid a toggle back and forth being marked as "changed".
-  user_changed_cookie_blocking_ = changed && !user_changed_cookie_blocking_;
+  user_changed_ub_state_ = changed && !user_changed_ub_state_;
 }
 
 int CookieControlsController::GetAllowedThirdPartyCookiesSitesCount() const {
@@ -474,10 +473,10 @@
 
 void CookieControlsController::UpdatePageReloadStatus(
     int recent_reloads_count) {
-  if (HasUserChangedCookieBlockingForSite() && recent_reloads_count > 0) {
+  if (StateChangedViaBypass() && recent_reloads_count > 0) {
     waiting_for_page_load_finish_ = true;
   }
-  SetUserChangedCookieBlockingForSite(false);
+  SetStateChangedViaBypass(false);
   recent_reloads_count_ = recent_reloads_count;
 
   if (recent_reloads_count_ >= features::kUserBypassUIReloadCount.Get()) {
diff --git a/components/content_settings/browser/ui/cookie_controls_controller.h b/components/content_settings/browser/ui/cookie_controls_controller.h
index 6b4199e..613ba052 100644
--- a/components/content_settings/browser/ui/cookie_controls_controller.h
+++ b/components/content_settings/browser/ui/cookie_controls_controller.h
@@ -80,10 +80,10 @@
   // Record UMA for toggling ACT User Bypass.
   void RecordActMetrics(bool pause_protections);
 
-  // Returns whether the cookie blocking setting for the current site was
-  // changed by the user via user bypass.
-  bool HasUserChangedCookieBlockingForSite();
-  void SetUserChangedCookieBlockingForSite(bool changed);
+  // Returns whether the user has changed their protections state via user
+  // bypass.
+  bool StateChangedViaBypass();
+  void SetStateChangedViaBypass(bool changed);
 
   void AddObserver(CookieControlsObserver* obs);
   void RemoveObserver(CookieControlsObserver* obs);
@@ -245,7 +245,7 @@
       cookie_observation_{this};
 
   bool should_reload_ = false;
-  bool user_changed_cookie_blocking_ = false;
+  bool user_changed_ub_state_ = false;
 
   // The number of page reloads in last 30 seconds.
   int recent_reloads_count_ = 0;
diff --git a/components/cronet/url_request_context_config.cc b/components/cronet/url_request_context_config.cc
index 8da024b..dd0a8cd8 100644
--- a/components/cronet/url_request_context_config.cc
+++ b/components/cronet/url_request_context_config.cc
@@ -10,7 +10,6 @@
 #include <utility>
 
 #include "base/containers/contains.h"
-#include "base/debug/stack_trace.h"
 #include "base/feature_list.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
diff --git a/components/data_sharing/internal/group_data_model.cc b/components/data_sharing/internal/group_data_model.cc
index c32746659..e7a4737 100644
--- a/components/data_sharing/internal/group_data_model.cc
+++ b/components/data_sharing/internal/group_data_model.cc
@@ -7,6 +7,7 @@
 #include <iterator>
 
 #include "base/check.h"
+#include "base/containers/contains.h"
 #include "base/files/file_path.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_forward.h"
@@ -388,40 +389,51 @@
   for (const auto& member : old_group_data.members) {
     old_members_gaia_ids.insert(member.gaia_id);
   }
-  std::set<GaiaId> new_members_gaia_ids;
+  std::map<GaiaId, base::Time> new_members;
   for (const auto& member : new_group_data.members) {
-    new_members_gaia_ids.insert(member.gaia_id);
+    new_members.emplace(member.gaia_id, member.last_updated_time);
+  }
+  std::map<GaiaId, base::Time> past_members;
+  for (const auto& member : new_group_data.former_members) {
+    past_members.emplace(member.gaia_id, member.last_updated_time);
   }
 
-  std::vector<GaiaId> added_members_gaia_ids;
-  std::ranges::set_difference(
-      new_members_gaia_ids.begin(), new_members_gaia_ids.end(),
-      old_members_gaia_ids.begin(), old_members_gaia_ids.end(),
-      std::back_inserter(added_members_gaia_ids));
+  std::vector<std::pair<GaiaId, base::Time>> added_members;
+  for (const auto& new_pair : new_members) {
+    if (!base::Contains(old_members_gaia_ids, new_pair.first)) {
+      added_members.push_back(new_pair);
+    }
+  }
 
-  std::vector<GaiaId> removed_members_gaia_ids;
-  std::ranges::set_difference(
-      old_members_gaia_ids.begin(), old_members_gaia_ids.end(),
-      new_members_gaia_ids.begin(), new_members_gaia_ids.end(),
-      std::back_inserter(removed_members_gaia_ids));
+  std::vector<std::pair<GaiaId, base::Time>> removed_members;
+  for (const auto& old_gaia_id : old_members_gaia_ids) {
+    if (new_members.find(old_gaia_id) == new_members.end()) {
+      auto iter = past_members.find(old_gaia_id);
+      base::Time event_time =
+          iter != past_members.end() ? iter->second : base::Time::Now();
+      removed_members.push_back(std::make_pair(old_gaia_id, event_time));
+    }
+  }
 
-  // TODO(crbug.com/377215683): pass the actual event time (at least derived
-  // from CollaborationGroupSpecifics).
-  const base::Time event_time = base::Time::Now();
   for (auto& observer : observers_) {
-    for (auto& member_gaia_id : added_members_gaia_ids) {
+    for (const auto& added_pair : added_members) {
+      base::Time event_time =
+          added_pair.second.is_null() ? base::Time::Now() : added_pair.second;
       MaybeRecordGroupEvent(new_group_data.group_token.group_id,
                             GroupEvent::EventType::kMemberAdded, event_time,
-                            member_gaia_id);
+                            added_pair.first);
       observer.OnMemberAdded(new_group_data.group_token.group_id,
-                             member_gaia_id, event_time);
+                             added_pair.first, event_time);
     }
-    for (auto& member_gaia_id : removed_members_gaia_ids) {
+    for (const auto& removed_pair : removed_members) {
+      base::Time event_time = removed_pair.second.is_null()
+                                  ? base::Time::Now()
+                                  : removed_pair.second;
       MaybeRecordGroupEvent(new_group_data.group_token.group_id,
                             GroupEvent::EventType::kMemberRemoved, event_time,
-                            member_gaia_id);
+                            removed_pair.first);
       observer.OnMemberRemoved(new_group_data.group_token.group_id,
-                               member_gaia_id, event_time);
+                               removed_pair.first, event_time);
     }
   }
 }
diff --git a/components/data_sharing/internal/group_data_model_unittest.cc b/components/data_sharing/internal/group_data_model_unittest.cc
index 0ed1f18..3a4dabf 100644
--- a/components/data_sharing/internal/group_data_model_unittest.cc
+++ b/components/data_sharing/internal/group_data_model_unittest.cc
@@ -264,7 +264,15 @@
 
   void MimicMemberAddedServerSide(const GroupId& group_id,
                                   const GaiaId& member_gaia_id) {
-    sdk_delegate_.AddMember(group_id, member_gaia_id);
+    auto iter = sdk_delegate_.groups()->find(group_id);
+    ASSERT_TRUE(iter != sdk_delegate_.groups()->end());
+
+    data_sharing_pb::GroupMember member;
+    member.set_gaia_id(member_gaia_id.ToString());
+    member.set_role(data_sharing_pb::MEMBER_ROLE_MEMBER);
+    member.set_last_updated_time_unix_epoch_millis(
+        next_changed_at_millis_since_unix_epoch_++);
+    *iter->second.add_members() = member;
 
     syncer::EntityChangeList entity_changes;
     entity_changes.push_back(EntityChangeUpdateFromSpecifics(
@@ -278,6 +286,15 @@
                                     const GaiaId& member_gaia_id) {
     sdk_delegate_.RemoveMember(group_id, member_gaia_id);
 
+    auto iter = sdk_delegate_.groups()->find(group_id);
+    ASSERT_TRUE(iter != sdk_delegate_.groups()->end());
+    data_sharing_pb::GroupMember member;
+    member.set_gaia_id(member_gaia_id.ToString());
+    member.set_role(data_sharing_pb::MEMBER_ROLE_FORMER_MEMBER);
+    member.set_last_updated_time_unix_epoch_millis(
+        next_changed_at_millis_since_unix_epoch_++);
+    *iter->second.add_former_members() = member;
+
     syncer::EntityChangeList entity_changes;
     entity_changes.push_back(EntityChangeUpdateFromSpecifics(
         MakeSpecifics(group_id, next_changed_at_millis_since_unix_epoch_++)));
@@ -464,14 +481,17 @@
   // Test that OnMemberAdded() is called when a member is added.
   const GaiaId member_gaia_id("gaia_id");
   EXPECT_CALL(model_observer(),
-              OnMemberAdded(group_id, member_gaia_id, NotNullTime()));
+              OnMemberAdded(group_id, member_gaia_id,
+                            base::Time::FromMillisecondsSinceUnixEpoch(1001)));
   MimicMemberAddedServerSide(group_id, member_gaia_id);
   WaitForGroupUpdated(group_id);
   testing::Mock::VerifyAndClearExpectations(&model_observer());
 
   // Test that OnMemberRemoved() is called when a member is removed.
-  EXPECT_CALL(model_observer(),
-              OnMemberRemoved(group_id, member_gaia_id, NotNullTime()));
+  EXPECT_CALL(
+      model_observer(),
+      OnMemberRemoved(group_id, member_gaia_id,
+                      base::Time::FromMillisecondsSinceUnixEpoch(1003)));
   MimicMemberRemovedServerSide(group_id, member_gaia_id);
   WaitForGroupUpdated(group_id);
 }
@@ -486,21 +506,25 @@
   // Test that OnMemberAdded() is called when a member is added.
   const GaiaId member_gaia_id1("gaia_id99");
   EXPECT_CALL(model_observer(),
-              OnMemberAdded(group_id, member_gaia_id1, NotNullTime()));
+              OnMemberAdded(group_id, member_gaia_id1,
+                            base::Time::FromMillisecondsSinceUnixEpoch(1001)));
   MimicMemberAddedServerSide(group_id, member_gaia_id1);
   WaitForGroupUpdated(group_id);
   testing::Mock::VerifyAndClearExpectations(&model_observer());
 
   const GaiaId member_gaia_id2("gaia_id2");
   EXPECT_CALL(model_observer(),
-              OnMemberAdded(group_id, member_gaia_id2, NotNullTime()));
+              OnMemberAdded(group_id, member_gaia_id2,
+                            base::Time::FromMillisecondsSinceUnixEpoch(1003)));
   MimicMemberAddedServerSide(group_id, member_gaia_id2);
   WaitForGroupUpdated(group_id);
   testing::Mock::VerifyAndClearExpectations(&model_observer());
 
   // Test that OnMemberRemoved() is called when a member is removed.
-  EXPECT_CALL(model_observer(),
-              OnMemberRemoved(group_id, member_gaia_id1, NotNullTime()));
+  EXPECT_CALL(
+      model_observer(),
+      OnMemberRemoved(group_id, member_gaia_id1,
+                      base::Time::FromMillisecondsSinceUnixEpoch(1005)));
   MimicMemberRemovedServerSide(group_id, member_gaia_id1);
   WaitForGroupUpdated(group_id);
 }
diff --git a/components/data_sharing/test_support/fake_data_sharing_sdk_delegate.h b/components/data_sharing/test_support/fake_data_sharing_sdk_delegate.h
index c293c17..0a48a81a 100644
--- a/components/data_sharing/test_support/fake_data_sharing_sdk_delegate.h
+++ b/components/data_sharing/test_support/fake_data_sharing_sdk_delegate.h
@@ -78,6 +78,8 @@
 
   void SetUserGaiaId(const GaiaId& gaia_id);
 
+  std::map<GroupId, data_sharing_pb::GroupData>* groups() { return &groups_; }
+
  private:
   std::map<GroupId, data_sharing_pb::GroupData> groups_;
   std::map<std::string, GaiaId> email_to_gaia_id_;
diff --git a/components/embedder_support/user_agent_utils.cc b/components/embedder_support/user_agent_utils.cc
index 47abbd2..98104fa 100644
--- a/components/embedder_support/user_agent_utils.cc
+++ b/components/embedder_support/user_agent_utils.cc
@@ -15,7 +15,6 @@
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/containers/contains.h"
-#include "base/debug/stack_trace.h"
 #include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/no_destructor.h"
diff --git a/components/feed/core/v2/web_feed_subscription_coordinator.cc b/components/feed/core/v2/web_feed_subscription_coordinator.cc
index 044f678..f07fb81 100644
--- a/components/feed/core/v2/web_feed_subscription_coordinator.cc
+++ b/components/feed/core/v2/web_feed_subscription_coordinator.cc
@@ -9,7 +9,6 @@
 #include <optional>
 #include <ostream>
 
-#include "base/debug/stack_trace.h"
 #include "base/feature_list.h"
 #include "base/functional/callback_helpers.h"
 #include "base/location.h"
diff --git a/components/history/core/browser/visitsegment_database.cc b/components/history/core/browser/visitsegment_database.cc
index 5009ea9..c9f08c84 100644
--- a/components/history/core/browser/visitsegment_database.cc
+++ b/components/history/core/browser/visitsegment_database.cc
@@ -17,6 +17,7 @@
 #include "base/check_op.h"
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/strings/string_util.h"
 #include "components/history/core/browser/page_usage_data.h"
 #include "components/history/core/browser/segment_scorer.h"
@@ -334,6 +335,8 @@
   DCHECK_GE(max_result_count, 0);
   // Tracks (hostname, title) pairs already added.
   std::set<HostTitleKey> added_host_titles;
+  // Tracks the number of duplicate tiles.
+  int duplicate_tiles = 0;
   for (std::unique_ptr<PageUsageData>& pud : segments) {
     statement2.BindInt64(0, pud->GetID());
     if (statement2.Step()) {
@@ -352,12 +355,18 @@
           if (results.size() >= static_cast<size_t>(max_result_count)) {
             break;
           }
+        } else {
+          duplicate_tiles++;
         }
       }
     }
     statement2.Reset(true);
   }
-
+  if (visual_deduplication_enabled && !histogram_recorded_) {
+    base::UmaHistogramCounts100("History.MostVisitedTilesVisualDeduplication",
+                                duplicate_tiles);
+    histogram_recorded_ = true;
+  }
   return results;
 }
 
diff --git a/components/history/core/browser/visitsegment_database.h b/components/history/core/browser/visitsegment_database.h
index 04d57b0..59ec3bf 100644
--- a/components/history/core/browser/visitsegment_database.h
+++ b/components/history/core/browser/visitsegment_database.h
@@ -88,6 +88,9 @@
   // `from_segment_id` are updated to `to_segment_id` and `from_segment_id` is
   // deleted. Returns true on success.
   bool MergeSegments(SegmentID from_segment_id, SegmentID to_segment_id);
+  // Tracks if the visual deduplication histogram has been recorded for this
+  // instance.
+  bool histogram_recorded_ = false;
 };
 
 }  // namespace history
diff --git a/components/history_embeddings/sql_database.cc b/components/history_embeddings/sql_database.cc
index 0e12efcd..cf45cb95 100644
--- a/components/history_embeddings/sql_database.cc
+++ b/components/history_embeddings/sql_database.cc
@@ -724,7 +724,7 @@
   if (blob.empty()) {
     return false;
   }
-  statement.BindBlob(3, blob);
+  statement.BindBlob(3, std::move(blob));
   bool result = statement.Run();
 
   if (result) {
diff --git a/components/leveldb_proto/internal/shared_proto_database_client_unittest.cc b/components/leveldb_proto/internal/shared_proto_database_client_unittest.cc
index be41c7e..ed3e2474 100644
--- a/components/leveldb_proto/internal/shared_proto_database_client_unittest.cc
+++ b/components/leveldb_proto/internal/shared_proto_database_client_unittest.cc
@@ -7,7 +7,6 @@
 #include <set>
 #include <string>
 
-#include "base/debug/stack_trace.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
diff --git a/components/memory_pressure/system_memory_pressure_evaluator_mac.cc b/components/memory_pressure/system_memory_pressure_evaluator_mac.cc
index 1188d08..45b4225 100644
--- a/components/memory_pressure/system_memory_pressure_evaluator_mac.cc
+++ b/components/memory_pressure/system_memory_pressure_evaluator_mac.cc
@@ -12,6 +12,7 @@
 #include <cmath>
 
 #include "base/check_op.h"
+#include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/mac/mac_util.h"
 #include "base/memory/memory_pressure_monitor.h"
@@ -19,6 +20,15 @@
 
 namespace memory_pressure::mac {
 
+namespace {
+// When enabled, the moderate memory pressure signals on macOS are ignored and
+// treated as 'none'. This is to experiment with the idea that the 'warn'
+// level signal from the OS is not always an accurate or useful signal.
+BASE_FEATURE(kSkipModerateMemoryPressureLevelMac,
+             "SkipModerateMemoryPressureLevelMac",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+}  // namespace
+
 base::MemoryPressureListener::MemoryPressureLevel
 SystemMemoryPressureEvaluator::MemoryPressureLevelForMacMemoryPressureLevel(
     int mac_memory_pressure_level) {
@@ -26,6 +36,9 @@
     case DISPATCH_MEMORYPRESSURE_NORMAL:
       return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
     case DISPATCH_MEMORYPRESSURE_WARN:
+      if (base::FeatureList::IsEnabled(kSkipModerateMemoryPressureLevelMac)) {
+        return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
+      }
       return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
     case DISPATCH_MEMORYPRESSURE_CRITICAL:
       return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
diff --git a/components/offline_pages/core/model/store_visuals_task.cc b/components/offline_pages/core/model/store_visuals_task.cc
index ee4878e..3ff074a 100644
--- a/components/offline_pages/core/model/store_visuals_task.cc
+++ b/components/offline_pages/core/model/store_visuals_task.cc
@@ -38,12 +38,12 @@
 bool StoreThumbnailSync(sql::Database* db,
                         int64_t offline_id,
                         base::Time expiration,
-                        const std::string& thumbnail) {
+                        std::string thumbnail) {
   static const char kUpdateSql[] =
       "UPDATE page_thumbnails SET expiration=?,thumbnail=? WHERE offline_id=?";
   sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kUpdateSql));
   statement.BindTime(0, expiration);
-  statement.BindBlob(1, thumbnail);
+  statement.BindBlob(1, std::move(thumbnail));
   statement.BindInt64(2, offline_id);
   return statement.Run();
 }
@@ -51,20 +51,20 @@
 bool StoreFaviconSync(sql::Database* db,
                       int64_t offline_id,
                       base::Time expiration,
-                      const std::string& favicon) {
+                      std::string favicon) {
   static const char kUpdateSql[] =
       "UPDATE page_thumbnails SET expiration=?,favicon=? WHERE offline_id=?";
   sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kUpdateSql));
   statement.BindTime(0, expiration);
-  statement.BindBlob(1, favicon);
+  statement.BindBlob(1, std::move(favicon));
   statement.BindInt64(2, offline_id);
   return statement.Run();
 }
 
 bool StoreVisualsSync(int64_t offline_id,
                       base::Time expiration,
-                      const std::string& thumbnail,
-                      const std::string& favicon,
+                      std::string thumbnail,
+                      std::string favicon,
                       sql::Database* db) {
   if (expiration == base::Time()) {
     expiration = OfflineTimeNow() + kVisualsExpirationDelta;
@@ -74,12 +74,14 @@
     return false;
 
   if (!thumbnail.empty() &&
-      !StoreThumbnailSync(db, offline_id, expiration, thumbnail))
+      !StoreThumbnailSync(db, offline_id, expiration, std::move(thumbnail))) {
     return false;
+  }
 
   if (!favicon.empty() &&
-      !StoreFaviconSync(db, offline_id, expiration, favicon))
+      !StoreFaviconSync(db, offline_id, expiration, std::move(favicon))) {
     return false;
+  }
 
   return true;
 }
diff --git a/components/offline_pages/core/offline_page_metadata_store_unittest.cc b/components/offline_pages/core/offline_page_metadata_store_unittest.cc
index 4041156..c4c7625 100644
--- a/components/offline_pages/core/offline_page_metadata_store_unittest.cc
+++ b/components/offline_pages/core/offline_page_metadata_store_unittest.cc
@@ -516,8 +516,7 @@
       meta_table.Init(&db, 2, OfflinePageMetadataStore::kCompatibleVersion));
 }
 
-bool InsertVisualsVersion3(sql::Database* db,
-                           const OfflinePageVisuals& visuals) {
+bool InsertVisualsVersion3(sql::Database* db, OfflinePageVisuals visuals) {
   static const char kInsertVisualsSql[] =
       "INSERT INTO page_thumbnails"
       " (offline_id,expiration,thumbnail) VALUES (?,?,?)";
@@ -525,7 +524,7 @@
       db->GetCachedStatement(SQL_FROM_HERE, kInsertVisualsSql));
   statement.BindInt64(0, visuals.offline_id);
   statement.BindInt64(1, store_utils::ToDatabaseTime(visuals.expiration));
-  statement.BindBlob(2, visuals.thumbnail);
+  statement.BindBlob(2, std::move(visuals.thumbnail));
   return statement.Run();
 }
 
diff --git a/components/omnibox/browser/actions/contextual_search_action.cc b/components/omnibox/browser/actions/contextual_search_action.cc
index 0105ab3..bc1cb36 100644
--- a/components/omnibox/browser/actions/contextual_search_action.cc
+++ b/components/omnibox/browser/actions/contextual_search_action.cc
@@ -61,12 +61,19 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 ContextualSearchOpenLensAction::ContextualSearchOpenLensAction()
-    : OmniboxAction(OmniboxAction::LabelStrings(
-                        l10n_util::GetStringUTF16(GetOpenLensActionLabelId()),
-                        u"",
-                        u"",
-                        u""),
-                    GURL()) {}
+    : OmniboxAction(
+          omnibox_feature_configs::Toolbelt::Get().enabled
+              ? OmniboxAction::LabelStrings(
+                    IDS_CONTEXTUAL_SEARCH_OPEN_LENS_ACTION_HINT,
+                    IDS_CONTEXTUAL_SEARCH_OPEN_LENS_ACTION_SUGGESTION_CONTENTS,
+                    IDS_ACC_CONTEXTUAL_SEARCH_OPEN_LENS_ACTION_SUFFIX,
+                    IDS_ACC_CONTEXTUAL_SEARCH_OPEN_LENS_ACTION)
+              : OmniboxAction::LabelStrings(
+                    l10n_util::GetStringUTF16(GetOpenLensActionLabelId()),
+                    u"",
+                    u"",
+                    u""),
+          GURL()) {}
 
 OmniboxActionId ContextualSearchOpenLensAction::ActionId() const {
   return OmniboxActionId::CONTEXTUAL_SEARCH_OPEN_LENS;
diff --git a/components/omnibox/browser/autocomplete_result.cc b/components/omnibox/browser/autocomplete_result.cc
index 427d060..bc3d921 100644
--- a/components/omnibox/browser/autocomplete_result.cc
+++ b/components/omnibox/browser/autocomplete_result.cc
@@ -20,7 +20,6 @@
 #include "base/command_line.h"
 #include "base/containers/adapters.h"
 #include "base/containers/contains.h"
-#include "base/debug/stack_trace.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/not_fatal_until.h"
@@ -581,6 +580,11 @@
         }
 #endif
       }
+      if (omnibox_feature_configs::Toolbelt::Get().enabled) {
+        sections.push_back(
+            std::make_unique<DesktopWebSearchZpsContextualOnlySection>(
+                suggestion_groups_map_, 1u, 0u));
+      }
     } else if constexpr (is_ios) {
       if (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET) {
         size_t num_trending_queries =
@@ -763,8 +767,14 @@
   if (size_before == 0) {
     return;
   }
+  const bool toolbelt_enabled =
+      omnibox_feature_configs::Toolbelt::Get().enabled;
   for (size_t i = 0; i < matches_.size(); i++) {
     for (size_t j = 0; j < matches_[i].actions.size(); j++) {
+      if (toolbelt_enabled &&
+          matches_[i].type == AutocompleteMatchType::NULL_RESULT_MESSAGE) {
+        continue;
+      }
       if (matches_[i].actions[j]->ActionId() == OmniboxActionId::PEDAL) {
         *matches_.insert(matches_.begin() + i + 1,
                          matches_[i].CreateActionMatch(j));
diff --git a/components/omnibox/browser/contextual_search_provider.cc b/components/omnibox/browser/contextual_search_provider.cc
index 58184ee..fc06ed5 100644
--- a/components/omnibox/browser/contextual_search_provider.cc
+++ b/components/omnibox/browser/contextual_search_provider.cc
@@ -24,6 +24,7 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "components/omnibox/browser/actions/contextual_search_action.h"
+#include "components/omnibox/browser/actions/omnibox_pedal_provider.h"
 #include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
@@ -80,6 +81,17 @@
       /*is_keyword_result=*/true, results);
 }
 
+// Requirements: web or SRP, non-NTP, with empty input, and local files are
+// allowed but not other local schemes.
+bool IsLensEntrypointAvailable(const AutocompleteInput& input,
+                               AutocompleteProviderClient* client) {
+  return (omnibox::IsOtherWebPage(input.current_page_classification()) ||
+          omnibox::IsSearchResultsPage(input.current_page_classification())) &&
+         (input.current_url().SchemeIsHTTPOrHTTPS() ||
+          input.current_url().SchemeIs(url::kFileScheme)) &&
+         input.IsZeroSuggest() && client->IsLensEnabled();
+}
+
 }  // namespace
 
 void ContextualSearchProvider::Start(
@@ -97,16 +109,13 @@
 
   const auto [input, starter_pack_engine] = AdjustInputForStarterPackKeyword(
       autocomplete_input, client()->GetTemplateURLService());
+  const bool toolbelted = MaybeAddToolbeltMatch(input, starter_pack_engine);
   if (!starter_pack_engine) {
-    // Only surface the action match that helps the user find their way to Lens.
-    // Requirements: web or SRP, non-NTP, with empty input, and local files are
-    // allowed but not other local schemes.
-    if ((omnibox::IsOtherWebPage(input.current_page_classification()) ||
-         omnibox::IsSearchResultsPage(input.current_page_classification())) &&
-        (input.current_url().SchemeIsHTTPOrHTTPS() ||
-         input.current_url().SchemeIs(url::kFileScheme)) &&
-        input.IsZeroSuggest() && client()->IsLensEnabled()) {
-      AddPageSearchActionMatches(input);
+    // Note, the dedicated entrypoint match is not added if the toolbelt is
+    // included because the toolbelt will already have an action to serve
+    // as the Lens entrypoint.
+    if (!toolbelted && IsLensEntrypointAvailable(input, client())) {
+      AddLensEntrypointMatch(input);
     }
     return;
   }
@@ -300,9 +309,9 @@
   }
 }
 
-void ContextualSearchProvider::AddPageSearchActionMatches(
+void ContextualSearchProvider::AddLensEntrypointMatch(
     const AutocompleteInput& input) {
-  // These matches are effectively pedals that don't require any query matching.
+  // This match is effectively a pedal that doesn't require any query matching.
   // Relevance depends on the page class, and selecting an appropriate score is
   // necessary to avoid downstream conflicts in grouping framework sort order.
   AutocompleteMatch match(
@@ -375,6 +384,42 @@
   matches_.push_back(match);
 }
 
+bool ContextualSearchProvider::MaybeAddToolbeltMatch(
+    const AutocompleteInput& input,
+    const TemplateURL* input_starter_pack_engine) {
+  const auto& config = omnibox_feature_configs::Toolbelt::Get();
+  if (!config.enabled ||
+      (!config.keep_toolbelt_after_zps && !input.IsZeroSuggest())) {
+    return false;
+  }
+  AutocompleteMatch match(this, omnibox::kToolbeltRelevance, false,
+                          AutocompleteMatchType::NULL_RESULT_MESSAGE);
+  match.transition = ui::PAGE_TRANSITION_GENERATED;
+  match.suggest_type = omnibox::SuggestType::TYPE_NATIVE_CHROME;
+  match.suggestion_group_id = omnibox::GroupId::GROUP_CONTEXTUAL_SEARCH_ACTION;
+
+  match.description = l10n_util::GetStringUTF16(IDS_OMNIBOX_TOOLBELT_LABEL);
+  if (!match.description.empty()) {
+    match.description_class = {{0, ACMatchClassification::NONE}};
+  }
+
+  if (config.always_include_lens_action ||
+      IsLensEntrypointAvailable(input, client())) {
+    match.actions.push_back(
+        base::MakeRefCounted<ContextualSearchOpenLensAction>());
+  }
+  for (const std::u16string query :
+       {u"launch incognito", u"clear data", u"update chrome"}) {
+    if (OmniboxPedal* pedal =
+            client()->GetPedalProvider()->FindPedalMatch(query)) {
+      match.actions.push_back(pedal);
+    }
+  }
+
+  matches_.push_back(match);
+  return true;
+}
+
 const TemplateURL* ContextualSearchProvider::GetKeywordTemplateURL() const {
   return client()->GetTemplateURLService()->GetTemplateURLForKeyword(
       input_keyword_);
diff --git a/components/omnibox/browser/contextual_search_provider.h b/components/omnibox/browser/contextual_search_provider.h
index 1b284974..5af14ab 100644
--- a/components/omnibox/browser/contextual_search_provider.h
+++ b/components/omnibox/browser/contextual_search_provider.h
@@ -26,6 +26,8 @@
 // distinct from the ZeroSuggestProvider. It does its main work when explicitly
 // invoked via the '@page' keyword mode, and also surfaces action matches for
 // empty/zero inputs to help the user find their way into the '@page' scope.
+// It also produces the omnibox toolbelt which can be used to enter various
+// forms of scoped search (Lens or some starter pack keywords, for example).
 class ContextualSearchProvider : public BaseSearchProvider {
  public:
   ContextualSearchProvider(AutocompleteProviderClient* client,
@@ -71,15 +73,21 @@
       const SearchSuggestionParser::Results& results,
       const AutocompleteInput& input);
 
-  // Populates `matches_` with special matches that help the user find their
-  // way into the '@page' scope.
-  void AddPageSearchActionMatches(const AutocompleteInput& input);
+  // Adds the Lens entrypoint takeover action match.
+  void AddLensEntrypointMatch(const AutocompleteInput& input);
 
   // Adds a default match for verbatim input, or keyword instructions if there
   // is no input yet. This is the match that holds the omnibox in keyword mode
   // when no other matches are available yet.
   void AddDefaultVerbatimMatch(const AutocompleteInput& input);
 
+  // Conditionally appends a special toolbelt match with various actions.
+  // The `input_starter_pack_engine` may be nullptr and its value can affect the
+  // actions included on the toolbelt. Returns true if toolbelt is added; false
+  // otherwise.
+  bool MaybeAddToolbeltMatch(const AutocompleteInput& input,
+                             const TemplateURL* input_starter_pack_engine);
+
   // Gets the '@page' starter pack engine using `input_keyword_`.
   const TemplateURL* GetKeywordTemplateURL() const;
 
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc
index 1f5d30d..dbbee84 100644
--- a/components/omnibox/browser/omnibox_edit_model.cc
+++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -2391,7 +2391,10 @@
 
   // NULL_RESULT_MESSAGE matches are informational only and cannot be acted
   // upon. Immediately return when attempting to open one.
-  if (match.type == AutocompleteMatchType::NULL_RESULT_MESSAGE) {
+  // When the toolbelt is enabled, a non-selectable match may still have
+  // selectable actions on it, and these can still be executed.
+  if (match.type == AutocompleteMatchType::NULL_RESULT_MESSAGE &&
+      (!omnibox_feature_configs::Toolbelt::Get().enabled || !action)) {
     return;
   }
 
diff --git a/components/omnibox/browser/omnibox_prefs.cc b/components/omnibox/browser/omnibox_prefs.cc
index 0b04bdc..dd6f822 100644
--- a/components/omnibox/browser/omnibox_prefs.cc
+++ b/components/omnibox/browser/omnibox_prefs.cc
@@ -22,6 +22,7 @@
 namespace {
 constexpr int kAIModeSearchSuggestAllowed = 0;
 constexpr int kAIModeSearchSuggestDisallowed = 1;
+constexpr int kAIModeAllowed = 0;
 }  // namespace
 
 void RegisterProfilePrefs(PrefRegistrySimple* registry) {
@@ -47,8 +48,11 @@
   registry->RegisterIntegerPref(kShownCountHistoryScopePromo, 0);
   registry->RegisterIntegerPref(kShownCountHistoryEmbeddingsScopePromo, 0);
   registry->RegisterIntegerPref(kFocusedSrpWebCount, 0);
+  // TODO(crbug.com/422744656): Remove `kAIModeSearchSuggestSettings` pref once
+  // `kAIModeSettings` is implemented.
   registry->RegisterIntegerPref(omnibox::kAIModeSearchSuggestSettings,
                                 kAIModeSearchSuggestAllowed);
+  registry->RegisterIntegerPref(omnibox::kAIModeSettings, kAIModeAllowed);
 }
 
 void SetUserPreferenceForZeroSuggestCachedResponse(
diff --git a/components/omnibox/browser/omnibox_prefs.h b/components/omnibox/browser/omnibox_prefs.h
index 78978db..e6c9f09 100644
--- a/components/omnibox/browser/omnibox_prefs.h
+++ b/components/omnibox/browser/omnibox_prefs.h
@@ -87,6 +87,7 @@
 inline constexpr char kFocusedSrpWebCount[] = "omnibox.focused_srp_web_count";
 inline constexpr char kAIModeSearchSuggestSettings[] =
     "omnibox.ai_mode_search_suggest_settings";
+inline constexpr char kAIModeSettings[] = "omnibox.ai_mode_settings";
 
 // Many of the prefs defined above are registered locally where they're used.
 // New prefs should be added here and ordered the same as they're defined above.
diff --git a/components/omnibox/browser/suggestion_group_util.h b/components/omnibox/browser/suggestion_group_util.h
index 5b08435..0b49809 100644
--- a/components/omnibox/browser/suggestion_group_util.h
+++ b/components/omnibox/browser/suggestion_group_util.h
@@ -49,6 +49,10 @@
 // IPH suggestions get a default relevance of 300 to ensure they appear at the
 // bottom.
 inline constexpr int kIPHZeroSuggestRelevance = 300;
+// The toolbelt appears even lower than IPH. It's almost like a UI element that
+// comes after all the matches, but it works like a match because it still needs
+// to be accessible as a popup selection, follow tab order, read a11y text, etc.
+inline constexpr int kToolbeltRelevance = 200;
 
 using GroupConfigMap = std::unordered_map<GroupId, GroupConfig>;
 
diff --git a/components/omnibox/common/omnibox_feature_configs.cc b/components/omnibox/common/omnibox_feature_configs.cc
index a136550..b4689f2 100644
--- a/components/omnibox/common/omnibox_feature_configs.cc
+++ b/components/omnibox/common/omnibox_feature_configs.cc
@@ -178,6 +178,21 @@
           .Get();
 }
 
+BASE_FEATURE(Toolbelt::kOmniboxToolbelt,
+             "OmniboxToolbelt",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+Toolbelt::Toolbelt() {
+  enabled = base::FeatureList::IsEnabled(kOmniboxToolbelt);
+  keep_toolbelt_after_zps =
+      base::FeatureParam<bool>(&kOmniboxToolbelt, "KeepToolbeltAfterZps", false)
+          .Get();
+  always_include_lens_action =
+      base::FeatureParam<bool>(&kOmniboxToolbelt, "AlwaysIncludeLensAction",
+                               false)
+          .Get();
+}
+
 DocumentProvider::DocumentProvider() {
   enabled = base::FeatureList::IsEnabled(omnibox::kDocumentProvider);
   min_query_length =
diff --git a/components/omnibox/common/omnibox_feature_configs.h b/components/omnibox/common/omnibox_feature_configs.h
index f0f9847..33f23d2 100644
--- a/components/omnibox/common/omnibox_feature_configs.h
+++ b/components/omnibox/common/omnibox_feature_configs.h
@@ -218,6 +218,24 @@
   bool local_history_non_normalized_contents;
 };
 
+// A config struct for the omnibox toolbelt.
+struct Toolbelt : Config<Toolbelt> {
+  DECLARE_FEATURE(kOmniboxToolbelt);
+
+  Toolbelt();
+
+  // Whether the toolbelt is to be included in the omnibox.
+  bool enabled;
+
+  // Whether the toolbelt will be preserved after zero suggest when user types.
+  bool keep_toolbelt_after_zps;
+
+  // Whether the lens entrypoint action should stay unconditionally on the
+  // toolbelt. When this is false, the regular triggering conditions apply
+  // so the action can sometimes be included or sometimes not.
+  bool always_include_lens_action;
+};
+
 // If enabled, adjusts the indentation of the omnibox input and matches to fix
 // the visual shift in omnibox input text when the omnibox popup opens.
 struct AdjustOmniboxIndent : Config<AdjustOmniboxIndent> {
diff --git a/components/omnibox_strings.grdp b/components/omnibox_strings.grdp
index 952d911..eb2d504 100644
--- a/components/omnibox_strings.grdp
+++ b/components/omnibox_strings.grdp
@@ -19,6 +19,23 @@
   <message name="IDS_CONTEXTUAL_SEARCH_OPEN_LENS_ACTION_LABEL_ALT2" desc="Second alternative label text for the action match that opens lens.">
     Ask Google Search about this page
   </message>
+  <message name="IDS_OMNIBOX_TOOLBELT_LABEL" desc="The label shown before actions in the omnibox toolbelt." translateable="false">
+    Your tools
+  </message>
+
+  <message name="IDS_CONTEXTUAL_SEARCH_OPEN_LENS_ACTION_HINT" desc="The button text contents to suggest an omnibox action that opens lens" translateable="false">
+    Ask Google about this page
+  </message>
+  <message name="IDS_CONTEXTUAL_SEARCH_OPEN_LENS_ACTION_SUGGESTION_CONTENTS" desc="The button hover tooltip text to describe omnibox action that opens lens" translateable="false">
+    Ask Google about this page
+  </message>
+  <message name="IDS_ACC_CONTEXTUAL_SEARCH_OPEN_LENS_ACTION_SUFFIX" desc="Suffix for spoken suggestion description with omnibox action button, to explain keystroke used to open lens." translateable="false">
+    <ph name="CONTEXTUAL_SEARCH_OPEN_LENS_FOCUSED_FRIENDLY_MATCH_TEXT">$1<ex>The Chromium Projects http://www.chromium.org bookmark</ex></ph>, press Enter to ask Google about this page
+  </message>
+  <message name="IDS_ACC_CONTEXTUAL_SEARCH_OPEN_LENS_ACTION" desc="Announcement when lens action button is focused" translateable="false">
+    Ask Google about this page button, activate to ask Google about this page
+  </message>
+
   <message name="IDS_EMPTY_KEYWORD_VALUE" desc="Shown in the location bar drop down when the user enters a string that matches a chrome keyword, but they haven't entered any text following the chrome keyword">
     &lt;Type search term&gt;
   </message>
diff --git a/components/optimization_guide/core/BUILD.gn b/components/optimization_guide/core/BUILD.gn
index 19d34b7..be0091e58 100644
--- a/components/optimization_guide/core/BUILD.gn
+++ b/components/optimization_guide/core/BUILD.gn
@@ -526,11 +526,6 @@
     "//services/network/public/cpp",
   ]
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
-  if (is_ios) {
-    deps += [ "//components/services/unzip:in_process" ]
-  } else {
-    deps += [ "//components/services/unzip/content" ]
-  }
 }
 
 source_set("filters_test_support") {
@@ -828,9 +823,6 @@
     "//ui/base",
     "//url:url",
   ]
-  if (!is_ios) {
-    deps += [ "//components/services/unzip/content" ]
-  }
 
   if (build_with_model_execution) {
     deps += [ "//components/component_updater" ]
diff --git a/components/optimization_guide/core/model_util.cc b/components/optimization_guide/core/model_util.cc
index d4d1bbb..b4f2b29 100644
--- a/components/optimization_guide/core/model_util.cc
+++ b/components/optimization_guide/core/model_util.cc
@@ -153,6 +153,8 @@
       return "NotificationPermissionsV3";
     case proto::OPTIMIZATION_TARGET_MODEL_EXECUTION_FEATURE_PROOFREADER_API:
       return "ModelExecutionFeatureProofreaderApi";
+    case proto::OPTIMIZATION_TARGET_SEGMENTATION_IOS_DEFAULT_BROWSER_PROMO:
+      return "SegmentationIosDefaultBrowserPromo";
       // Whenever a new value is added, make sure to add it to the OptTarget
       // variant list in
       // //tools/metrics/histograms/metadata/optimization/histograms.xml.
diff --git a/components/optimization_guide/core/prediction_manager.cc b/components/optimization_guide/core/prediction_manager.cc
index c3af940..b52cd480 100644
--- a/components/optimization_guide/core/prediction_manager.cc
+++ b/components/optimization_guide/core/prediction_manager.cc
@@ -44,6 +44,7 @@
 #include "components/optimization_guide/optimization_guide_internals/webui/optimization_guide_internals.mojom.h"
 #include "components/optimization_guide/proto/models.pb.h"
 #include "components/prefs/pref_service.h"
+#include "components/services/unzip/public/cpp/unzip.h"
 #include "google_apis/google_api_keys.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -185,12 +186,14 @@
     const base::FilePath& models_dir_path,
     OptimizationGuideLogger* optimization_guide_logger,
     BackgroundDownloadServiceProvider background_download_service_provider,
-    ComponentUpdatesEnabledProvider component_updates_enabled_provider)
+    ComponentUpdatesEnabledProvider component_updates_enabled_provider,
+    unzip::UnzipperFactory unzipper_factory)
     : prediction_model_download_manager_(nullptr),
       prediction_model_store_(prediction_model_store),
       url_loader_factory_(url_loader_factory),
       optimization_guide_logger_(optimization_guide_logger),
       component_updates_enabled_provider_(component_updates_enabled_provider),
+      unzipper_factory_(std::move(unzipper_factory)),
       prediction_model_fetch_timer_(
           pref_service,
           base::BindRepeating(
@@ -783,6 +786,7 @@
                 // base::Unretained is safe here because the
                 // PredictionModelDownloadManager is owned by `this`
                 base::Unretained(this)),
+            unzipper_factory_,
             base::ThreadPool::CreateSequencedTaskRunner(
                 {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
                  base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}));
@@ -823,7 +827,7 @@
       base::FilePath base_model_dir =
           GetBaseModelDirForDownload(optimization_target);
       entry->BuildModel(
-          base_model_dir,
+          base_model_dir, unzipper_factory_,
           base::BindOnce(&PredictionManager::OnPredictionModelOverrideLoaded,
                          ui_weak_ptr_factory_.GetWeakPtr(),
                          optimization_target));
diff --git a/components/optimization_guide/core/prediction_manager.h b/components/optimization_guide/core/prediction_manager.h
index e5543da7..8a6e602 100644
--- a/components/optimization_guide/core/prediction_manager.h
+++ b/components/optimization_guide/core/prediction_manager.h
@@ -37,6 +37,16 @@
 class SharedURLLoaderFactory;
 }  // namespace network
 
+namespace unzip::mojom {
+class Unzipper;
+}  // namespace unzip::mojom
+
+namespace unzip {
+// TODO: crbug.com/421262905 - Avoid duplicating this alias.
+using UnzipperFactory =
+    base::RepeatingCallback<mojo::PendingRemote<mojom::Unzipper>()>;
+}  // namespace unzip
+
 class OptimizationGuideLogger;
 class PrefService;
 
@@ -71,7 +81,8 @@
       const base::FilePath& models_dir_path,
       OptimizationGuideLogger* optimization_guide_logger,
       BackgroundDownloadServiceProvider background_download_service_provider,
-      ComponentUpdatesEnabledProvider component_updates_enabled_provider);
+      ComponentUpdatesEnabledProvider component_updates_enabled_provider,
+      unzip::UnzipperFactory unzipper_factory);
 
   PredictionManager(const PredictionManager&) = delete;
   PredictionManager& operator=(const PredictionManager&) = delete;
@@ -340,6 +351,9 @@
   // are enabled.
   ComponentUpdatesEnabledProvider component_updates_enabled_provider_;
 
+  // Callback to build Unzipper remotes.
+  unzip::UnzipperFactory unzipper_factory_;
+
   // Time the prediction manager got initialized.
   // TODO(crbug.com/40861855): Remove this old model store once the new model
   // store is launched.
diff --git a/components/optimization_guide/core/prediction_manager_unittest.cc b/components/optimization_guide/core/prediction_manager_unittest.cc
index 0833f6a0..2b8f77be 100644
--- a/components/optimization_guide/core/prediction_manager_unittest.cc
+++ b/components/optimization_guide/core/prediction_manager_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/base64.h"
 #include "base/command_line.h"
 #include "base/files/file_util.h"
+#include "base/functional/bind.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/sequenced_task_runner.h"
@@ -39,6 +40,7 @@
 #include "components/optimization_guide/proto/hint_cache.pb.h"
 #include "components/optimization_guide/proto/models.pb.h"
 #include "components/prefs/testing_pref_service.h"
+#include "components/services/unzip/in_process_unzipper.h"
 #include "components/variations/scoped_variations_ids_provider.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
@@ -146,6 +148,7 @@
       : PredictionModelDownloadManager(
             /*download_service=*/nullptr,
             get_base_model_dir_for_download_callback,
+            base::BindRepeating(&unzip::LaunchInProcessUnzipper),
             task_runner) {}
   ~FakePredictionModelDownloadManager() override = default;
 
@@ -325,7 +328,6 @@
   base::flat_map<proto::OptimizationTarget, int64_t> expected_version_;
 };
 
-
 class TestPredictionManager : public PredictionManager {
  public:
   TestPredictionManager(
@@ -346,7 +348,8 @@
             &optimization_guide_logger_,
             /*background_download_service_provider=*/
             base::OnceCallback<download::BackgroundDownloadService*()>(),
-            component_updates_enabled_provider) {}
+            component_updates_enabled_provider,
+            base::BindRepeating(&unzip::LaunchInProcessUnzipper)) {}
 
   ~TestPredictionManager() override = default;
 
diff --git a/components/optimization_guide/core/prediction_model_download_manager.cc b/components/optimization_guide/core/prediction_model_download_manager.cc
index c40acdd..d257cf0 100644
--- a/components/optimization_guide/core/prediction_model_download_manager.cc
+++ b/components/optimization_guide/core/prediction_model_download_manager.cc
@@ -28,16 +28,11 @@
 #include "components/optimization_guide/core/prediction_model_download_observer.h"
 #include "components/optimization_guide/core/prediction_model_store.h"
 #include "components/services/unzip/public/cpp/unzip.h"
+#include "components/services/unzip/public/mojom/unzipper.mojom.h"
 #include "crypto/hash.h"
 #include "google_apis/common/api_key_request_util.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 
-#if BUILDFLAG(IS_IOS)
-#include "components/services/unzip/in_process_unzipper.h"  // nogncheck
-#else
-#include "components/services/unzip/content/unzip_service.h"  // nogncheck
-#endif
-
 namespace optimization_guide {
 
 namespace {
@@ -102,12 +97,14 @@
 PredictionModelDownloadManager::PredictionModelDownloadManager(
     download::BackgroundDownloadService* download_service,
     GetBaseModelDirForDownloadCallback get_base_model_dir_for_download_callback,
+    unzip::UnzipperFactory unzipper_factory,
     scoped_refptr<base::SequencedTaskRunner> background_task_runner)
     : download_service_(download_service),
       is_available_for_downloads_(true),
       api_key_(features::GetOptimizationGuideServiceAPIKey()),
       get_base_model_dir_for_download_callback_(
           get_base_model_dir_for_download_callback),
+      unzipper_factory_(std::move(unzipper_factory)),
       background_task_runner_(background_task_runner) {}
 
 PredictionModelDownloadManager::~PredictionModelDownloadManager() = default;
@@ -324,13 +321,8 @@
     return;
   }
 
-#if BUILDFLAG(IS_IOS)
-  auto unzipper = unzip::LaunchInProcessUnzipper();
-#else
-  auto unzipper = unzip::LaunchUnzipper();
-#endif
   unzip::Unzip(
-      std::move(unzipper), download_file_path, base_model_dir,
+      unzipper_factory_.Run(), download_file_path, base_model_dir,
       unzip::mojom::UnzipOptions::New(), unzip::AllContents(),
       base::DoNothing(),
       base::BindOnce(&PredictionModelDownloadManager::OnDownloadUnzipped,
diff --git a/components/optimization_guide/core/prediction_model_download_manager.h b/components/optimization_guide/core/prediction_model_download_manager.h
index 179917af..05b106c 100644
--- a/components/optimization_guide/core/prediction_model_download_manager.h
+++ b/components/optimization_guide/core/prediction_model_download_manager.h
@@ -11,6 +11,7 @@
 #include <string>
 
 #include "base/files/file_path.h"
+#include "base/functional/callback_forward.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
@@ -18,11 +19,22 @@
 #include "components/download/public/background_service/download_params.h"
 #include "components/optimization_guide/core/prediction_model_store.h"
 #include "components/optimization_guide/proto/models.pb.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 
 namespace download {
 class BackgroundDownloadService;
 }  // namespace download
 
+namespace unzip::mojom {
+class Unzipper;
+}  // namespace unzip::mojom
+
+namespace unzip {
+// TODO: crbug.com/421262905 - Avoid duplicating this alias.
+using UnzipperFactory =
+    base::RepeatingCallback<mojo::PendingRemote<mojom::Unzipper>()>;
+}  // namespace unzip
+
 namespace optimization_guide {
 
 class PredictionModelDownloadClient;
@@ -59,6 +71,7 @@
       download::BackgroundDownloadService* download_service,
       GetBaseModelDirForDownloadCallback
           get_base_model_dir_for_download_callback,
+      unzip::UnzipperFactory unzipper_factory,
       scoped_refptr<base::SequencedTaskRunner> background_task_runner);
   virtual ~PredictionModelDownloadManager();
   PredictionModelDownloadManager(const PredictionModelDownloadManager&) =
@@ -185,6 +198,9 @@
   // Callback to get the directory to download models.
   GetBaseModelDirForDownloadCallback get_base_model_dir_for_download_callback_;
 
+  // How to get an unzipper remote.
+  unzip::UnzipperFactory unzipper_factory_;
+
   // Background thread where download file processing should be performed.
   scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
 
diff --git a/components/optimization_guide/core/prediction_model_download_manager_unittest.cc b/components/optimization_guide/core/prediction_model_download_manager_unittest.cc
index 1bb2a7b..54b0aab 100644
--- a/components/optimization_guide/core/prediction_model_download_manager_unittest.cc
+++ b/components/optimization_guide/core/prediction_model_download_manager_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/functional/bind.h"
 #include "base/path_service.h"
 #include "base/sequence_checker.h"
 #include "base/strings/utf_string_conversions.h"
@@ -29,10 +30,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/zlib/google/zip.h"
 
-#if !BUILDFLAG(IS_IOS)
-#include "components/services/unzip/content/unzip_service.h"  // nogncheck
-#endif
-
 namespace optimization_guide {
 
 using ::testing::_;
@@ -92,12 +89,8 @@
                   base::Uuid::GenerateRandomV4().AsLowercaseString());
             },
             temp_models_dir_.GetPath()),
+        base::BindRepeating(&unzip::LaunchInProcessUnzipper),
         task_environment_.GetMainThreadTaskRunner());
-
-#if !BUILDFLAG(IS_IOS)
-    unzip::SetUnzipperLaunchOverrideForTesting(
-        base::BindRepeating(&unzip::LaunchInProcessUnzipper));
-#endif
   }
 
   void TearDown() override {
diff --git a/components/optimization_guide/core/prediction_model_override.cc b/components/optimization_guide/core/prediction_model_override.cc
index 34dbe07..e534ce5 100644
--- a/components/optimization_guide/core/prediction_model_override.cc
+++ b/components/optimization_guide/core/prediction_model_override.cc
@@ -21,12 +21,7 @@
 #include "components/optimization_guide/core/prediction_model_download_manager.h"
 #include "components/optimization_guide/proto/models.pb.h"
 #include "components/services/unzip/public/cpp/unzip.h"
-
-#if BUILDFLAG(IS_IOS)
-#include "components/services/unzip/in_process_unzipper.h"  // nogncheck
-#else
-#include "components/services/unzip/content/unzip_service.h"  // nogncheck
-#endif
+#include "components/services/unzip/public/mojom/unzipper.mojom.h"
 
 namespace optimization_guide {
 
@@ -113,6 +108,7 @@
 void OnModelOverrideVerified(proto::OptimizationTarget optimization_target,
                              const base::FilePath& passed_crx_file_path,
                              const base::FilePath& base_model_dir,
+                             unzip::UnzipperFactory unzipper_factory,
                              BuiltCallback callback,
                              bool is_verify_success) {
   if (!is_verify_success) {
@@ -121,12 +117,7 @@
     return;
   }
 
-#if BUILDFLAG(IS_IOS)
-  auto unzipper = unzip::LaunchInProcessUnzipper();
-#else
-  auto unzipper = unzip::LaunchUnzipper();
-#endif
-  unzip::Unzip(std::move(unzipper), passed_crx_file_path, base_model_dir,
+  unzip::Unzip(unzipper_factory.Run(), passed_crx_file_path, base_model_dir,
                unzip::mojom::UnzipOptions::New(), unzip::AllContents(),
                base::DoNothing(),
                base::BindOnce(&OnModelOverrideUnzipped, optimization_target,
@@ -192,6 +183,7 @@
 
 void PredictionModelOverrides::Entry::BuildModel(
     const base::FilePath& base_model_dir,
+    unzip::UnzipperFactory unzipper_factory,
     PredictionModelOverrides::Entry::BuiltCallback callback) const {
   if (path_.MatchesFinalExtension(FILE_PATH_LITERAL(".crx3"))) {
     DVLOG(0) << "Attempting to parse the model override at " << path_.value()
@@ -208,7 +200,7 @@
                        base_model_dir,
                        /*delete_file_on_error=*/false),
         base::BindOnce(&OnModelOverrideVerified, target_, path_, base_model_dir,
-                       std::move(callback)));
+                       std::move(unzipper_factory), std::move(callback)));
     return;
   }
 
diff --git a/components/optimization_guide/core/prediction_model_override.h b/components/optimization_guide/core/prediction_model_override.h
index ddfc2e2..01826fe 100644
--- a/components/optimization_guide/core/prediction_model_override.h
+++ b/components/optimization_guide/core/prediction_model_override.h
@@ -15,11 +15,22 @@
 #include "base/files/file_path.h"
 #include "base/functional/callback.h"
 #include "components/optimization_guide/proto/models.pb.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 
 namespace base {
 class FilePath;
 }  // namespace base
 
+namespace unzip::mojom {
+class Unzipper;
+}  // namespace unzip::mojom
+
+namespace unzip {
+// TODO: crbug.com/421262905 - Avoid duplicating this alias.
+using UnzipperFactory =
+    base::RepeatingCallback<mojo::PendingRemote<mojom::Unzipper>()>;
+}  // namespace unzip
+
 namespace optimization_guide {
 
 // Return the separator used in the model override switch.
@@ -42,6 +53,7 @@
     // Returns the result (or nullptr if there was an error) via `callback`.
     // In the event of an error, check LOG(ERROR).
     void BuildModel(const base::FilePath& base_model_dir,
+                    unzip::UnzipperFactory unzipper_factory,
                     BuiltCallback callback) const;
 
     proto::OptimizationTarget target() const { return target_; }
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal
index 628b2ac..f1e5abb 160000
--- a/components/optimization_guide/internal
+++ b/components/optimization_guide/internal
@@ -1 +1 @@
-Subproject commit 628b2acf00d0de13413f41d29f044cae2ee865be
+Subproject commit f1e5abb6f8c93c4eb6ba9a48bdc40cd4b9579779
diff --git a/components/optimization_guide/proto/models.proto b/components/optimization_guide/proto/models.proto
index cc63ec4..cbb8964e 100644
--- a/components/optimization_guide/proto/models.proto
+++ b/components/optimization_guide/proto/models.proto
@@ -252,6 +252,8 @@
   OPTIMIZATION_TARGET_NOTIFICATION_IMAGE_PERMISSION_RELEVANCE = 61;
   // Target for Proofreader API.
   OPTIMIZATION_TARGET_MODEL_EXECUTION_FEATURE_PROOFREADER_API = 62;
+  // Target to determine whether to show the iOS default browser promo.
+  OPTIMIZATION_TARGET_SEGMENTATION_IOS_DEFAULT_BROWSER_PROMO = 63;
 }
 
 // The model engine versions that can be used to do model inference.
diff --git a/components/origin_matcher/BUILD.gn b/components/origin_matcher/BUILD.gn
index 59cef80..71ecb28 100644
--- a/components/origin_matcher/BUILD.gn
+++ b/components/origin_matcher/BUILD.gn
@@ -51,10 +51,7 @@
   ]
 
   if (is_android) {
-    sources += [
-      "android/origin_matcher_bindings.cc",
-      "android/origin_matcher_bindings.h",
-    ]
+    sources += [ "android/origin_matcher_bindings.cc" ]
 
     deps += [ "//components/origin_matcher/android:jni" ]
   }
diff --git a/components/origin_matcher/android/origin_matcher_bindings.cc b/components/origin_matcher/android/origin_matcher_bindings.cc
index 554df44f..67012863f 100644
--- a/components/origin_matcher/android/origin_matcher_bindings.cc
+++ b/components/origin_matcher/android/origin_matcher_bindings.cc
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/origin_matcher/android/origin_matcher_bindings.h"
-
 #include "base/android/jni_android.h"
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
diff --git a/components/origin_matcher/android/origin_matcher_bindings.h b/components/origin_matcher/android/origin_matcher_bindings.h
deleted file mode 100644
index 6c5bb8e..0000000
--- a/components/origin_matcher/android/origin_matcher_bindings.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2025 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_ORIGIN_MATCHER_ANDROID_ORIGIN_MATCHER_BINDINGS_H_
-#define COMPONENTS_ORIGIN_MATCHER_ANDROID_ORIGIN_MATCHER_BINDINGS_H_
-
-#include "base/android/jni_android.h"
-#include "base/android/scoped_java_ref.h"
-#include "components/origin_matcher/origin_matcher.h"
-
-namespace origin_matcher {
-OriginMatcher ToNativeOriginMatcher(JNIEnv* env,
-                                    const jni_zero::JavaRef<jobject>& jobject);
-}  // namespace origin_matcher
-
-namespace jni_zero {
-template <>
-inline origin_matcher::OriginMatcher FromJniType(
-    JNIEnv* env,
-    const base::android::JavaRef<jobject>& obj) {
-  return origin_matcher::ToNativeOriginMatcher(env, obj);
-}
-}  // namespace jni_zero
-
-#endif  // COMPONENTS_ORIGIN_MATCHER_ANDROID_ORIGIN_MATCHER_BINDINGS_H_
diff --git a/components/origin_matcher/origin_matcher.h b/components/origin_matcher/origin_matcher.h
index 43f37c0e..85e5ed6 100644
--- a/components/origin_matcher/origin_matcher.h
+++ b/components/origin_matcher/origin_matcher.h
@@ -9,6 +9,13 @@
 #include <string>
 #include <vector>
 
+#include "build/build_config.h"
+
+#if BUILDFLAG(IS_ANDROID)
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#endif  // BUILDFLAG(IS_ANDROID)
+
 namespace url {
 class Origin;
 }  // namespace url
@@ -73,6 +80,24 @@
   RuleList rules_;
 };
 
+#if BUILDFLAG(IS_ANDROID)
+OriginMatcher ToNativeOriginMatcher(JNIEnv* env,
+                                    const jni_zero::JavaRef<jobject>& jobject);
+#endif  // BUILDFLAG(IS_ANDROID)
+
 }  // namespace origin_matcher
 
+#if BUILDFLAG(IS_ANDROID)
+namespace jni_zero {
+template <>
+inline origin_matcher::OriginMatcher FromJniType(
+    JNIEnv* env,
+    const base::android::JavaRef<jobject>& obj) {
+  // This creates a copy of the OriginMatcher in native because the Java object
+  // needs to manage the lifetime of the native instance.
+  return origin_matcher::ToNativeOriginMatcher(env, obj);
+}
+}  // namespace jni_zero
+#endif  // BUILDFLAG(IS_ANDROID)
+
 #endif  // COMPONENTS_ORIGIN_MATCHER_ORIGIN_MATCHER_H_
diff --git a/components/payments/content/android/BUILD.gn b/components/payments/content/android/BUILD.gn
index ae0f643..0482310 100644
--- a/components/payments/content/android/BUILD.gn
+++ b/components/payments/content/android/BUILD.gn
@@ -127,6 +127,8 @@
     "java/src/org/chromium/components/payments/PaymentRequestUpdateEventListener.java",
     "java/src/org/chromium/components/payments/intent/WebPaymentIntentHelper.java",
     "java/src/org/chromium/components/payments/intent/WebPaymentIntentHelperType.java",
+    "java/src/org/chromium/components/payments/service/Reconnectable.java",
+    "java/src/org/chromium/components/payments/service/ServiceReconnector.java",
   ]
   deps = [
     ":feature_list_java",
@@ -355,6 +357,7 @@
     "junit/src/org/chromium/components/payments/PaymentManifestResolverTest.java",
     "junit/src/org/chromium/components/payments/PaymentRequestServiceTest.java",
     "junit/src/org/chromium/components/payments/intent/WebPaymentIntentHelperTypeConverterTest.java",
+    "junit/src/org/chromium/components/payments/service/ServiceReconnectorUnitTest.java",
   ]
   deps = [
     ":java",
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/AndroidPaymentApp.java b/components/payments/content/android/java/src/org/chromium/components/payments/AndroidPaymentApp.java
index f7ae74c..0b4862d 100644
--- a/components/payments/content/android/java/src/org/chromium/components/payments/AndroidPaymentApp.java
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/AndroidPaymentApp.java
@@ -56,6 +56,7 @@
     private final SupportedDelegations mSupportedDelegations;
     private final boolean mShowReadyToPayDebugInfo;
     private final boolean mRemoveDeprecatedFields;
+    private final int mPaymentDetailsUpdateServiceMaxRetryNumber;
 
     private @Nullable IsReadyToPayCallback mIsReadyToPayCallback;
     private @Nullable InstrumentDetailsCallback mInstrumentDetailsCallback;
@@ -89,6 +90,9 @@
      * @param showReadyToPayDebugInfo Whether IS_READY_TO_PAY intent should be displayed in a debug
      *     dialog.
      * @param removeDeprecatedFields Whether intents should omit deprecated fields.
+     * @param paymentDetailsUpdateServiceMaxRetryNumber The maximum number of times to attempt to
+     *     reconnect to the UPDATE_PAYMENT_DETAILS service in the payment app, if it unexpectedly
+     *     disconnects during payment.
      */
     public AndroidPaymentApp(
             AndroidIntentLauncher launcher,
@@ -103,7 +107,8 @@
             @Nullable String appToHide,
             SupportedDelegations supportedDelegations,
             boolean showReadyToPayDebugInfo,
-            boolean removeDeprecatedFields) {
+            boolean removeDeprecatedFields,
+            int paymentDetailsUpdateServiceMaxRetryNumber) {
         super(packageName, label, null, icon);
         ThreadUtils.assertOnUiThread();
         mHandler = new Handler();
@@ -125,6 +130,7 @@
         mSupportedDelegations = supportedDelegations;
         mShowReadyToPayDebugInfo = showReadyToPayDebugInfo;
         mRemoveDeprecatedFields = removeDeprecatedFields;
+        mPaymentDetailsUpdateServiceMaxRetryNumber = paymentDetailsUpdateServiceMaxRetryNumber;
         mIsPreferred = false;
     }
 
@@ -395,7 +401,8 @@
                                             .getPackageName(),
                                     mPackageName,
                                     mPaymentDetailsUpdateServiceName),
-                            new PaymentDetailsUpdateService().getBinder());
+                            new PaymentDetailsUpdateService().getBinder(),
+                            mPaymentDetailsUpdateServiceMaxRetryNumber);
             mPaymentDetailsUpdateConnection.connectToService();
         }
     }
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/AndroidPaymentAppFinder.java b/components/payments/content/android/java/src/org/chromium/components/payments/AndroidPaymentAppFinder.java
index 7552348..3559067 100644
--- a/components/payments/content/android/java/src/org/chromium/components/payments/AndroidPaymentAppFinder.java
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/AndroidPaymentAppFinder.java
@@ -51,6 +51,8 @@
     /** The maximum number of payment method manifests to download. */
     private static final int MAX_NUMBER_OF_MANIFESTS = 10;
 
+    private static final int DEFAULT_PAYMENT_DETAILS_UPDATE_SERVICE_MAX_RETRY_NUMBER = 5;
+
     /** Meta data name of an app's supported payment method names. */
     public static final String META_DATA_NAME_OF_PAYMENT_METHOD_NAMES =
             "org.chromium.payment_method_names";
@@ -153,6 +155,8 @@
      */
     private final Map<String, String> mUpdatePaymentDetailsServices = new HashMap<>();
 
+    private final int mPaymentDetailsUpdateServiceMaxRetryNumber;
+
     private int mPendingVerifiersCount;
     private int mPendingIsReadyToPayQueries;
     private int mPendingResourceUsersCount;
@@ -231,6 +235,13 @@
         mFactory = factory;
         assert mFactoryDelegate.getParams() != null;
         mIsOffTheRecord = mFactoryDelegate.getParams().isOffTheRecord();
+
+        mPaymentDetailsUpdateServiceMaxRetryNumber =
+                PaymentFeatureList.isEnabledOrExperimentalFeaturesEnabled(
+                                PaymentFeatureList
+                                        .RECONNECT_ON_LOST_CONNECTION_TO_UPDATE_PAYMENT_DETAILS_SERVICE)
+                        ? DEFAULT_PAYMENT_DETAILS_UPDATE_SERVICE_MAX_RETRY_NUMBER
+                        : 0; // No reconnect attempts.
     }
 
     private void findAppStoreBillingApp(List<ResolveInfo> allInstalledPaymentApps) {
@@ -867,7 +878,8 @@
                             /* removeDeprecatedFields= */ PaymentFeatureList
                                     .isEnabledOrExperimentalFeaturesEnabled(
                                             PaymentFeatureList
-                                                    .ANDROID_PAYMENT_INTENTS_OMIT_DEPRECATED_PARAMETERS));
+                                                    .ANDROID_PAYMENT_INTENTS_OMIT_DEPRECATED_PARAMETERS),
+                            mPaymentDetailsUpdateServiceMaxRetryNumber);
             mValidApps.put(packageName, app);
         }
 
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentDetailsUpdateConnection.java b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentDetailsUpdateConnection.java
index 236ca927..c05f90d 100644
--- a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentDetailsUpdateConnection.java
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentDetailsUpdateConnection.java
@@ -8,10 +8,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.os.Handler;
 import android.os.IBinder;
 
 import org.chromium.base.Log;
 import org.chromium.build.annotations.NullMarked;
+import org.chromium.components.payments.service.Reconnectable;
+import org.chromium.components.payments.service.ServiceReconnector;
 
 /**
  * A helper to connect to the payment app's service that dynamically updates payment details (e.g.,
@@ -19,12 +22,13 @@
  * connection stays open until terminated.
  */
 @NullMarked
-public class PaymentDetailsUpdateConnection implements ServiceConnection {
+public class PaymentDetailsUpdateConnection implements ServiceConnection, Reconnectable {
     private static final String TAG = "PaymentDetailUpdate";
     private final Context mContext;
     private final Intent mPaymentAppServiceIntent;
     private final IPaymentDetailsUpdateService.Stub mChromiumService;
     private final String mServiceName;
+    private final ServiceReconnector mServiceReconnector;
     private boolean mIsBindingInitiated;
 
     /**
@@ -36,11 +40,14 @@
      *     null.
      * @param chromiumService The service in Chromium that receives the updates to user's payment
      *     method, shipping address, and shipping option. Should not be null.
+     * @param maxRetryNumber The maximum number of times to attempt to reconnect in case of an
+     *     unexpected disconnect.
      */
     public PaymentDetailsUpdateConnection(
             Context context,
             Intent paymentAppServiceIntent,
-            IPaymentDetailsUpdateService.Stub chromiumService) {
+            IPaymentDetailsUpdateService.Stub chromiumService,
+            int maxRetryNumber) {
         assert context != null;
         assert paymentAppServiceIntent != null;
         assert chromiumService != null;
@@ -51,9 +58,11 @@
                 mPaymentAppServiceIntent != null && mPaymentAppServiceIntent.getComponent() != null
                         ? mPaymentAppServiceIntent.getComponent().getClassName()
                         : "";
+        mServiceReconnector = new ServiceReconnector(this, maxRetryNumber, new Handler());
     }
 
-    /** Connect to the service. */
+    // Reconnectable:
+    @Override
     public void connectToService() {
         mIsBindingInitiated = true;
         Log.i(TAG, "Connecting to \"%s\".", mServiceName);
@@ -96,7 +105,7 @@
             return;
         }
 
-        Log.i(TAG, "Sending payment details upate service to \"%s\".", mServiceName);
+        Log.i(TAG, "Sending payment details update service to \"%s\".", mServiceName);
         try {
             paymentAppService.setPaymentDetailsUpdateService(mChromiumService);
         } catch (Throwable e) {
@@ -116,8 +125,8 @@
         // receive a call to onServiceConnected(ComponentName, IBinder) when the Service is next
         // running."
         // https://developer.android.com/reference/android/content/ServiceConnection#onServiceDisconnected(android.content.ComponentName)
-        Log.i(TAG, "Service \"%s\" disconnected.", mServiceName);
-        terminateConnection();
+        Log.i(TAG, "\"%s\" disconnected.", mServiceName);
+        mServiceReconnector.onUnexpectedServiceDisconnect();
     }
 
     // ServiceConnection implementation:
@@ -140,16 +149,24 @@
         // with this ServiceConnection even if this callback was invoked following
         // Context.bindService() bindService()."
         // https://developer.android.com/reference/android/content/ServiceConnection#onBindingDied(android.content.ComponentName)
-        Log.e(TAG, "Service \"%s\" binding died.", mServiceName);
-        terminateConnection();
+        Log.e(TAG, "\"%s\" binding died.", mServiceName);
+        mServiceReconnector.onUnexpectedServiceDisconnect();
     }
 
-    /** Disconnect from the service, if still connected, and release the tracking resources. */
+    // Reconnectable:
+    @Override
     public void terminateConnection() {
+        mServiceReconnector.onIntentionalServiceDisconnect();
         if (mIsBindingInitiated) {
-            Log.i(TAG, "Terminating connection to service \"%s\".", mServiceName);
-            mContext.unbindService(/* serviceConnection= */ this);
+            Log.i(TAG, "Terminating connection to \"%s\".", mServiceName);
+            unbindService();
             mIsBindingInitiated = false;
         }
     }
+
+    // Reconnectable:
+    @Override
+    public void unbindService() {
+        mContext.unbindService(/* serviceConnection= */ this);
+    }
 }
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentFeatureList.java b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentFeatureList.java
index 779aa0d..f1fc6a8 100644
--- a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentFeatureList.java
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentFeatureList.java
@@ -25,6 +25,8 @@
     public static final String ENFORCE_FULL_DELEGATION = "EnforceFullDelegation";
     public static final String GOOGLE_PAY_VIA_ANDROID_INTENTS = "GooglePayViaAndroidIntents";
     public static final String OMIT_PARAMETERS_IN_READY_TO_PAY = "OmitParametersInReadyToPay";
+    public static final String RECONNECT_ON_LOST_CONNECTION_TO_UPDATE_PAYMENT_DETAILS_SERVICE =
+            "ReconnectOnLostConnectionToUpdatePaymentDetailsService";
     public static final String SERVICE_WORKER_PAYMENT_APPS = "ServiceWorkerPaymentApps";
     public static final String SHOW_READY_TO_PAY_DEBUG_INFO = "ShowReadyToPayDebugInfo";
     public static final String UPDATE_PAYMENT_DETAILS_INTENT_FILTER_IN_PAYMENT_APP =
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/service/Reconnectable.java b/components/payments/content/android/java/src/org/chromium/components/payments/service/Reconnectable.java
new file mode 100644
index 0000000..cdfc651
--- /dev/null
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/service/Reconnectable.java
@@ -0,0 +1,20 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.payments.service;
+
+import org.chromium.build.annotations.NullMarked;
+
+/** The interface for controlling the connection state of a service. */
+@NullMarked
+public interface Reconnectable {
+    /** Connects to the remote service. */
+    void connectToService();
+
+    /** Terminates the connection to the remote service. */
+    void terminateConnection();
+
+    /** Cleans up the system resources that were used for the connection to the service. */
+    void unbindService();
+}
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/service/ServiceReconnector.java b/components/payments/content/android/java/src/org/chromium/components/payments/service/ServiceReconnector.java
new file mode 100644
index 0000000..0677c45
--- /dev/null
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/service/ServiceReconnector.java
@@ -0,0 +1,63 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.payments.service;
+
+import android.os.Handler;
+
+import org.chromium.base.Log;
+import org.chromium.build.annotations.NullMarked;
+
+/**
+ * A helper class for reconnecting to the remote service in the case of unexpected connection
+ * failure. Uses exponential back off.
+ */
+@NullMarked
+public class ServiceReconnector {
+    private static final String TAG = "PayServiceReconnect";
+    private static final int RETRY_DELAY_MILLISECONDS = 1000;
+    private static final int EXPONENTIAL_BACKOFF_FACTOR = 2;
+    private final Reconnectable mConnection;
+    private final int mMaxRetryNumber;
+    private final Handler mHandler;
+    private int mRetryNumber;
+
+    /**
+     * Creates a service reconnector.
+     *
+     * @param connection The service connection to attempt to reconnect after an unexpected
+     *     disconnect.
+     * @param maxRetryNumber The maximum number of times that a reconnection should be attempted
+     *     after unexpected disconnects.
+     * @param handler The handler for posting tasks to run after a delay.
+     */
+    public ServiceReconnector(Reconnectable connection, int maxRetryNumber, Handler handler) {
+        mConnection = connection;
+        mMaxRetryNumber = maxRetryNumber;
+        mHandler = handler;
+    }
+
+    /** Handle unexpected service disconnect. */
+    public void onUnexpectedServiceDisconnect() {
+        if (mRetryNumber >= mMaxRetryNumber) {
+            Log.i(TAG, "Max reconnects reached.");
+            mConnection.terminateConnection();
+            return;
+        }
+
+        int delayMilliseconds =
+                RETRY_DELAY_MILLISECONDS
+                        * (int) Math.pow(EXPONENTIAL_BACKOFF_FACTOR, mRetryNumber++);
+        Log.i(TAG, "%d: will reconnect in %d ms.", mRetryNumber, delayMilliseconds);
+
+        mConnection.unbindService();
+        mHandler.postDelayed(mConnection::connectToService, delayMilliseconds);
+    }
+
+    /** Handle intentional service disconnect. */
+    public void onIntentionalServiceDisconnect() {
+        mRetryNumber = mMaxRetryNumber;
+        mHandler.removeCallbacksAndMessages(null);
+    }
+}
diff --git a/components/payments/content/android/javatests/src/org/chromium/components/payments/AndroidPaymentAppUnitTest.java b/components/payments/content/android/javatests/src/org/chromium/components/payments/AndroidPaymentAppUnitTest.java
index 0530c29..0a32e0d 100644
--- a/components/payments/content/android/javatests/src/org/chromium/components/payments/AndroidPaymentAppUnitTest.java
+++ b/components/payments/content/android/javatests/src/org/chromium/components/payments/AndroidPaymentAppUnitTest.java
@@ -135,7 +135,8 @@
                         /* appToHide= */ null,
                         new SupportedDelegations(),
                         showReadyToPayDebugInfo,
-                        /* removeDeprecatedFields= */ false);
+                        /* removeDeprecatedFields= */ false,
+                        /* paymentDetailsUpdateServiceMaxRetryNumber= */ 0);
         app.addMethodName("https://company.com/pay");
         return app;
     }
diff --git a/components/payments/content/android/junit/src/org/chromium/components/payments/PaymentDetailsUpdateConnectionTest.java b/components/payments/content/android/junit/src/org/chromium/components/payments/PaymentDetailsUpdateConnectionTest.java
index ba9c321..68c43b5 100644
--- a/components/payments/content/android/junit/src/org/chromium/components/payments/PaymentDetailsUpdateConnectionTest.java
+++ b/components/payments/content/android/junit/src/org/chromium/components/payments/PaymentDetailsUpdateConnectionTest.java
@@ -30,6 +30,8 @@
 /** Tests for PaymentDetailsUpdateConnection. */
 @RunWith(BaseRobolectricTestRunner.class)
 public class PaymentDetailsUpdateConnectionTest {
+    private static final int MAX_RETRY_NUMBER = 5;
+
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
 
     @Mock private IBinder mPaymentAppServiceBinder;
@@ -42,7 +44,8 @@
     public void testConnectionBindsToService() throws Throwable {
         Intent intent = new Intent();
         PaymentDetailsUpdateConnection connection =
-                new PaymentDetailsUpdateConnection(mContext, intent, mBrowserService);
+                new PaymentDetailsUpdateConnection(
+                        mContext, intent, mBrowserService, MAX_RETRY_NUMBER);
 
         connection.connectToService();
 
@@ -54,15 +57,17 @@
     public void testConnectionSetsUpTheService() throws Throwable {
         Intent intent = new Intent();
         PaymentDetailsUpdateConnection connection =
-                new PaymentDetailsUpdateConnection(mContext, intent, mBrowserService);
+                new PaymentDetailsUpdateConnection(
+                        mContext, intent, mBrowserService, MAX_RETRY_NUMBER);
         doReturn(mPaymentAppService).when(mPaymentAppServiceBinder).queryLocalInterface(any());
-        doAnswer((invocation) -> {
-            ((ServiceConnection) invocation.getArgument(1))
-                    .onServiceConnected(
-                            /* name= */ null,
-                            /* service= */ mPaymentAppServiceBinder);
-            return true;
-        })
+        doAnswer(
+                        (invocation) -> {
+                            ((ServiceConnection) invocation.getArgument(1))
+                                    .onServiceConnected(
+                                            /* name= */ null,
+                                            /* service= */ mPaymentAppServiceBinder);
+                            return true;
+                        })
                 .when(mContext)
                 .bindService(eq(intent), eq(connection), eq(Context.BIND_AUTO_CREATE));
 
@@ -76,8 +81,13 @@
     public void testNoPermissionToConnectUnbindsService() throws Throwable {
         Intent intent = new Intent();
         PaymentDetailsUpdateConnection connection =
-                new PaymentDetailsUpdateConnection(mContext, intent, mBrowserService);
-        doAnswer(answerVoid((invocation) -> { throw new SecurityException("Permission denied"); }))
+                new PaymentDetailsUpdateConnection(
+                        mContext, intent, mBrowserService, MAX_RETRY_NUMBER);
+        doAnswer(
+                        answerVoid(
+                                (invocation) -> {
+                                    throw new SecurityException("Permission denied");
+                                }))
                 .when(mContext)
                 .bindService(eq(intent), eq(connection), eq(Context.BIND_AUTO_CREATE));
 
@@ -91,12 +101,14 @@
     public void testNullServiceUnbindsService() throws Throwable {
         Intent intent = new Intent();
         PaymentDetailsUpdateConnection connection =
-                new PaymentDetailsUpdateConnection(mContext, intent, mBrowserService);
-        doAnswer((invocation) -> {
-            ((ServiceConnection) invocation.getArgument(1))
-                    .onServiceConnected(/* name= */ null, /* service= */ null);
-            return true;
-        })
+                new PaymentDetailsUpdateConnection(
+                        mContext, intent, mBrowserService, MAX_RETRY_NUMBER);
+        doAnswer(
+                        (invocation) -> {
+                            ((ServiceConnection) invocation.getArgument(1))
+                                    .onServiceConnected(/* name= */ null, /* service= */ null);
+                            return true;
+                        })
                 .when(mContext)
                 .bindService(eq(intent), eq(connection), eq(Context.BIND_AUTO_CREATE));
 
@@ -110,15 +122,17 @@
     public void testErrorsInPaymentAppsServiceUnbindsService() throws Throwable {
         Intent intent = new Intent();
         PaymentDetailsUpdateConnection connection =
-                new PaymentDetailsUpdateConnection(mContext, intent, mBrowserService);
+                new PaymentDetailsUpdateConnection(
+                        mContext, intent, mBrowserService, MAX_RETRY_NUMBER);
         doReturn(mPaymentAppService).when(mPaymentAppServiceBinder).queryLocalInterface(any());
-        doAnswer((invocation) -> {
-            ((ServiceConnection) invocation.getArgument(1))
-                    .onServiceConnected(
-                            /* name= */ null,
-                            /* service= */ mPaymentAppServiceBinder);
-            return true;
-        })
+        doAnswer(
+                        (invocation) -> {
+                            ((ServiceConnection) invocation.getArgument(1))
+                                    .onServiceConnected(
+                                            /* name= */ null,
+                                            /* service= */ mPaymentAppServiceBinder);
+                            return true;
+                        })
                 .when(mContext)
                 .bindService(eq(intent), eq(connection), eq(Context.BIND_AUTO_CREATE));
         doAnswer((invocation) -> { throw new Exception("Internal error"); })
@@ -134,7 +148,8 @@
     @Feature({"Payments"})
     public void testServiceDisconnectUnbindsService() throws Throwable {
         PaymentDetailsUpdateConnection connection =
-                new PaymentDetailsUpdateConnection(mContext, new Intent(), mBrowserService);
+                new PaymentDetailsUpdateConnection(
+                        mContext, new Intent(), mBrowserService, MAX_RETRY_NUMBER);
         connection.connectToService();
 
         connection.onServiceDisconnected(/*name=*/null);
@@ -146,7 +161,8 @@
     @Feature({"Payments"})
     public void testNullBindingUnbindsService() throws Throwable {
         PaymentDetailsUpdateConnection connection =
-                new PaymentDetailsUpdateConnection(mContext, new Intent(), mBrowserService);
+                new PaymentDetailsUpdateConnection(
+                        mContext, new Intent(), mBrowserService, MAX_RETRY_NUMBER);
         connection.connectToService();
 
         connection.onNullBinding(/*name=*/null);
@@ -158,7 +174,8 @@
     @Feature({"Payments"})
     public void testDeadBindingUnbindsService() throws Throwable {
         PaymentDetailsUpdateConnection connection =
-                new PaymentDetailsUpdateConnection(mContext, new Intent(), mBrowserService);
+                new PaymentDetailsUpdateConnection(
+                        mContext, new Intent(), mBrowserService, MAX_RETRY_NUMBER);
         connection.connectToService();
 
         connection.onBindingDied(/*name=*/null);
@@ -170,7 +187,8 @@
     @Feature({"Payments"})
     public void testTerminateConnectionUnbindsService() throws Throwable {
         PaymentDetailsUpdateConnection connection =
-                new PaymentDetailsUpdateConnection(mContext, new Intent(), mBrowserService);
+                new PaymentDetailsUpdateConnection(
+                        mContext, new Intent(), mBrowserService, MAX_RETRY_NUMBER);
         connection.connectToService();
 
         connection.terminateConnection();
@@ -182,7 +200,8 @@
     @Feature({"Payments"})
     public void testTerminateConnectionNoOpWhenNotConnected() {
         PaymentDetailsUpdateConnection connection =
-                new PaymentDetailsUpdateConnection(mContext, new Intent(), mBrowserService);
+                new PaymentDetailsUpdateConnection(
+                        mContext, new Intent(), mBrowserService, MAX_RETRY_NUMBER);
 
         connection.terminateConnection();
 
diff --git a/components/payments/content/android/junit/src/org/chromium/components/payments/service/ServiceReconnectorUnitTest.java b/components/payments/content/android/junit/src/org/chromium/components/payments/service/ServiceReconnectorUnitTest.java
new file mode 100644
index 0000000..14cceec
--- /dev/null
+++ b/components/payments/content/android/junit/src/org/chromium/components/payments/service/ServiceReconnectorUnitTest.java
@@ -0,0 +1,95 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.payments.service;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+
+import android.os.Handler;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Feature;
+
+/** Tests for the service reconnector behavior. */
+@RunWith(BaseRobolectricTestRunner.class)
+public class ServiceReconnectorUnitTest {
+    @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+    @Mock private Reconnectable mConnection;
+    @Mock private Handler mHandler;
+
+    @Test
+    @Feature({"Payments"})
+    public void testTerminateConnectionWhenMaxReconnectsIsZero() throws Exception {
+        ServiceReconnector reconnector =
+                new ServiceReconnector(mConnection, /* maxRetryNumber= */ 0, mHandler);
+
+        reconnector.onUnexpectedServiceDisconnect();
+
+        Mockito.verify(mConnection).terminateConnection();
+    }
+
+    @Test
+    @Feature({"Payments"})
+    public void testUnbindServiceBeforeReconnect() throws Exception {
+        ServiceReconnector reconnector =
+                new ServiceReconnector(mConnection, /* maxRetryNumber= */ 1, mHandler);
+
+        reconnector.onUnexpectedServiceDisconnect();
+
+        Mockito.verify(mConnection).unbindService();
+        // First reconnect delay = 1 second.
+        Mockito.verify(mHandler).postDelayed(any(), eq(1000L));
+    }
+
+    @Test
+    @Feature({"Payments"})
+    public void testIntentionalDisconnectPreventsReconnects() throws Exception {
+        ServiceReconnector reconnector =
+                new ServiceReconnector(mConnection, /* maxRetryNumber= */ 999, mHandler);
+
+        reconnector.onIntentionalServiceDisconnect();
+
+        Mockito.verify(mHandler).removeCallbacksAndMessages(eq(null));
+
+        reconnector.onUnexpectedServiceDisconnect();
+
+        Mockito.verify(mConnection).terminateConnection();
+    }
+
+    @Test
+    @Feature({"Payments"})
+    public void testThreeReconnectAttempts() throws Exception {
+        ServiceReconnector reconnector =
+                new ServiceReconnector(mConnection, /* maxRetryNumber= */ 3, mHandler);
+
+        reconnector.onUnexpectedServiceDisconnect();
+
+        // First reconnect delay = 1 second.
+        Mockito.verify(mHandler).postDelayed(any(), eq(1000L));
+
+        reconnector.onUnexpectedServiceDisconnect();
+
+        // Second reconnect delay = 2 seconds.
+        Mockito.verify(mHandler).postDelayed(any(), eq(2000L));
+
+        reconnector.onUnexpectedServiceDisconnect();
+
+        // Third reconnect delay = 4 seconds.
+        Mockito.verify(mHandler).postDelayed(any(), eq(4000L));
+
+        reconnector.onUnexpectedServiceDisconnect();
+
+        // Give up reconnecting after 3 attempts.
+        Mockito.verify(mConnection).terminateConnection();
+    }
+}
diff --git a/components/payments/content/android/payment_feature_map.cc b/components/payments/content/android/payment_feature_map.cc
index 5d99c1c..0c6dd5e2 100644
--- a/components/payments/content/android/payment_feature_map.cc
+++ b/components/payments/content/android/payment_feature_map.cc
@@ -38,6 +38,7 @@
     &kAndroidPaymentIntentsOmitDeprecatedParameters,
     &kGooglePayViaAndroidIntents,
     &kOmitParametersInReadyToPay,
+    &kReconnectOnLostConnectionToUpdatePaymentDetailsService,
     &kShowReadyToPayDebugInfo,
     &kUpdatePaymentDetailsIntentFilterInPaymentApp,
 };
@@ -65,6 +66,9 @@
 BASE_FEATURE(kOmitParametersInReadyToPay,
              "OmitParametersInReadyToPay",
              base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kReconnectOnLostConnectionToUpdatePaymentDetailsService,
+             "ReconnectOnLostConnectionToUpdatePaymentDetailsService",
+             base::FEATURE_ENABLED_BY_DEFAULT);
 BASE_FEATURE(kShowReadyToPayDebugInfo,
              "ShowReadyToPayDebugInfo",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/components/payments/content/android/payment_feature_map.h b/components/payments/content/android/payment_feature_map.h
index 06f38b6a..a2d7ca4f 100644
--- a/components/payments/content/android/payment_feature_map.h
+++ b/components/payments/content/android/payment_feature_map.h
@@ -25,6 +25,10 @@
 // omitted from the isReadyToPayRequest. See: https://crbug.com/1406655.
 BASE_DECLARE_FEATURE(kOmitParametersInReadyToPay);
 
+// If enabled, the payment details update service reconnects to the Android
+// payment app when the connection is lost unexpectedly.
+BASE_DECLARE_FEATURE(kReconnectOnLostConnectionToUpdatePaymentDetailsService);
+
 // If enabled, then Clank displays an alert dialog with the content of the
 // IS_READY_TO_PAY intent, whenever Clank fires this intent.
 BASE_DECLARE_FEATURE(kShowReadyToPayDebugInfo);
diff --git a/components/payments/content/payment_method_manifest_table.cc b/components/payments/content/payment_method_manifest_table.cc
index f141b2c..0a10a50 100644
--- a/components/payments/content/payment_method_manifest_table.cc
+++ b/components/payments/content/payment_method_manifest_table.cc
@@ -312,7 +312,7 @@
       "credential_id, relying_party_id, browser_bound_key_id) "
       "VALUES (?, ?, ?)"));
   int index = 0;
-  s.BindBlob(index++, credential_id);
+  s.BindBlob(index++, std::move(credential_id));
   s.BindString(index++, relying_party_id);
   s.BindBlob(index++, browser_bound_key_id);
   return s.Run();
@@ -327,7 +327,7 @@
       "FROM secure_payment_confirmation_browser_bound_key "
       "WHERE credential_id = ? AND relying_party_id = ?"));
   int index = 0;
-  s.BindBlob(index++, credential_id);
+  s.BindBlob(index++, std::move(credential_id));
   s.BindString(index++, relying_party_id);
   if (!s.Step()) {
     return std::nullopt;
@@ -358,12 +358,12 @@
 bool PaymentMethodManifestTable::DeleteBrowserBoundKeys(
     std::vector<BrowserBoundKeyMetadata::RelyingPartyAndCredentialId>
         passkeys) {
-  for (auto passkey : passkeys) {
+  for (auto& passkey : passkeys) {
     sql::Statement s(db()->GetUniqueStatement(
         "DELETE FROM secure_payment_confirmation_browser_bound_key "
         "WHERE relying_party_id = ? AND credential_id = ?"));
     s.BindString(0, passkey.relying_party_id);
-    s.BindBlob(1, passkey.credential_id);
+    s.BindBlob(1, std::move(passkey.credential_id));
     if (!s.Run()) {
       return false;
     }
diff --git a/components/payments/content/web_app_manifest_section_table.cc b/components/payments/content/web_app_manifest_section_table.cc
index 935883c..c9a41bd 100644
--- a/components/payments/content/web_app_manifest_section_table.cc
+++ b/components/payments/content/web_app_manifest_section_table.cc
@@ -31,15 +31,15 @@
   return reinterpret_cast<void*>(&table_key);
 }
 
-// Converts 2-dimensional vector |fingerprints| to 1-dimesional vector.
-std::unique_ptr<std::vector<uint8_t>> SerializeFingerPrints(
+// Converts 2-dimensional vector |fingerprints| to 1-dimensional vector.
+std::vector<uint8_t> SerializeFingerPrints(
     const std::vector<std::vector<uint8_t>>& fingerprints) {
-  auto serialized_fingerprints = std::make_unique<std::vector<uint8_t>>();
+  std::vector<uint8_t> serialized_fingerprints;
 
   for (const auto& fingerprint : fingerprints) {
     DCHECK_EQ(fingerprint.size(), kFingerPrintLength);
-    serialized_fingerprints->insert(serialized_fingerprints->end(),
-                                    fingerprint.begin(), fingerprint.end());
+    serialized_fingerprints.insert(serialized_fingerprints.end(),
+                                   fingerprint.begin(), fingerprint.end());
   }
 
   return serialized_fingerprints;
@@ -133,9 +133,7 @@
     s2.BindTime(index++, expire_date);
     s2.BindString(index++, section.id);
     s2.BindInt64(index++, section.min_version);
-    std::unique_ptr<std::vector<uint8_t>> serialized_fingerprints =
-        SerializeFingerPrints(section.fingerprints);
-    s2.BindBlob(index, *serialized_fingerprints);
+    s2.BindBlob(index, SerializeFingerPrints(section.fingerprints));
     if (!s2.Run())
       return false;
     s2.Reset(true);
diff --git a/components/payments/core/payment_method_data.h b/components/payments/core/payment_method_data.h
index 585886a..60bbff1 100644
--- a/components/payments/core/payment_method_data.h
+++ b/components/payments/core/payment_method_data.h
@@ -5,17 +5,10 @@
 #ifndef COMPONENTS_PAYMENTS_CORE_PAYMENT_METHOD_DATA_H_
 #define COMPONENTS_PAYMENTS_CORE_PAYMENT_METHOD_DATA_H_
 
-#include <memory>
-#include <set>
 #include <string>
 #include <vector>
 
 #include "base/values.h"
-#include "components/autofill/core/browser/data_model/payments/credit_card.h"
-
-namespace base {
-class Value;
-}
 
 namespace payments {
 
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 515b01e..c677bde 100644
--- a/components/performance_manager/public/resource_attribution/cpu_measurement_delegate.h
+++ b/components/performance_manager/public/resource_attribution/cpu_measurement_delegate.h
@@ -55,8 +55,8 @@
 
   // Returns true iff a CPUMeasurementDelegate should be created for
   // `process_node`. The production factory returns true to measure
-  // renderer processes with a valid (running) base::Process and a
-  // base::ProcessId assigned.
+  // renderer processes for which ProcessNodeHasRunningProcess() returns true,
+  // except on Android which can only measure the current process.
   virtual bool ShouldMeasureProcess(
       const performance_manager::ProcessNode* process_node) = 0;
 
@@ -64,6 +64,11 @@
   // called if ShouldMeasureProcess(process_node) returns true.
   virtual std::unique_ptr<CPUMeasurementDelegate> CreateDelegateForProcess(
       const performance_manager::ProcessNode* process_node) = 0;
+
+  // Returns true if `process_node` has a valid (running) base::Process and a
+  // base::ProcessId assigned.
+  static bool ProcessNodeHasRunningProcess(
+      const performance_manager::ProcessNode* process_node);
 };
 
 }  // namespace resource_attribution
diff --git a/components/performance_manager/resource_attribution/cpu_measurement_delegate.cc b/components/performance_manager/resource_attribution/cpu_measurement_delegate.cc
index 7c4e54cd..8d4754d9 100644
--- a/components/performance_manager/resource_attribution/cpu_measurement_delegate.cc
+++ b/components/performance_manager/resource_attribution/cpu_measurement_delegate.cc
@@ -66,20 +66,17 @@
 
 bool CPUMeasurementDelegateFactoryImpl::ShouldMeasureProcess(
     const ProcessNode* process_node) {
-  // The process start time is not available until the ProcessId is assigned.
-  if (process_node->GetProcessId() == base::kNullProcessId) {
+#if BUILDFLAG(IS_ANDROID)
+  // On Android, sandbox restrictions prevent the browser process from reading
+  // /proc/<pid>/stat of other processes. Bail out early to avoid the overhead
+  // of trying and failing to read the file.
+  // TODO(crbug.com/421901748): Implement CPU measurement of child processes on
+  // Android.
+  if (process_node->GetProcessId() != base::GetCurrentProcId()) {
     return false;
   }
-  // This can be called from OnProcessLifetimeChange after a process exits.
-  // Only handle process start notifications (which is when the pid is
-  // assigned), not exit notifications. Note the pid can be reassigned if a
-  // process dies and a new one is started for the same ProcessNode - in that
-  // case MonitorCPUUsage will reset the measurements and start monitoring the
-  // new process from scratch.
-  if (!process_node->GetProcess().IsValid()) {
-    return false;
-  }
-  return true;
+#endif
+  return ProcessNodeHasRunningProcess(process_node);
 }
 
 std::unique_ptr<CPUMeasurementDelegate>
@@ -107,4 +104,23 @@
   return default_factory.get();
 }
 
+// static
+bool CPUMeasurementDelegate::Factory::ProcessNodeHasRunningProcess(
+    const performance_manager::ProcessNode* process_node) {
+  // The process start time is not available until the ProcessId is assigned.
+  if (process_node->GetProcessId() == base::kNullProcessId) {
+    return false;
+  }
+  // ShouldMeasureProcess() can be called from OnProcessLifetimeChange after a
+  // process exits. Only handle process start notifications (which is when the
+  // pid is assigned), not exit notifications. Note the pid can be reassigned if
+  // a process dies and a new one is started for the same ProcessNode - in that
+  // case MonitorCPUUsage will reset the measurements and start monitoring the
+  // new process from scratch.
+  if (!process_node->GetProcess().IsValid()) {
+    return false;
+  }
+  return true;
+}
+
 }  // namespace resource_attribution
diff --git a/components/performance_manager/resource_attribution/process_context.cc b/components/performance_manager/resource_attribution/process_context.cc
index 65b9e82..3c99eea9 100644
--- a/components/performance_manager/resource_attribution/process_context.cc
+++ b/components/performance_manager/resource_attribution/process_context.cc
@@ -9,7 +9,6 @@
 #include <variant>
 
 #include "base/check.h"
-#include "base/functional/overloaded.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
@@ -23,6 +22,7 @@
 #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/functional/overload.h"
 
 namespace resource_attribution {
 
@@ -174,7 +174,7 @@
 
 std::string ProcessContext::ToString() const {
   return std::visit(
-      base::Overloaded{
+      absl::Overload{
           [](const BrowserProcessTag&) -> std::string {
             return "ProcessContext:Browser";
           },
diff --git a/components/performance_manager/test_support/performance_manager_test_harness.cc b/components/performance_manager/test_support/performance_manager_test_harness.cc
index 2d0d8e8..908a603f 100644
--- a/components/performance_manager/test_support/performance_manager_test_harness.cc
+++ b/components/performance_manager/test_support/performance_manager_test_harness.cc
@@ -11,7 +11,6 @@
 #include "base/check.h"
 #include "base/containers/contains.h"
 #include "base/functional/bind.h"
-#include "base/functional/overloaded.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/bind.h"
 #include "base/unguessable_token.h"
@@ -30,6 +29,7 @@
 #include "content/public/browser/service_worker_running_info.h"
 #include "content/public/browser/shared_worker_service.h"
 #include "content/public/browser/web_contents.h"
+#include "third_party/abseil-cpp/absl/functional/overload.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
 #include "url/gurl.h"
@@ -352,7 +352,7 @@
   // Get the worker-type specific token. Service workers can't be clients of
   // shared workers.
   std::optional<content::ServiceWorkerClientInfo> service_worker_client_info =
-      worker_node->GetWorkerToken().Visit(base::Overloaded(
+      worker_node->GetWorkerToken().Visit(absl::Overload(
           [](const blink::ServiceWorkerToken& service_worker_token)
               -> std::optional<content::ServiceWorkerClientInfo> {
             return std::nullopt;
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 5574bc1..3cf9d8d7 100644
--- a/components/performance_manager/test_support/resource_attribution/measurement_delegates.cc
+++ b/components/performance_manager/test_support/resource_attribution/measurement_delegates.cc
@@ -63,8 +63,7 @@
 bool SimulatedCPUMeasurementDelegateFactory::ShouldMeasureProcess(
     const ProcessNode* process_node) {
   if (require_valid_processes_) {
-    // Delegate the decision to the production factory.
-    return CPUMeasurementDelegate::GetDefaultFactory()->ShouldMeasureProcess(
+    return CPUMeasurementDelegate::Factory::ProcessNodeHasRunningProcess(
         process_node);
   }
   return true;
diff --git a/components/performance_manager/worker_watcher.cc b/components/performance_manager/worker_watcher.cc
index 9f18a027..51b7bc7a 100644
--- a/components/performance_manager/worker_watcher.cc
+++ b/components/performance_manager/worker_watcher.cc
@@ -11,7 +11,6 @@
 
 #include "base/check_op.h"
 #include "base/containers/contains.h"
-#include "base/functional/overloaded.h"
 #include "base/memory/raw_ptr.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/notreached.h"
@@ -22,6 +21,7 @@
 #include "components/performance_manager/public/features.h"
 #include "components/performance_manager/render_process_user_data.h"
 #include "content/public/browser/render_process_host.h"
+#include "third_party/abseil-cpp/absl/functional/overload.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -176,7 +176,7 @@
   DCHECK(insertion_result.second);
 
   std::visit(
-      base::Overloaded(
+      absl::Overload(
           [&,
            this](const content::GlobalRenderFrameHostId& render_frame_host_id) {
             AddFrameClientConnection(insertion_result.first->second.get(),
@@ -202,7 +202,7 @@
   // First disconnect the creator's node from this worker node.
 
   std::visit(
-      base::Overloaded(
+      absl::Overload(
           [&,
            this](const content::GlobalRenderFrameHostId& render_frame_host_id) {
             RemoveFrameClientConnection(worker_node.get(),
@@ -391,7 +391,7 @@
     const std::string& client_uuid,
     const content::ServiceWorkerClientInfo& client_info) {
   std::visit(
-      base::Overloaded(
+      absl::Overload(
           [&, this](content::GlobalRenderFrameHostId render_frame_host_id) {
             // For window clients, it is necessary to wait until the navigation
             // has committed to a RenderFrameHost.
@@ -469,7 +469,7 @@
     return;
 
   std::visit(
-      base::Overloaded(
+      absl::Overload(
           [&, this](content::GlobalRenderFrameHostId render_frame_host_id) {
             RemoveFrameClientConnection(worker_node, render_frame_host_id);
           },
@@ -755,7 +755,7 @@
 
   for (const auto& kv : it->second) {
     std::visit(
-        base::Overloaded(
+        absl::Overload(
             [&, this](content::GlobalRenderFrameHostId render_frame_host_id) {
               AddFrameClientConnection(service_worker_node,
                                        render_frame_host_id);
@@ -782,7 +782,7 @@
 
   for (const auto& kv : it->second) {
     std::visit(
-        base::Overloaded(
+        absl::Overload(
             [&, this](
                 const content::GlobalRenderFrameHostId& render_frame_host_id) {
               RemoveFrameClientConnection(service_worker_node,
diff --git a/components/permissions/permission_request_manager_unittest.cc b/components/permissions/permission_request_manager_unittest.cc
index 3f2cfb2d..0a53948f 100644
--- a/components/permissions/permission_request_manager_unittest.cc
+++ b/components/permissions/permission_request_manager_unittest.cc
@@ -12,7 +12,6 @@
 #include <utility>
 
 #include "base/command_line.h"
-#include "base/debug/stack_trace.h"
 #include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
diff --git a/components/policy/resources/templates/policies.yaml b/components/policy/resources/templates/policies.yaml
index 7242a777..a9400a0 100644
--- a/components/policy/resources/templates/policies.yaml
+++ b/components/policy/resources/templates/policies.yaml
@@ -1365,6 +1365,7 @@
   1364: LocalNetworkAccessRestrictionsEnabled
   1365: PrefetchWithServiceWorkerEnabled
   1366: AIModeSearchSuggestSettings
+  1367: AIModeSettings
 
 atomic_groups:
   1: Homepage
diff --git a/components/policy/resources/templates/policy_definitions/GenerativeAI/AIModeSettings.yaml b/components/policy/resources/templates/policy_definitions/GenerativeAI/AIModeSettings.yaml
new file mode 100644
index 0000000..e875642
--- /dev/null
+++ b/components/policy/resources/templates/policy_definitions/GenerativeAI/AIModeSettings.yaml
@@ -0,0 +1,38 @@
+caption: Settings for Google's AI Mode integrations in the address bar and New Tab page search box.
+
+desc: |-
+  This policy controls Google's AI Mode integrations in the address bar and the New Tab page search box.
+
+  To access this feature, Google must be set as the user's default search engine.
+
+  0/unset = The feature will be available to users.
+
+  1 = The feature will not be available to users.
+
+  If the policy is unset, its behavior is determined by the <ph name="GEN_AI_DEFAULT_SETTINGS_POLICY_NAME">GenAiDefaultSettings</ph> policy.
+default: 0
+example_value: 1
+features:
+  dynamic_refresh: true
+  per_profile: true
+items:
+- caption: Allow AI Mode integrations.
+  name: Allowed
+  value: 0
+- caption: Do not allow AI Mode integrations.
+  name: Disabled
+  value: 1
+owners:
+- file://components/omnibox/OWNERS
+schema:
+  enum:
+  - 0
+  - 1
+  type: integer
+supported_on:
+- android:138-
+- ios:138-
+- chrome.*:138-
+- chrome_os:138-
+tags: []
+type: int-enum
diff --git a/components/policy/resources/webui/policy_table.html b/components/policy/resources/webui/policy_table.html
index 691fd36..a62953a 100644
--- a/components/policy/resources/webui/policy_table.html
+++ b/components/policy/resources/webui/policy_table.html
@@ -159,7 +159,8 @@
   <p class="id"></p>
   <div class="main">
     <div class="header row" role="row">
-      <div class="name" role="columnheader" aria-sort="none">$i18n{headerName}
+      <div class="name" role="columnheader" aria-sort="none" aria-label="$i18n{headerName}">
+        $i18n{headerName}
         <div class="sort-arrows">
           <button
             class="sort-up-arrow"
@@ -176,8 +177,11 @@
           </button>
         </div>
       </div>
-      <div class="value" role="columnheader">$i18n{headerValue}</div>
-      <div class="source" role="columnheader" aria-sort="none">$i18n{headerSource}
+      <div class="value" role="columnheader" aria-label="$i18n{headerValue}">
+        $i18n{headerValue}
+      </div>
+      <div class="source" role="columnheader" aria-sort="none" aria-label="$i18n{headerSource}">
+        $i18n{headerSource}
         <div class="sort-arrows">
           <button
             class="sort-up-arrow"
@@ -195,7 +199,8 @@
           </button>
         </div>
       </div>
-      <div class="scope" role="columnheader" aria-sort="none">$i18n{headerScope}
+      <div class="scope" role="columnheader" aria-sort="none" aria-label="$i18n{headerScope}">
+        $i18n{headerScope}
         <div class="sort-arrows">
           <button
             class="sort-up-arrow"
@@ -213,7 +218,8 @@
           </button>
         </div>
       </div>
-      <div class="level" role="columnheader" aria-sort="none">$i18n{headerLevel}
+      <div class="level" role="columnheader" aria-sort="none" aria-label="$i18n{headerLevel}">
+        $i18n{headerLevel}
         <div class="sort-arrows">
           <button
             class="sort-up-arrow"
@@ -231,7 +237,8 @@
           </button>
         </div>
       </div>
-      <div class="status" role="columnheader" aria-sort="none">$i18n{headerStatus}
+      <div class="status" role="columnheader" aria-sort="none" aria-label="$i18n{headerStatus}">
+        $i18n{headerStatus}
         <div class="sort-arrows">
           <button
             class="sort-up-arrow"
diff --git a/components/policy/test/data/pref_mapping/AIModeSettings.json b/components/policy/test/data/pref_mapping/AIModeSettings.json
new file mode 100644
index 0000000..aed3a20
--- /dev/null
+++ b/components/policy/test/data/pref_mapping/AIModeSettings.json
@@ -0,0 +1,20 @@
+[
+    {
+      "os": [
+        "android",
+        "win",
+        "linux",
+        "mac",
+        "chromeos"
+      ],
+      "simple_policy_pref_mapping_test": {
+        "pref_name": "omnibox.ai_mode_settings",
+        "default_value": 0,
+        "default_for_enterprise_users": 0,
+        "values_to_test": [
+          1,
+          0
+        ]
+      }
+    }
+]
diff --git a/components/policy/test/data/pref_mapping/GenAiDefaultSettings.json b/components/policy/test/data/pref_mapping/GenAiDefaultSettings.json
index a287d78..36d2447fc 100644
--- a/components/policy/test/data/pref_mapping/GenAiDefaultSettings.json
+++ b/components/policy/test/data/pref_mapping/GenAiDefaultSettings.json
@@ -17,6 +17,9 @@
           "omnibox.ai_mode_search_suggest_settings": {
             "value": 0
           },
+          "omnibox.ai_mode_settings": {
+            "value": 0
+          },
           "optimization_guide.model_execution.autofill_prediction_improvements_enterprise_policy_allowed": {
             "value": 0
           },
@@ -48,6 +51,9 @@
           "omnibox.ai_mode_search_suggest_settings": {
             "value": 0
           },
+          "omnibox.ai_mode_settings": {
+            "value": 0
+          },
           "optimization_guide.model_execution.autofill_prediction_improvements_enterprise_policy_allowed": {
             "value": 1
           },
@@ -79,6 +85,9 @@
           "omnibox.ai_mode_search_suggest_settings": {
             "value": 1
           },
+          "omnibox.ai_mode_settings": {
+            "value": 1
+          },
           "optimization_guide.model_execution.autofill_prediction_improvements_enterprise_policy_allowed": {
             "value": 2
           },
@@ -108,6 +117,9 @@
           "omnibox.ai_mode_search_suggest_settings": {
             "default_value": 0
           },
+          "omnibox.ai_mode_settings": {
+            "default_value": 0
+          },
           "optimization_guide.model_execution.autofill_prediction_improvements_enterprise_policy_allowed": {
             "default_value": 0
           },
@@ -137,7 +149,8 @@
           "HelpMeWriteSettings": 2,
           "HistorySearchSettings": 2,
           "TabCompareSettings": 2,
-          "GeminiSettings": 1
+          "GeminiSettings": 1,
+          "AIModeSettings": 1
         },
         "prefs": {
           "devtools.gen_ai_settings": {
@@ -160,6 +173,9 @@
           },
           "browser.gemini_settings": {
             "value": 1
+          },
+          "omnibox.ai_mode_settings": {
+            "value": 1
           }
         }
       }
@@ -196,6 +212,9 @@
           "omnibox.ai_mode_search_suggest_settings": {
             "value": 0
           },
+          "omnibox.ai_mode_settings": {
+            "value": 0
+          },
           "optimization_guide.model_execution.autofill_prediction_improvements_enterprise_policy_allowed": {
             "value": 0
           },
@@ -272,6 +291,9 @@
           },
           "browser.gemini_settings": {
             "value": 0
+          },
+          "omnibox.ai_mode_settings": {
+            "value": 0
           }
         }
       },
@@ -291,7 +313,8 @@
           "HelpMeWriteSettings": 2,
           "HistorySearchSettings": 2,
           "TabCompareSettings": 2,
-          "GeminiSettings": 1
+          "GeminiSettings": 1,
+          "AIModeSettings": 1
         },
         "prefs": {
           "ash.gen_ai_photo_editing_settings": {
@@ -338,6 +361,9 @@
           },
           "browser.gemini_settings": {
             "value": 1
+          },
+          "omnibox.ai_mode_settings": {
+            "value": 1
           }
         }
       }
@@ -355,6 +381,9 @@
         "prefs": {
           "omnibox.ai_mode_search_suggest_settings": {
             "value": 0
+          },
+          "omnibox.ai_mode_settings": {
+            "value": 0
           }
         }
       },
@@ -365,6 +394,9 @@
         "prefs": {
           "omnibox.ai_mode_search_suggest_settings": {
             "value": 0
+          },
+          "omnibox.ai_mode_settings": {
+            "value": 0
           }
         }
       },
@@ -375,6 +407,9 @@
         "prefs": {
           "omnibox.ai_mode_search_suggest_settings": {
             "value": 1
+          },
+          "omnibox.ai_mode_settings": {
+            "value": 1
           }
         }
       },
@@ -383,6 +418,31 @@
         "prefs": {
           "omnibox.ai_mode_search_suggest_settings": {
             "default_value": 0
+          },
+          "omnibox.ai_mode_settings": {
+            "default_value": 0
+          }
+        }
+      },
+      {
+        "policies": {
+          "GenAiDefaultSettings": 1,
+          "AIModeSettings": 1
+        },
+        "prefs": {
+          "omnibox.ai_mode_settings": {
+            "value": 1
+          }
+        }
+      },
+      {
+        "policies": {
+          "GenAiDefaultSettings": 2,
+          "AIModeSettings": 1
+        },
+        "prefs": {
+          "omnibox.ai_mode_settings": {
+            "value": 1
           }
         }
       }
diff --git a/components/power_bookmarks/storage/power_bookmark_database_impl.cc b/components/power_bookmarks/storage/power_bookmark_database_impl.cc
index bb89536..e2382e6 100644
--- a/components/power_bookmarks/storage/power_bookmark_database_impl.cc
+++ b/components/power_bookmarks/storage/power_bookmark_database_impl.cc
@@ -839,7 +839,7 @@
   power.ToPowerBookmarkSpecifics(&specifics);
   bool success = specifics.SerializeToString(&data);
   DCHECK(success);
-  blob_statement.BindBlob(0, data);
+  blob_statement.BindBlob(0, std::move(data));
   blob_statement.BindString(1, power.guid_string());
   if (!blob_statement.Run()) {
     return false;
diff --git a/components/power_bookmarks/storage/power_bookmark_database_impl_unittest.cc b/components/power_bookmarks/storage/power_bookmark_database_impl_unittest.cc
index fd020c4e..67babdf 100644
--- a/components/power_bookmarks/storage/power_bookmark_database_impl_unittest.cc
+++ b/components/power_bookmarks/storage/power_bookmark_database_impl_unittest.cc
@@ -132,8 +132,7 @@
     sql::Statement blob_statement(
         db.GetCachedStatement(SQL_FROM_HERE, kCreatePowerBlobSql));
     blob_statement.BindString(0, power->guid_string());
-    std::string data = "badprotofortesting";
-    blob_statement.BindBlob(1, data);
+    blob_statement.BindBlob(1, std::string{"badprotofortesting"});
     EXPECT_TRUE(blob_statement.Run());
   }
 
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 127a282..71158ca 100644
--- a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h
+++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h
@@ -107,6 +107,9 @@
 // ordering.
 @property(assign, nonatomic) BOOL isShufflingForOrdering;
 
+// Prevents the window from becoming the key window.
+@property(assign, nonatomic) BOOL preventKeyWindow;
+
 // Called whenever a child window is added to the receiver.
 @property(nonatomic, copy) void (^childWindowAddedHandler)(NSWindow* child);
 
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 6c809d7..4239df4 100644
--- a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
+++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
@@ -209,7 +209,6 @@
   BOOL _willUpdateRestorableState;
   BOOL _willSaveRestorableStateAfterDelay;
   BOOL _isEnforcingNeverMadeVisible;
-  BOOL _preventKeyWindow;
   BOOL _activationIndependence;
   BOOL _isTooltip;
   BOOL _isHeadless;
@@ -221,6 +220,7 @@
 @synthesize isTooltip = _isTooltip;
 @synthesize isHeadless = _isHeadless;
 @synthesize isShufflingForOrdering = _isShufflingForOrdering;
+@synthesize preventKeyWindow = _preventKeyWindow;
 @synthesize childWindowAddedHandler = _childWindowAddedHandler;
 @synthesize childWindowRemovedHandler = _childWindowRemovedHandler;
 @synthesize commandDispatchParentOverride = _commandDispatchParentOverride;
diff --git a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h
index c15ab9a5..7b5f8c8 100644
--- a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h
+++ b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h
@@ -183,7 +183,7 @@
   remote_cocoa::mojom::TextInputHost* text_input_host() const {
     return text_input_host_;
   }
-  NSWindow* ns_window();
+  NativeWidgetMacNSWindow* ns_window();
 
   remote_cocoa::DragDropClient* drag_drop_client();
   bool is_translucent_window() const { return is_translucent_window_; }
@@ -349,6 +349,8 @@
   // with the height of the menu bar if it autohides, or 0 if it doesn't.
   void OnAutohidingMenuBarHeightChanged(int menu_bar_height);
 
+  base::WeakPtr<NativeWidgetNSWindowBridge> GetWeakPtr();
+
  private:
   friend class views::test::BridgedNativeWidgetTestApi;
 
diff --git a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
index ff96d22..acc9537 100644
--- a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
+++ b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
@@ -1145,6 +1145,11 @@
   host_->OnAutohidingMenuBarHeightChanged(menu_bar_height);
 }
 
+base::WeakPtr<NativeWidgetNSWindowBridge>
+NativeWidgetNSWindowBridge::GetWeakPtr() {
+  return factory_.GetWeakPtr();
+}
+
 void NativeWidgetNSWindowBridge::SetCanGoBack(bool can_go_back) {
   can_go_back_ = can_go_back;
 }
@@ -1414,7 +1419,7 @@
   return [[window_ commandDispatcher] redispatchKeyEvent:event];
 }
 
-NSWindow* NativeWidgetNSWindowBridge::ns_window() {
+NativeWidgetMacNSWindow* NativeWidgetNSWindowBridge::ns_window() {
   return window_;
 }
 
diff --git a/components/remote_cocoa/app_shim/select_file_dialog_bridge.h b/components/remote_cocoa/app_shim/select_file_dialog_bridge.h
index e5cab30a..a3a9a1ae 100644
--- a/components/remote_cocoa/app_shim/select_file_dialog_bridge.h
+++ b/components/remote_cocoa/app_shim/select_file_dialog_bridge.h
@@ -7,6 +7,7 @@
 
 #import <Cocoa/Cocoa.h>
 
+#include <memory>
 #include <vector>
 
 #include "base/functional/callback.h"
@@ -46,6 +47,8 @@
   static NSSavePanel* GetLastCreatedNativePanelForTesting();
 
  private:
+  class ScopedPreventKeyWindow;
+
   // Sets the accessory view for |dialog_| and sets
   // |extension_dropdown_handler_|. |is_save_panel| specifies whether this is
   // for a save panel or not.
@@ -69,6 +72,9 @@
   // The parent window for |panel_|.
   NSWindow* __strong owning_window_;
 
+  // Used to prevent the sheet parent from getting key.
+  std::unique_ptr<ScopedPreventKeyWindow> scoped_prevent_key_window_;
+
   // The delegate for |panel|.
   SelectFileDialogDelegate* __strong delegate_;
 
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 8e5fcc91..773384f 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/threading/hang_watcher.h"
 #include "base/threading/thread_restrictions.h"
 #import "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h"
+#import "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h"
 #import "ui/base/l10n/l10n_util_mac.h"
 #include "ui/strings/grit/ui_strings.h"
 
@@ -405,6 +406,18 @@
           base::apple::ObjCCast<NativeWidgetMacNSWindow>(sheet_parent)) {
     sheet_parent = [sheet_parent_widget_window preferredSheetParent];
   }
+
+  // The sheet parent will be activated by AppKit on sheet close, which may
+  // cause auto-dismissal of the owning window (e.g. extension popup).
+  // Therefore, prevent the sheet parent from becoming key.
+  if (sheet_parent != owning_window_) {
+    if (NativeWidgetMacNSWindow* sheet_parent_widget_window =
+            base::apple::ObjCCast<NativeWidgetMacNSWindow>(sheet_parent)) {
+      scoped_prevent_key_window_ =
+          std::make_unique<ScopedPreventKeyWindow>(sheet_parent_widget_window);
+    }
+  }
+
   [panel_ beginSheetModalForWindow:sheet_parent
                  completionHandler:^(NSInteger result) {
                    ended_callback.Run(result != NSModalResponseOK);
@@ -578,6 +591,26 @@
   std::move(show_callback_).Run(did_cancel, paths, index, file_tags);
 }
 
+class SelectFileDialogBridge::ScopedPreventKeyWindow {
+ public:
+  ScopedPreventKeyWindow(NativeWidgetMacNSWindow* window) {
+    bridge_ = window.bridge->GetWeakPtr();
+    window.preventKeyWindow = YES;
+  }
+
+  ScopedPreventKeyWindow(const SelectFileDialogBridge&) = delete;
+  ScopedPreventKeyWindow& operator=(const SelectFileDialogBridge&) = delete;
+
+  ~ScopedPreventKeyWindow() {
+    if (bridge_) {
+      bridge_->ns_window().preventKeyWindow = NO;
+    }
+  }
+
+ private:
+  base::WeakPtr<NativeWidgetNSWindowBridge> bridge_;
+};
+
 // static
 NSSavePanel* SelectFileDialogBridge::GetLastCreatedNativePanelForTesting() {
   return g_last_created_panel_for_testing;
diff --git a/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier_delegate.cc b/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier_delegate.cc
index aac9ac5..f02fab8 100644
--- a/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier_delegate.cc
+++ b/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier_delegate.cc
@@ -8,7 +8,6 @@
 #include <set>
 #include <utility>
 
-#include "base/debug/stack_trace.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/lazy_instance.h"
diff --git a/components/search_engines/keyword_table.cc b/components/search_engines/keyword_table.cc
index f4c40a2..ad6b7bd0 100644
--- a/components/search_engines/keyword_table.cc
+++ b/components/search_engines/keyword_table.cc
@@ -303,7 +303,7 @@
   while (s.Step()) {
     const auto data = GetKeywordDataFromStatement(s);
     if (data) {
-      keywords->emplace_back(std::move(*data));
+      keywords->emplace_back(*std::move(data));
     } else {
       bad_entries.insert(s.ColumnInt64(0));
     }
@@ -549,9 +549,10 @@
     }
 
     data.SetURL(maybe_url);
-    const auto url_hash = data.GenerateHash();
-    const auto encrypted_hash = encryptor()->EncryptString(
-        std::string(url_hash.begin(), url_hash.end()));
+    const std::vector<uint8_t> url_hash = data.GenerateHash();
+    const std::optional<std::vector<uint8_t>> encrypted_hash =
+        encryptor()->EncryptString(
+            std::string(url_hash.begin(), url_hash.end()));
     if (!encrypted_hash) {
       all_rows_migrated = false;
       continue;
@@ -561,7 +562,7 @@
     sql::Statement update_statement(db()->GetCachedStatement(
         SQL_FROM_HERE, "UPDATE keywords SET url_hash=? WHERE id=?"));
 
-    update_statement.BindBlob(0, *encrypted_hash);
+    update_statement.BindBlob(0, *std::move(encrypted_hash));
     update_statement.BindInt64(1, data.id);
 
     if (!update_statement.Run()) {
@@ -714,11 +715,12 @@
   s->BindBool(starting_column + 24, data.enforced_by_policy);
   s->BindBool(starting_column + 25, data.featured_by_policy);
   if (encryptor()->IsEncryptionAvailable()) {
-    const auto url_hash = data.GenerateHash();
-    const auto encrypted_hash = encryptor()->EncryptString(
-        std::string(url_hash.begin(), url_hash.end()));
+    const std::vector<uint8_t> url_hash = data.GenerateHash();
+    std::optional<std::vector<uint8_t>> encrypted_hash =
+        encryptor()->EncryptString(
+            std::string(url_hash.begin(), url_hash.end()));
     CHECK(encrypted_hash);
-    s->BindBlob(starting_column + 26, *encrypted_hash);
+    s->BindBlob(starting_column + 26, *std::move(encrypted_hash));
   } else {
     s->BindNull(starting_column + 26);
   }
diff --git a/components/segmentation_platform/public/constants.h b/components/segmentation_platform/public/constants.h
index 9e00bff..d548d8e6 100644
--- a/components/segmentation_platform/public/constants.h
+++ b/components/segmentation_platform/public/constants.h
@@ -112,6 +112,9 @@
 
 const char kEphemeralHomeModuleBackendKey[] = "ephemeral_home_module_backend";
 
+const char kIosDefaultBrowserPromoKey[] = "ios_default_browser_promo";
+const char kIosDefaultBrowserPromoUmaName[] = "IosDefaultBrowserPromo";
+
 // The key is used to decide whether the user would use FedCM.
 const char kFedCmUserKey[] = "fedcm_user";
 const char kFedCmUserUmaName[] = "FedCmUser";
diff --git a/components/segmentation_platform/public/proto/segmentation_platform.proto b/components/segmentation_platform/public/proto/segmentation_platform.proto
index e12ac25..e0e9ead 100644
--- a/components/segmentation_platform/public/proto/segmentation_platform.proto
+++ b/components/segmentation_platform/public/proto/segmentation_platform.proto
@@ -31,7 +31,8 @@
   // Reserved fields are `OptimizationTarget`s that are not used by segmentation
   // to prevent accidental misuse.
   reserved 1, 2, 3, 7, 8, 9, 13, 14, 15, 19, 20, 24, 25, 26, 30, 31, 33, 34, 35,
-      36, 39, 40, 42, 43, 44, 47, 48, 49, 51, 52, 53, 54, 55, 56, 57, 1013;
+      36, 39, 40, 42, 43, 44, 47, 48, 49, 51, 52, 53, 54, 55, 56, 57, 59, 60,
+      61, 62, 1013;
 
   OPTIMIZATION_TARGET_UNKNOWN = 0;
   // Target for segmentation: New tab page user.
@@ -81,6 +82,8 @@
   OPTIMIZATION_TARGET_SEGMENTATION_METRICS_CLUSTERING = 50;
   // Target for segmentation: FedCM user.
   OPTIMIZATION_TARGET_SEGMENTATION_FEDCM_USER = 58;
+  // Target for ios_default_browser_promo.
+  OPTIMIZATION_TARGET_SEGMENTATION_IOS_DEFAULT_BROWSER_PROMO = 63;
   // Add new entries to OptimizationTarget proto.
 
   // New entries should start from a 1000 if OptimizationTarget does not
diff --git a/components/services/on_device_translation/public/cpp/features.cc b/components/services/on_device_translation/public/cpp/features.cc
index e95bd745..2c8eeda 100644
--- a/components/services/on_device_translation/public/cpp/features.cc
+++ b/components/services/on_device_translation/public/cpp/features.cc
@@ -15,10 +15,6 @@
 
 namespace {
 
-// Limit the number of downloadable language packs to 5 during OT to mitigate
-// the risk of fingerprinting attacks.
-constexpr size_t kTranslationAPILimitLanguagePackCountMax = 5;
-
 base::FilePath GetPathFromCommandLine(const char* switch_name) {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (!command_line->HasSwitch(switch_name)) {
@@ -33,14 +29,6 @@
     &blink::features::kTranslationAPI, "TranslationAPILibraryMinimumVersion",
     "2025.1.10.0"};
 
-const base::FeatureParam<bool> kTranslationAPIAcceptLanguagesCheck{
-    &blink::features::kTranslationAPI, "TranslationAPIAcceptLanguagesCheck",
-    true};
-
-const base::FeatureParam<bool> kTranslationAPILimitLanguagePackCount{
-    &blink::features::kTranslationAPI, "TranslationAPILimitLanguagePackCount",
-    true};
-
 const base::FeatureParam<base::TimeDelta> kTranslationAPIServiceIdleTimeout{
     &blink::features::kTranslationAPI, "TranslationAPIServiceIdleTimeout",
     base::Minutes(1)};
@@ -53,17 +41,6 @@
   return GetPathFromCommandLine(kTranslateKitBinaryPath);
 }
 
-size_t GetInstallablePackageCount(size_t installed_package_count) {
-  if (base::FeatureList::IsEnabled(blink::features::kTranslationAPIV1) ||
-      !kTranslationAPILimitLanguagePackCount.Get()) {
-    return std::numeric_limits<size_t>::max();
-  }
-  if (installed_package_count >= kTranslationAPILimitLanguagePackCountMax) {
-    return 0;
-  }
-  return kTranslationAPILimitLanguagePackCountMax - installed_package_count;
-}
-
 bool IsValidTranslateKitVersion(std::string_view version_str) {
   base::Version minimum_version(kTranslationAPILibraryMinimumVersion.Get());
   CHECK(minimum_version.IsValid());
diff --git a/components/services/on_device_translation/public/cpp/features.h b/components/services/on_device_translation/public/cpp/features.h
index 1a80c7d..279e820 100644
--- a/components/services/on_device_translation/public/cpp/features.h
+++ b/components/services/on_device_translation/public/cpp/features.h
@@ -23,18 +23,6 @@
 // Any version string longer than this will be truncated.
 inline constexpr size_t kTranslationAPILibraryVersionStringSize = 14;
 
-// When this feature param is enabled, the Translation API will fail if neither
-// the source nor destination language is in the AcceptLanguages. This is
-// introduced to mitigate privacy concerns.
-extern const base::FeatureParam<bool> kTranslationAPIAcceptLanguagesCheck;
-
-// This feature limits the number of language components downloaded by
-// createTranslator() to 3.
-extern const base::FeatureParam<bool> kTranslationAPILimitLanguagePackCount;
-
-// Returns the number of additionally installable language packs.
-size_t GetInstallablePackageCount(size_t installed_package_count);
-
 // The duration that the OnDeviceTranslation service can remain idle before it
 // is terminated.
 extern const base::FeatureParam<base::TimeDelta>
diff --git a/components/services/storage/indexed_db/scopes/leveldb_scope.cc b/components/services/storage/indexed_db/scopes/leveldb_scope.cc
index d087054..4aae754 100644
--- a/components/services/storage/indexed_db/scopes/leveldb_scope.cc
+++ b/components/services/storage/indexed_db/scopes/leveldb_scope.cc
@@ -11,7 +11,6 @@
 
 #include "base/check.h"
 #include "base/compiler_specific.h"
-#include "base/debug/stack_trace.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/sequence_checker.h"
diff --git a/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_iterator.cc b/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_iterator.cc
index c3bd153..340925c8 100644
--- a/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_iterator.cc
+++ b/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_iterator.cc
@@ -6,7 +6,6 @@
 
 #include <utility>
 
-#include "base/debug/stack_trace.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "components/services/storage/indexed_db/scopes/leveldb_scope.h"
diff --git a/components/services/storage/partition_impl.cc b/components/services/storage/partition_impl.cc
index 15e641a..0b136de4 100644
--- a/components/services/storage/partition_impl.cc
+++ b/components/services/storage/partition_impl.cc
@@ -85,14 +85,6 @@
 }
 
 void PartitionImpl::BindLocalStorageControl(
-    mojo::PendingReceiver<mojom::LocalStorageControl> receiver) {
-  local_storage_ = std::make_unique<LocalStorageImpl>(
-      path_.value_or(base::FilePath()),
-      base::SequencedTaskRunner::GetCurrentDefault(), std::move(receiver));
-}
-
-#if BUILDFLAG(IS_MAC)
-void PartitionImpl::BindLocalStorageControlAndReportLifecycle(
     mojom::LocalStorageLifecycle lifecycle,
     mojo::PendingReceiver<mojom::LocalStorageControl> receiver) {
   SCOPED_CRASH_KEY_NUMBER("396030877", "local_storage_lifecycle",
@@ -101,7 +93,6 @@
       path_.value_or(base::FilePath()),
       base::SequencedTaskRunner::GetCurrentDefault(), std::move(receiver));
 }
-#endif  // BUILDFLAG(IS_MAC)
 
 void PartitionImpl::OnDisconnect() {
   if (receivers_.empty()) {
diff --git a/components/services/storage/partition_impl.h b/components/services/storage/partition_impl.h
index ba21d6f..e012fbb9 100644
--- a/components/services/storage/partition_impl.h
+++ b/components/services/storage/partition_impl.h
@@ -47,12 +47,8 @@
   void BindSessionStorageControl(
       mojo::PendingReceiver<mojom::SessionStorageControl> receiver) override;
   void BindLocalStorageControl(
-      mojo::PendingReceiver<mojom::LocalStorageControl> receiver) override;
-#if BUILDFLAG(IS_MAC)
-  void BindLocalStorageControlAndReportLifecycle(
       mojom::LocalStorageLifecycle lifecycle,
       mojo::PendingReceiver<mojom::LocalStorageControl> receiver) override;
-#endif  // BUILDFLAG(IS_MAC)
 
  private:
   void OnDisconnect();
diff --git a/components/services/storage/public/mojom/partition.mojom b/components/services/storage/public/mojom/partition.mojom
index 0959952..01dfc75d 100644
--- a/components/services/storage/public/mojom/partition.mojom
+++ b/components/services/storage/public/mojom/partition.mojom
@@ -7,10 +7,9 @@
 import "components/services/storage/public/mojom/local_storage_control.mojom";
 import "components/services/storage/public/mojom/session_storage_control.mojom";
 
-// Mac only enum used as a crash key to help investigate Storage process
-// crashes.
+// Enum used as a crash key to help investigate Storage process crashes on
+// various platforms.
 // TODO(crbug.com/396030877): Fix the bug and remove this enum.
-[EnableIf=is_mac]
 enum LocalStorageLifecycle {
   kInitializing,
   kInitializingWithUnboundStorageService,
@@ -26,14 +25,9 @@
       pending_receiver<SessionStorageControl> receiver);
 
   // Binds the main control interface for Local Storage in this partition.
-  BindLocalStorageControl(pending_receiver<LocalStorageControl> receiver);
-
-  // Similar to the above method but with a `lifecycle` param. This is included
-  // as a crash key to help root cause a Storage process crash in LocalStorage
-  // code on macOS.
-  // TODO(crbug.com/396030877): Fix the bug and remove this param.
-  [EnableIf=is_mac]
-  BindLocalStorageControlAndReportLifecycle(
-      LocalStorageLifecycle lifecycle,
-      pending_receiver<LocalStorageControl> receiver);
+  // Includes a `lifecycle` param which is used as a crash key to help root
+  // cause a Storage process crash in LocalStorage code.
+  // TODO(crbug.com/396030877): Fix the bug and remove the `lifecycle` param.
+  BindLocalStorageControl(LocalStorageLifecycle lifecycle,
+                          pending_receiver<LocalStorageControl> receiver);
 };
diff --git a/components/services/storage/shared_storage/shared_storage_database.cc b/components/services/storage/shared_storage/shared_storage_database.cc
index c1f800cc..c0f0dab 100644
--- a/components/services/storage/shared_storage/shared_storage_database.cc
+++ b/components/services/storage/shared_storage/shared_storage_database.cc
@@ -315,7 +315,7 @@
   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kSelectSql));
   std::string origin_str(SerializeOrigin(context_origin));
   statement.BindString(0, origin_str);
-  statement.BindBlob(1, key);
+  statement.BindBlob(1, std::u16string(key));
 
   if (statement.Step()) {
     base::Time last_used_time = statement.ColumnTime(1);
@@ -450,7 +450,7 @@
 
   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kDeleteSql));
   statement.BindString(0, origin_str);
-  statement.BindBlob(1, key);
+  statement.BindBlob(1, std::u16string(key));
 
   if (!statement.Run())
     return OperationResult::kSqlError;
@@ -1683,7 +1683,7 @@
 
   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kSelectSql));
   statement.BindString(0, context_origin);
-  statement.BindBlob(1, key);
+  statement.BindBlob(1, std::u16string(key));
 
   std::u16string value;
   if (statement.Step() && statement.ColumnBlobAsString16(0, &value)) {
@@ -1776,10 +1776,10 @@
         "WHERE context_origin=? AND key=?";
 
     sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kUpdateSql));
-    statement.BindBlob(0, value);
+    statement.BindBlob(0, std::u16string(value));
     statement.BindTime(1, last_used_time);
     statement.BindString(2, context_origin);
-    statement.BindBlob(3, key);
+    statement.BindBlob(3, std::u16string(key));
 
     if (!statement.Run()) {
       return false;
@@ -1801,8 +1801,8 @@
 
   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kInsertSql));
   statement.BindString(0, context_origin);
-  statement.BindBlob(1, key);
-  statement.BindBlob(2, value);
+  statement.BindBlob(1, std::u16string(key));
+  statement.BindBlob(2, std::u16string(value));
   statement.BindTime(3, last_used_time);
 
   if (!statement.Run())
diff --git a/components/services/unzip/public/cpp/unzip.h b/components/services/unzip/public/cpp/unzip.h
index 71a0d4c..34518f8 100644
--- a/components/services/unzip/public/cpp/unzip.h
+++ b/components/services/unzip/public/cpp/unzip.h
@@ -8,7 +8,7 @@
 #include <cstdint>
 
 #include "base/functional/callback_forward.h"
-#include "components/services/unzip/public/mojom/unzipper.mojom.h"
+#include "components/services/unzip/public/mojom/unzipper.mojom-forward.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/ced/src/util/encodings/encodings.h"
 
@@ -18,6 +18,11 @@
 
 namespace unzip {
 
+// A type storing how to create Unzipper remotes, for dependency injection.
+// This will typically be unzip::LaunchUnzipper, except in tests / iOS.
+using UnzipperFactory =
+    base::RepeatingCallback<mojo::PendingRemote<mojom::Unzipper>()>;
+
 // Unzips files and directories in `zip_file` that match `filter_callback` into
 // `output_dir`. Returns a closure that cancels the unzip operation when called.
 // Unzip must be called on a sequenced task runner. The cancellation closure may
diff --git a/components/signin/public/webdata/token_service_table.cc b/components/signin/public/webdata/token_service_table.cc
index 4ffd3d5e..789ecb3 100644
--- a/components/signin/public/webdata/token_service_table.cc
+++ b/components/signin/public/webdata/token_service_table.cc
@@ -131,7 +131,7 @@
         "INSERT OR REPLACE INTO token_service "
         "(service, encrypted_token, binding_key) VALUES (?, ?, ?)"));
     s.BindString(0, service);
-    s.BindBlob(1, encrypted_token);
+    s.BindBlob(1, std::move(encrypted_token));
     s.BindBlob(2, wrapped_binding_key);
 
     if (!s.Run()) {
diff --git a/components/sqlite_proto/key_value_table.cc b/components/sqlite_proto/key_value_table.cc
index 9cd4d7d4..3d24a8e 100644
--- a/components/sqlite_proto/key_value_table.cc
+++ b/components/sqlite_proto/key_value_table.cc
@@ -21,7 +21,7 @@
   size_t size = data.ByteSizeLong();
   std::vector<uint8_t> proto_buffer(size);
   data.SerializeToArray(proto_buffer.data(), size);
-  statement->BindBlob(1, proto_buffer);
+  statement->BindBlob(1, std::move(proto_buffer));
 }
 
 std::string GetSelectAllSql(const std::string& table_name) {
diff --git a/components/tab_groups/android/java/res/values/colors.xml b/components/tab_groups/android/java/res/values/colors.xml
index 5772f3d..03c2724 100644
--- a/components/tab_groups/android/java/res/values/colors.xml
+++ b/components/tab_groups/android/java/res/values/colors.xml
@@ -37,4 +37,15 @@
     <color name="tab_group_card_color_purple">@color/tab_group_card_color_purple_gm3</color>
     <color name="tab_group_card_color_red">@color/tab_group_card_color_red_gm3</color>
     <color name="tab_group_card_color_yellow">@color/tab_group_card_color_yellow_gm3</color>
+
+    <!-- Tab group card text related colors. -->
+    <color name="tab_group_card_text_color_grey">@color/tab_group_card_text_color_grey_gm3</color>
+    <color name="tab_group_card_text_color_blue">@color/tab_group_card_text_color_blue_gm3</color>
+    <color name="tab_group_card_text_color_cyan">@color/tab_group_card_text_color_cyan_gm3</color>
+    <color name="tab_group_card_text_color_green">@color/tab_group_card_text_color_green_gm3</color>
+    <color name="tab_group_card_text_color_orange">@color/tab_group_card_text_color_orange_gm3</color>
+    <color name="tab_group_card_text_color_pink">@color/tab_group_card_text_color_pink_gm3</color>
+    <color name="tab_group_card_text_color_purple">@color/tab_group_card_text_color_purple_gm3</color>
+    <color name="tab_group_card_text_color_red">@color/tab_group_card_text_color_red_gm3</color>
+    <color name="tab_group_card_text_color_yellow">@color/tab_group_card_text_color_yellow_gm3</color>
 </resources>
\ No newline at end of file
diff --git a/components/tab_groups/android/java/src/org/chromium/components/tab_groups/TabGroupColorPickerUtils.java b/components/tab_groups/android/java/src/org/chromium/components/tab_groups/TabGroupColorPickerUtils.java
index 8be7c44..b77713ac 100644
--- a/components/tab_groups/android/java/src/org/chromium/components/tab_groups/TabGroupColorPickerUtils.java
+++ b/components/tab_groups/android/java/src/org/chromium/components/tab_groups/TabGroupColorPickerUtils.java
@@ -151,6 +151,45 @@
         };
     }
 
+    /**
+     * Get the color corresponding to the color id that is passed in. Adjust the color depending on
+     * light/dark/incognito mode as well as dynamic color themes. This function should only be used
+     * for retrieving items from the tab group card text color.
+     *
+     * @param context The current context.
+     * @param colorId The color id corresponding to the color of the Tab Group.
+     * @param isIncognito Whether the current tab model is in incognito mode.
+     */
+    public static @ColorInt int getTabGroupCardTextColor(
+            Context context, @TabGroupColorId int colorId, boolean isIncognito) {
+        @ColorRes int colorRes = getTabGroupCardTextColorResource(colorId);
+        return resolveGroupRelatedColor(context, colorRes, isIncognito);
+    }
+
+    /**
+     * Get the color resource corresponding to the respective Tab Group Text color. This function
+     * should only be used for retrieving items from the tab group text color.
+     *
+     * @param colorId The color id corresponding to the color of the Tab Group.
+     */
+    public static @ColorRes int getTabGroupCardTextColorResource(@TabGroupColorId int colorId) {
+        return switch (colorId) {
+            case TabGroupColorId.GREY -> R.color.tab_group_card_text_color_grey;
+            case TabGroupColorId.BLUE -> R.color.tab_group_card_text_color_blue;
+            case TabGroupColorId.RED -> R.color.tab_group_card_text_color_red;
+            case TabGroupColorId.YELLOW -> R.color.tab_group_card_text_color_yellow;
+            case TabGroupColorId.GREEN -> R.color.tab_group_card_text_color_green;
+            case TabGroupColorId.PINK -> R.color.tab_group_card_text_color_pink;
+            case TabGroupColorId.PURPLE -> R.color.tab_group_card_text_color_purple;
+            case TabGroupColorId.CYAN -> R.color.tab_group_card_text_color_cyan;
+            case TabGroupColorId.ORANGE -> R.color.tab_group_card_text_color_orange;
+            default -> {
+                assert false : "Invalid tab group text color id " + colorId;
+                yield Resources.ID_NULL;
+            }
+        };
+    }
+
     private static @ColorInt int resolveGroupRelatedColor(
             Context context, @ColorRes int colorRes, boolean isIncognito) {
         @ColorInt int color = ContextCompat.getColor(context, colorRes);
diff --git a/components/user_data_importer/utility/BUILD.gn b/components/user_data_importer/utility/BUILD.gn
index 83424897..9fe2142 100644
--- a/components/user_data_importer/utility/BUILD.gn
+++ b/components/user_data_importer/utility/BUILD.gn
@@ -6,6 +6,8 @@
 
 source_set("safari_data_importer") {
   sources = [
+    "safari_data_import_manager.cc",
+    "safari_data_import_manager.h",
     "safari_data_importer.cc",
     "safari_data_importer.h",
   ]
@@ -15,6 +17,7 @@
     "//base",
     "//components/password_manager/core/browser/import:importer",
     "//components/password_manager/core/browser/ui",
+    "//components/user_data_importer/common",
   ]
 }
 
diff --git a/components/user_data_importer/utility/safari_data_import_manager.cc b/components/user_data_importer/utility/safari_data_import_manager.cc
new file mode 100644
index 0000000..fc96041e
--- /dev/null
+++ b/components/user_data_importer/utility/safari_data_import_manager.cc
@@ -0,0 +1,12 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/user_data_importer/utility/safari_data_import_manager.h"
+
+namespace user_data_importer {
+
+SafariDataImportManager::ParsedBookmarks::ParsedBookmarks() = default;
+SafariDataImportManager::ParsedBookmarks::~ParsedBookmarks() = default;
+
+}  // namespace user_data_importer
diff --git a/components/user_data_importer/utility/safari_data_import_manager.h b/components/user_data_importer/utility/safari_data_import_manager.h
new file mode 100644
index 0000000..4246c4d3d
--- /dev/null
+++ b/components/user_data_importer/utility/safari_data_import_manager.h
@@ -0,0 +1,63 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_USER_DATA_IMPORTER_UTILITY_SAFARI_DATA_IMPORT_MANAGER_H_
+#define COMPONENTS_USER_DATA_IMPORTER_UTILITY_SAFARI_DATA_IMPORT_MANAGER_H_
+
+#include "base/functional/callback.h"
+#include "base/types/expected.h"
+#include "components/user_data_importer/common/imported_bookmark_entry.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace user_data_importer {
+
+// Interface for providing platform-specific implementations of certain
+// model-layer logic (e.g., some parsing).
+class SafariDataImportManager {
+ public:
+  // Result of a successful invocation of `ParseBookmarks` below.
+  struct ParsedBookmarks {
+    ParsedBookmarks();
+    ~ParsedBookmarks();
+
+    // List of standard bookmarks and folders.
+    std::vector<ImportedBookmarkEntry> bookmarks;
+
+    // Safari includes Reading List entries in bookmarks.html.
+    std::vector<ImportedBookmarkEntry> reading_list;
+  };
+
+  // Failure reason for an unsuccessful invocation of `ParseBookmarks` below.
+  enum class BookmarkParsingError {
+    // The file was larger than the maximum supported by this manager.
+    kTooBig,
+
+    // The file could not be parsed (e.g., bad syntax).
+    kParsingFailed,
+
+    // The operation did not complete within the allotted time.
+    kTimedOut
+  };
+
+  virtual ~SafariDataImportManager() = default;
+
+  using BookmarkParsingResult =
+      base::expected<ParsedBookmarks, BookmarkParsingError>;
+
+  // Opens the file at the given FilePath, treating it as an HTML file matching
+  // the Netscape bookmarks format:
+  // https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa753582(v=vs.85)
+  // Parses the document and extracts bookmarks and reading list entries.
+  // Invokes `callback` with the result of parsing.
+  virtual void ParseBookmarks(
+      const base::FilePath& bookmarks_html,
+      base::OnceCallback<void(BookmarkParsingResult)> callback) = 0;
+};
+
+}  //  namespace user_data_importer
+
+#endif  // COMPONENTS_USER_DATA_IMPORTER_UTILITY_SAFARI_DATA_IMPORT_MANAGER_H_
diff --git a/components/user_data_importer/utility/safari_data_importer.cc b/components/user_data_importer/utility/safari_data_importer.cc
index e9413bc4..89e8e57 100644
--- a/components/user_data_importer/utility/safari_data_importer.cc
+++ b/components/user_data_importer/utility/safari_data_importer.cc
@@ -5,9 +5,11 @@
 #include "components/user_data_importer/utility/safari_data_importer.h"
 
 #include "base/containers/span_rust.h"
+#include "base/files/file_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/task/thread_pool.h"
 #include "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
+#include "components/user_data_importer/utility/safari_data_import_manager.h"
 #include "components/user_data_importer/utility/zip_ffi_glue.rs.h"
 
 namespace user_data_importer {
@@ -37,11 +39,13 @@
 }
 
 SafariDataImporter::SafariDataImporter(
-    password_manager::SavedPasswordsPresenter* presenter)
+    password_manager::SavedPasswordsPresenter* presenter,
+    std::unique_ptr<SafariDataImportManager> manager)
     : password_importer_(std::make_unique<password_manager::PasswordImporter>(
           presenter,
           /*user_confirmation_required=*/true)),
-      task_runner_(base::SequencedTaskRunner::GetCurrentDefault()) {}
+      task_runner_(base::SequencedTaskRunner::GetCurrentDefault()),
+      manager_(std::move(manager)) {}
 
 SafariDataImporter::~SafariDataImporter() = default;
 
@@ -95,13 +99,18 @@
     const std::string& zip_filename,
     ImportCallback bookmarks_callback) {
   std::string html_data = UnzipFile(zip_filename, FileType::Bookmarks);
-  if (html_data.empty()) {
+  base::ScopedTempFile bookmarks_html;
+  if (!html_data.empty() && bookmarks_html.Create()) {
+    base::WriteFile(bookmarks_html.path(), html_data);
+  }
+
+  if (!bookmarks_html) {
     PostCallback(std::move(bookmarks_callback), /*number_of_imports=*/0);
   } else {
     task_runner_->PostTask(
-        FROM_HERE,
-        base::BindOnce(&SafariDataImporter::ImportBookmarks, AsWeakPtr(),
-                       std::move(html_data), std::move(bookmarks_callback)));
+        FROM_HERE, base::BindOnce(&SafariDataImporter::ImportBookmarks,
+                                  AsWeakPtr(), std::move(bookmarks_html),
+                                  std::move(bookmarks_callback)));
   }
 }
 
@@ -168,12 +177,9 @@
   PostCallback(std::move(payment_cards_callback), /*number_of_imports=*/0);
 }
 
-void SafariDataImporter::ImportBookmarks(std::string html_data,
+void SafariDataImporter::ImportBookmarks(base::ScopedTempFile&& bookmarks_html,
                                          ImportCallback bookmarks_callback) {
-  if (html_data.empty()) {
-    PostCallback(std::move(bookmarks_callback), /*number_of_imports=*/0);
-    return;
-  }
+  CHECK(bookmarks_html);
 
   // TODO(crbug.com/407587751): Import bookmarks.
   PostCallback(std::move(bookmarks_callback), /*number_of_imports=*/0);
diff --git a/components/user_data_importer/utility/safari_data_importer.h b/components/user_data_importer/utility/safari_data_importer.h
index da53d2d..a9de06c 100644
--- a/components/user_data_importer/utility/safari_data_importer.h
+++ b/components/user_data_importer/utility/safari_data_importer.h
@@ -5,12 +5,22 @@
 #ifndef COMPONENTS_USER_DATA_IMPORTER_UTILITY_SAFARI_DATA_IMPORTER_H_
 #define COMPONENTS_USER_DATA_IMPORTER_UTILITY_SAFARI_DATA_IMPORTER_H_
 
+#include "base/files/scoped_temp_file.h"
 #include "base/memory/weak_ptr.h"
 #include "base/task/sequenced_task_runner.h"
 #include "components/password_manager/core/browser/import/password_importer.h"
 
 namespace user_data_importer {
 
+class SafariDataImportManager;
+
+// Main model-layer object for extracting and importing user data from a bundle
+// of data exported by Safari. The bundle is a ZIP file containing various data
+// types in individual files, the format of which is documented here:
+// https://developer.apple.com/documentation/safariservices/importing-data-exported-from-safari?language=objc
+// Users of this class must also provide an object implementing the
+// `SafariDataImportManager` interface, which abstracts out certain logic which
+// can't live in the components layer (because of platform dependencies).
 class SafariDataImporter {
  public:
   // A callback used to obtain the number of successfully imported bookmarks,
@@ -21,7 +31,8 @@
       password_manager::PasswordImporter::ImportResultsCallback;
   using PasswordImportResults = password_manager::ImportResults;
 
-  SafariDataImporter(password_manager::SavedPasswordsPresenter* presenter);
+  SafariDataImporter(password_manager::SavedPasswordsPresenter* presenter,
+                     std::unique_ptr<SafariDataImportManager> manager);
   ~SafariDataImporter();
 
   // Attempts to import various data types (passwords, payment cards, bookmarks
@@ -60,7 +71,7 @@
 
   // Attempts to import bookmarks by parsing the provided HTML data.
   // Calls "bookmarks_callback" when done.
-  void ImportBookmarks(std::string html_data,
+  void ImportBookmarks(base::ScopedTempFile&& bookmarks_html,
                        ImportCallback bookmarks_callback);
 
   // Calls "history_callback" with an approximation of the number of URLs
@@ -108,6 +119,10 @@
   // for example.
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
+  // Encapsulates model-layer logic that has to be injected (e.g.,
+  // platform-specific logic).
+  std::unique_ptr<SafariDataImportManager> manager_;
+
   // This is necessary because this object could be deleted during any callback,
   // and we don't want to risk a UAF if that happens.
   base::WeakPtrFactory<SafariDataImporter> weak_factory_{this};
diff --git a/components/user_data_importer/utility/safari_data_importer_unittest.cc b/components/user_data_importer/utility/safari_data_importer_unittest.cc
index 5cf0f61..8345245e9 100644
--- a/components/user_data_importer/utility/safari_data_importer_unittest.cc
+++ b/components/user_data_importer/utility/safari_data_importer_unittest.cc
@@ -23,6 +23,7 @@
 #include "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
 #include "components/password_manager/core/common/password_manager_constants.h"
 #include "components/password_manager/services/csv_password/fake_password_parser_service.h"
+#include "components/user_data_importer/utility/safari_data_import_manager.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -31,9 +32,22 @@
 
 namespace user_data_importer {
 
+class TestSafariDataImportManager : public SafariDataImportManager {
+ public:
+  TestSafariDataImportManager() = default;
+  ~TestSafariDataImportManager() override = default;
+
+  void ParseBookmarks(
+      const base::FilePath& bookmarks_html,
+      base::OnceCallback<void(BookmarkParsingResult)> callback) override {}
+};
+
 class SafariDataImporterTest : public testing::Test {
  public:
-  SafariDataImporterTest() : receiver_{&service_}, importer_(&presenter_) {
+  SafariDataImporterTest()
+      : receiver_{&service_},
+        importer_(&presenter_,
+                  std::make_unique<TestSafariDataImportManager>()) {
     mojo::PendingRemote<password_manager::mojom::CSVPasswordParser>
         pending_remote{receiver_.BindNewPipeAndPassRemote()};
     importer_.password_importer_->SetServiceForTesting(
@@ -103,8 +117,11 @@
 
   void ImportBookmarks(std::string html_data) {
     bookmarks_callback_called_ = false;
+    base::ScopedTempFile file;
+    ASSERT_TRUE(file.Create());
+    base::WriteFile(file.path(), html_data);
     importer_.ImportBookmarks(
-        std::move(html_data),
+        std::move(file),
         // Use of Unretained below is safe because the RunUntil loop below
         // guarantees this outlives the tasks.
         base::BindOnce(&SafariDataImporterTest::OnBookmarksConsumed,
diff --git a/components/webapps/browser/android/webapk/webapk_single_icon_hasher_unittest.cc b/components/webapps/browser/android/webapk/webapk_single_icon_hasher_unittest.cc
index 50f0e84..d0544b2 100644
--- a/components/webapps/browser/android/webapk/webapk_single_icon_hasher_unittest.cc
+++ b/components/webapps/browser/android/webapk/webapk_single_icon_hasher_unittest.cc
@@ -6,7 +6,6 @@
 
 #include <string>
 
-#include "base/debug/stack_trace.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/functional/bind.h"
diff --git a/content/app/content_main.cc b/content/app/content_main.cc
index ffc5c37..36df9fa 100644
--- a/content/app/content_main.cc
+++ b/content/app/content_main.cc
@@ -18,7 +18,6 @@
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/debug/debugger.h"
-#include "base/debug/stack_trace.h"
 #include "base/feature_list.h"
 #include "base/i18n/icu_util.h"
 #include "base/logging.h"
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 9379bc7..575f837 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -3429,6 +3429,7 @@
       ":fontations_name_table_ffi",
       ":reflection_jni_headers",
       "//cc/slim",
+      "//components/origin_matcher",
       "//components/tracing:graphics_provider",
       "//content/public/android:browser_jni",
       "//content/public/browser:transfer_input_to_viz_result_enum",
diff --git a/content/browser/aggregation_service/aggregation_service_storage_sql.cc b/content/browser/aggregation_service/aggregation_service_storage_sql.cc
index 42604da..082d8b3e 100644
--- a/content/browser/aggregation_service/aggregation_service_storage_sql.cc
+++ b/content/browser/aggregation_service/aggregation_service_storage_sql.cc
@@ -526,7 +526,7 @@
       db_.GetCachedStatement(SQL_FROM_HERE, kUpdateRequestSql));
 
   update_request_statement.BindTime(0, new_report_time);
-  update_request_statement.BindBlob(1, serialized_proto);
+  update_request_statement.BindBlob(1, std::move(serialized_proto));
   update_request_statement.BindInt64(2, request_id.value());
 
   if (!update_request_statement.Run()) {
@@ -581,7 +581,7 @@
   // While an empty vector can be a valid proto serialization, report requests
   // should always be non-empty.
   CHECK(!serialized_request.empty());
-  store_request_statement.BindBlob(3, serialized_request);
+  store_request_statement.BindBlob(3, std::move(serialized_request));
 
   if (!store_request_statement.Run()) {
     return;
diff --git a/content/browser/android/java/gin_java_bridge_dispatcher_host.cc b/content/browser/android/java/gin_java_bridge_dispatcher_host.cc
index 1b51996..12f9d68 100644
--- a/content/browser/android/java/gin_java_bridge_dispatcher_host.cc
+++ b/content/browser/android/java/gin_java_bridge_dispatcher_host.cc
@@ -11,6 +11,7 @@
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "build/build_config.h"
+#include "components/origin_matcher/origin_matcher.h"
 #include "content/browser/android/java/gin_java_bound_object_delegate.h"
 #include "content/browser/android/java/java_bridge_thread.h"
 #include "content/browser/renderer_host/agent_scheduling_group_host.h"
@@ -22,7 +23,6 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_features.h"
-#include "net/base/scheme_host_port_matcher.h"
 
 #if !BUILDFLAG(IS_ANDROID)
 #error "JavaBridge only supports OS_ANDROID"
@@ -93,7 +93,7 @@
     // Initialize with all the current named objects.
     for (auto& object : named_objects_) {
       bound_remote->AddNamedObject(object.first, object.second.object_id,
-                                   object.second.allowlist_rules);
+                                   object.second.matcher);
     }
 
     return bound_remote.get();
@@ -232,7 +232,7 @@
     const std::string& name,
     const base::android::JavaRef<jobject>& object,
     const base::android::JavaRef<jclass>& safe_annotation_clazz,
-    net::SchemeHostPortMatcher matcher) {
+    origin_matcher::OriginMatcher matcher) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   GinJavaBoundObject::ObjectID object_id;
   NamedObjectMap::iterator iter = named_objects_.find(name);
@@ -257,8 +257,7 @@
   // in the render process. We pass this around like this because we can
   // then trust that all the rules being fed to the render process are well
   // formed rules.
-  // TODO(crbug.com/407420300): Rely on OriginMatcher instead of a string here.
-  named_objects_[name] = {object_id, matcher.ToString()};
+  named_objects_[name] = {object_id, matcher};
 
   web_contents()
       ->GetPrimaryMainFrame()
@@ -270,7 +269,7 @@
             }
 
             GetJavaBridge(render_frame_host, /*should_create=*/true)
-                ->AddNamedObject(name, object_id, matcher.ToString());
+                ->AddNamedObject(name, object_id, matcher);
           });
 }
 
diff --git a/content/browser/android/java/gin_java_bridge_dispatcher_host.h b/content/browser/android/java/gin_java_bridge_dispatcher_host.h
index e4636ed..a59058e1 100644
--- a/content/browser/android/java/gin_java_bridge_dispatcher_host.h
+++ b/content/browser/android/java/gin_java_bridge_dispatcher_host.h
@@ -17,6 +17,7 @@
 #include "base/synchronization/lock.h"
 #include "base/thread_annotations.h"
 #include "base/values.h"
+#include "components/origin_matcher/origin_matcher.h"
 #include "content/browser/android/java/gin_java_bound_object.h"
 #include "content/browser/android/java/gin_java_method_invocation_helper.h"
 #include "content/common/buildflags.h"
@@ -26,17 +27,13 @@
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 
-namespace net {
-class SchemeHostPortMatcher;
-}
-
 namespace content {
 
 class WebContentsImpl;
 
 struct NamedObject {
   GinJavaBoundObject::ObjectID object_id;
-  std::string allowlist_rules;
+  origin_matcher::OriginMatcher matcher;
 };
 
 // This class handles injecting Java objects into a single WebContents /
@@ -67,7 +64,7 @@
       const std::string& name,
       const base::android::JavaRef<jobject>& object,
       const base::android::JavaRef<jclass>& safe_annotation_clazz,
-      net::SchemeHostPortMatcher matcher);
+      origin_matcher::OriginMatcher matcher);
   void RemoveNamedObject(const std::string& name);
   void SetAllowObjectContentsInspection(bool allow);
 
diff --git a/content/browser/android/javascript_injector.cc b/content/browser/android/javascript_injector.cc
index 02003f3..34b5c0a 100644
--- a/content/browser/android/javascript_injector.cc
+++ b/content/browser/android/javascript_injector.cc
@@ -8,6 +8,7 @@
 
 #include "base/android/jni_string.h"
 #include "base/memory/ptr_util.h"
+#include "components/origin_matcher/origin_matcher.h"
 #include "content/browser/android/java/gin_java_bridge_dispatcher_host.h"
 #include "content/browser/preloading/prerender/prerender_final_status.h"
 #include "content/browser/preloading/prerender/prerender_host_registry.h"
@@ -50,32 +51,15 @@
   java_bridge_dispatcher_host_->SetAllowObjectContentsInspection(allow);
 }
 
-std::vector<std::string> JavascriptInjector::AddInterface(
+void JavascriptInjector::AddInterface(
     JNIEnv* env,
     const JavaParamRef<jobject>& /* obj */,
     const JavaParamRef<jobject>& object,
     const JavaParamRef<jstring>& name,
     const JavaParamRef<jclass>& safe_annotation_clazz,
-    std::vector<std::string> origin_allow_list_rules) {
+    origin_matcher::OriginMatcher matcher) {
   DCHECK(java_bridge_dispatcher_host_);
 
-  std::vector<std::string> bad_origins;
-  net::SchemeHostPortMatcher matcher;
-
-  for (const std::string& string_rule : origin_allow_list_rules) {
-    auto rule =
-        net::SchemeHostPortMatcherRule::FromUntrimmedRawString(string_rule);
-    if (rule) {
-      matcher.AddAsLastRule(std::move(rule));
-    } else {
-      bad_origins.emplace_back(string_rule);
-    }
-  }
-
-  if (!bad_origins.empty()) {
-    return bad_origins;
-  }
-
   // If a new js object is added or removed when a page is in BFCache or
   // prerendered, the change won't apply after activating the page. To avoid
   // this behavior difference when these features are involved vs not,
@@ -91,8 +75,6 @@
   java_bridge_dispatcher_host_->AddNamedObject(
       ConvertJavaStringToUTF8(env, name), object, safe_annotation_clazz,
       std::move(matcher));
-
-  return {};
 }
 
 void JavascriptInjector::RemoveInterface(JNIEnv* env,
diff --git a/content/browser/android/javascript_injector.h b/content/browser/android/javascript_injector.h
index d2b8ac6..d235786c 100644
--- a/content/browser/android/javascript_injector.h
+++ b/content/browser/android/javascript_injector.h
@@ -11,6 +11,7 @@
 #include "base/android/jni_android.h"
 #include "base/android/jni_weak_ref.h"
 #include "base/android/scoped_java_ref.h"
+#include "components/origin_matcher/origin_matcher.h"
 #include "content/common/gin_java_bridge.mojom-forward.h"
 #include "content/public/browser/web_contents_user_data.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -38,15 +39,13 @@
                           jboolean allow);
 
   // See GinJavaBridgeDispatcherHost::AddNamedObject more information.
-  // Note that an interface will not be injected if the origin allow list
-  // is ill-formed. Will return all bad origin rules.
-  std::vector<std::string> AddInterface(
+  void AddInterface(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& /* obj */,
       const base::android::JavaParamRef<jobject>& object,
       const base::android::JavaParamRef<jstring>& name,
       const base::android::JavaParamRef<jclass>& safe_annotation_clazz,
-      std::vector<std::string> origin_allow_list_rules);
+      origin_matcher::OriginMatcher matcher);
 
   void RemoveInterface(JNIEnv* env,
                        const base::android::JavaParamRef<jobject>& /* obj */,
diff --git a/content/browser/back_forward_cache_features_browsertest.cc b/content/browser/back_forward_cache_features_browsertest.cc
index c55128c..30ddcfa1 100644
--- a/content/browser/back_forward_cache_features_browsertest.cc
+++ b/content/browser/back_forward_cache_features_browsertest.cc
@@ -2834,8 +2834,11 @@
 // Verifies that transactions from a single client/render frame and a dedicated
 // worker belonging to the frame cannot disable BFCache for that client.
 // Regression test for https://crbug.com/343519262.
-IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
-                       IndexedDBClientWithDedicatedWorkerDoesntBlockSelf) {
+//
+// TODO(https://crbug.com/422753550): Reactivate test.
+IN_PROC_BROWSER_TEST_F(
+    BackForwardCacheBrowserTest,
+    DISABLED_IndexedDBClientWithDedicatedWorkerDoesntBlockSelf) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
   // 1) Use IDB and spam transactions.
diff --git a/content/browser/browsing_data/browsing_data_remover_impl.cc b/content/browser/browsing_data/browsing_data_remover_impl.cc
index 82d25ef2..977e3ab 100644
--- a/content/browser/browsing_data/browsing_data_remover_impl.cc
+++ b/content/browser/browsing_data/browsing_data_remover_impl.cc
@@ -442,9 +442,6 @@
     storage_partition_remove_mask |=
         StoragePartition::REMOVE_DATA_MASK_INDEXEDDB;
   }
-  if (remove_mask & DATA_TYPE_WEB_SQL) {
-    storage_partition_remove_mask |= StoragePartition::REMOVE_DATA_MASK_WEBSQL;
-  }
   if (remove_mask & DATA_TYPE_SERVICE_WORKERS) {
     storage_partition_remove_mask |=
         StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS;
diff --git a/content/browser/browsing_data/browsing_data_remover_impl_unittest.cc b/content/browser/browsing_data/browsing_data_remover_impl_unittest.cc
index a39451f8..d58d77a 100644
--- a/content/browser/browsing_data/browsing_data_remover_impl_unittest.cc
+++ b/content/browser/browsing_data/browsing_data_remover_impl_unittest.cc
@@ -776,14 +776,12 @@
   BlockUntilBrowsingDataRemoved(
       base::Time(), base::Time::Max(),
       BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-          BrowsingDataRemover::DATA_TYPE_WEB_SQL |
           BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
           BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
           BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
       false);
 
   EXPECT_EQ(BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-                BrowsingDataRemover::DATA_TYPE_WEB_SQL |
                 BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
                 BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
                 BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
@@ -795,7 +793,6 @@
   StoragePartitionRemovalData removal_data = GetStoragePartitionRemovalData();
   EXPECT_EQ(removal_data.remove_mask,
             StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS |
-                StoragePartition::REMOVE_DATA_MASK_WEBSQL |
                 StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS |
                 StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE |
                 StoragePartition::REMOVE_DATA_MASK_INDEXEDDB);
@@ -810,14 +807,12 @@
   BlockUntilBrowsingDataRemoved(
       base::Time(), base::Time::Max(),
       BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-          BrowsingDataRemover::DATA_TYPE_WEB_SQL |
           BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
           BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
           BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
       false);
 
   EXPECT_EQ(BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-                BrowsingDataRemover::DATA_TYPE_WEB_SQL |
                 BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
                 BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
                 BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
@@ -830,7 +825,6 @@
 
   EXPECT_EQ(removal_data.remove_mask,
             StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS |
-                StoragePartition::REMOVE_DATA_MASK_WEBSQL |
                 StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS |
                 StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE |
                 StoragePartition::REMOVE_DATA_MASK_INDEXEDDB);
@@ -860,14 +854,12 @@
   BlockUntilBrowsingDataRemoved(
       base::Time(), base::Time::Max(),
       BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-          BrowsingDataRemover::DATA_TYPE_WEB_SQL |
           BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
           BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
           BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
       false);
 
   EXPECT_EQ(BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-                BrowsingDataRemover::DATA_TYPE_WEB_SQL |
                 BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
                 BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
                 BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
@@ -880,7 +872,6 @@
 
   EXPECT_EQ(removal_data.remove_mask,
             StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS |
-                StoragePartition::REMOVE_DATA_MASK_WEBSQL |
                 StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS |
                 StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE |
                 StoragePartition::REMOVE_DATA_MASK_INDEXEDDB);
@@ -909,14 +900,12 @@
   BlockUntilBrowsingDataRemoved(
       base::Time(), base::Time::Max(),
       BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-          BrowsingDataRemover::DATA_TYPE_WEB_SQL |
           BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
           BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
           BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
       false);
 
   EXPECT_EQ(BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-                BrowsingDataRemover::DATA_TYPE_WEB_SQL |
                 BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
                 BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
                 BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
@@ -929,7 +918,6 @@
 
   EXPECT_EQ(removal_data.remove_mask,
             StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS |
-                StoragePartition::REMOVE_DATA_MASK_WEBSQL |
                 StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS |
                 StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE |
                 StoragePartition::REMOVE_DATA_MASK_INDEXEDDB);
@@ -964,15 +952,13 @@
                               BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
                                   BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
                                   BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-                                  BrowsingDataRemover::DATA_TYPE_INDEXED_DB |
-                                  BrowsingDataRemover::DATA_TYPE_WEB_SQL,
+                                  BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
                               std::move(builder));
 
   EXPECT_EQ(BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
                 BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
                 BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-                BrowsingDataRemover::DATA_TYPE_INDEXED_DB |
-                BrowsingDataRemover::DATA_TYPE_WEB_SQL,
+                BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
             GetRemovalMask());
   EXPECT_EQ(BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
             GetOriginTypeMask());
@@ -982,7 +968,6 @@
 
   EXPECT_EQ(removal_data.remove_mask,
             StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS |
-                StoragePartition::REMOVE_DATA_MASK_WEBSQL |
                 StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS |
                 StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE |
                 StoragePartition::REMOVE_DATA_MASK_INDEXEDDB);
@@ -1005,14 +990,12 @@
   BlockUntilBrowsingDataRemoved(
       AnHourAgo(), base::Time::Max(),
       BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-          BrowsingDataRemover::DATA_TYPE_WEB_SQL |
           BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
           BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
           BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
       false);
 
   EXPECT_EQ(BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-                BrowsingDataRemover::DATA_TYPE_WEB_SQL |
                 BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
                 BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
                 BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
@@ -1025,7 +1008,6 @@
 
   EXPECT_EQ(removal_data.remove_mask,
             StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS |
-                StoragePartition::REMOVE_DATA_MASK_WEBSQL |
                 StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS |
                 StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE |
                 StoragePartition::REMOVE_DATA_MASK_INDEXEDDB);
@@ -1040,14 +1022,12 @@
   BlockUntilBrowsingDataRemoved(
       base::Time::Now() - base::Days(7), base::Time::Max(),
       BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-          BrowsingDataRemover::DATA_TYPE_WEB_SQL |
           BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
           BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
           BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
       false);
 
   EXPECT_EQ(BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-                BrowsingDataRemover::DATA_TYPE_WEB_SQL |
                 BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
                 BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
                 BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
@@ -1060,7 +1040,6 @@
 
   EXPECT_EQ(removal_data.remove_mask,
             StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS |
-                StoragePartition::REMOVE_DATA_MASK_WEBSQL |
                 StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS |
                 StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE |
                 StoragePartition::REMOVE_DATA_MASK_INDEXEDDB);
@@ -1080,14 +1059,12 @@
   BlockUntilBrowsingDataRemoved(
       base::Time(), base::Time::Max(),
       BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-          BrowsingDataRemover::DATA_TYPE_WEB_SQL |
           BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
           BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
           BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
       false);
 
   EXPECT_EQ(BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-                BrowsingDataRemover::DATA_TYPE_WEB_SQL |
                 BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
                 BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
                 BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
@@ -1100,7 +1077,6 @@
 
   EXPECT_EQ(removal_data.remove_mask,
             StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS |
-                StoragePartition::REMOVE_DATA_MASK_WEBSQL |
                 StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS |
                 StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE |
                 StoragePartition::REMOVE_DATA_MASK_INDEXEDDB);
@@ -1137,15 +1113,13 @@
                               BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
                                   BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
                                   BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-                                  BrowsingDataRemover::DATA_TYPE_INDEXED_DB |
-                                  BrowsingDataRemover::DATA_TYPE_WEB_SQL,
+                                  BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
                               std::move(builder));
 
   EXPECT_EQ(BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
                 BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
                 BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-                BrowsingDataRemover::DATA_TYPE_INDEXED_DB |
-                BrowsingDataRemover::DATA_TYPE_WEB_SQL,
+                BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
             GetRemovalMask());
   EXPECT_EQ(BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
             GetOriginTypeMask());
@@ -1155,7 +1129,6 @@
 
   EXPECT_EQ(removal_data.remove_mask,
             StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS |
-                StoragePartition::REMOVE_DATA_MASK_WEBSQL |
                 StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS |
                 StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE |
                 StoragePartition::REMOVE_DATA_MASK_INDEXEDDB);
@@ -1192,15 +1165,13 @@
       BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
           BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
           BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-          BrowsingDataRemover::DATA_TYPE_INDEXED_DB |
-          BrowsingDataRemover::DATA_TYPE_WEB_SQL,
+          BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
       true);
 
   EXPECT_EQ(BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
                 BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
                 BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-                BrowsingDataRemover::DATA_TYPE_INDEXED_DB |
-                BrowsingDataRemover::DATA_TYPE_WEB_SQL,
+                BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
             GetRemovalMask());
   EXPECT_EQ(BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB |
                 BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
@@ -1211,7 +1182,6 @@
 
   EXPECT_EQ(removal_data.remove_mask,
             StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS |
-                StoragePartition::REMOVE_DATA_MASK_WEBSQL |
                 StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS |
                 StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE |
                 StoragePartition::REMOVE_DATA_MASK_INDEXEDDB);
@@ -1242,15 +1212,13 @@
       BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
           BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
           BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-          BrowsingDataRemover::DATA_TYPE_INDEXED_DB |
-          BrowsingDataRemover::DATA_TYPE_WEB_SQL,
+          BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
       false);
 
   EXPECT_EQ(BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
                 BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
                 BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-                BrowsingDataRemover::DATA_TYPE_INDEXED_DB |
-                BrowsingDataRemover::DATA_TYPE_WEB_SQL,
+                BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
             GetRemovalMask());
   EXPECT_EQ(BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
             GetOriginTypeMask());
@@ -1260,7 +1228,6 @@
 
   EXPECT_EQ(removal_data.remove_mask,
             StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS |
-                StoragePartition::REMOVE_DATA_MASK_WEBSQL |
                 StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS |
                 StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE |
                 StoragePartition::REMOVE_DATA_MASK_INDEXEDDB);
@@ -1537,10 +1504,6 @@
                      BrowsingDataFilterBuilder::Create(
                          BrowsingDataFilterBuilder::Mode::kPreserve),
                      observer.target_b());
-  tasks.emplace_back(base::Time(), base::Time::UnixEpoch(),
-                     BrowsingDataRemover::DATA_TYPE_WEB_SQL,
-                     BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
-                     std::move(filter_builder_1), observer.target_b());
 
   for (BrowsingDataRemoverImpl::RemovalTask& task : tasks) {
     // All tasks can be directly translated to a RemoveInternal() call. Since
@@ -1786,7 +1749,6 @@
   uint32_t dom_storage_mask =
       StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE |
       StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS |
-      StoragePartition::REMOVE_DATA_MASK_WEBSQL |
       StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS |
       StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE |
       StoragePartition::REMOVE_DATA_MASK_BACKGROUND_FETCH |
@@ -1927,15 +1889,13 @@
                               BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
                                   BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
                                   BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-                                  BrowsingDataRemover::DATA_TYPE_INDEXED_DB |
-                                  BrowsingDataRemover::DATA_TYPE_WEB_SQL,
+                                  BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
                               std::move(builder));
 
   EXPECT_EQ(BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS |
                 BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE |
                 BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS |
-                BrowsingDataRemover::DATA_TYPE_INDEXED_DB |
-                BrowsingDataRemover::DATA_TYPE_WEB_SQL,
+                BrowsingDataRemover::DATA_TYPE_INDEXED_DB,
             GetRemovalMask());
   EXPECT_EQ(BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
             GetOriginTypeMask());
@@ -1952,7 +1912,6 @@
   StoragePartitionRemovalData removal_data = all_removal_data.back();
   EXPECT_EQ(removal_data.remove_mask,
             StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS |
-                StoragePartition::REMOVE_DATA_MASK_WEBSQL |
                 StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS |
                 StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE |
                 StoragePartition::REMOVE_DATA_MASK_INDEXEDDB);
diff --git a/content/browser/devtools/devtools_session.cc b/content/browser/devtools/devtools_session.cc
index 09a71ffb..e1237821 100644
--- a/content/browser/devtools/devtools_session.cc
+++ b/content/browser/devtools/devtools_session.cc
@@ -9,7 +9,6 @@
 
 #include "base/containers/contains.h"
 #include "base/containers/flat_set.h"
-#include "base/debug/stack_trace.h"
 #include "base/functional/bind.h"
 #include "base/trace_event/trace_event.h"
 #include "content/browser/devtools/devtools_manager.h"
diff --git a/content/browser/devtools/protocol/storage_handler.cc b/content/browser/devtools/protocol/storage_handler.cc
index db22e13..7178f79f 100644
--- a/content/browser/devtools/protocol/storage_handler.cc
+++ b/content/browser/devtools/protocol/storage_handler.cc
@@ -100,7 +100,6 @@
 UsageListInitializer initializers[] = {
     {Storage::StorageTypeEnum::File_systems,
      &blink::mojom::UsageBreakdown::fileSystem},
-    {Storage::StorageTypeEnum::Websql, &blink::mojom::UsageBreakdown::webSql},
     {Storage::StorageTypeEnum::Indexeddb,
      &blink::mojom::UsageBreakdown::indexedDatabase},
     {Storage::StorageTypeEnum::Cache_storage,
@@ -579,9 +578,6 @@
   if (set.count(Storage::StorageTypeEnum::Shader_cache)) {
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE;
   }
-  if (set.count(Storage::StorageTypeEnum::Websql)) {
-    remove_mask |= StoragePartition::REMOVE_DATA_MASK_WEBSQL;
-  }
   if (set.count(Storage::StorageTypeEnum::Service_workers)) {
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS;
   }
diff --git a/content/browser/dom_storage/dom_storage_context_wrapper.cc b/content/browser/dom_storage/dom_storage_context_wrapper.cc
index 47f693f..13b335a1 100644
--- a/content/browser/dom_storage/dom_storage_context_wrapper.cc
+++ b/content/browser/dom_storage/dom_storage_context_wrapper.cc
@@ -43,9 +43,9 @@
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 
 namespace content {
-#if BUILDFLAG(IS_MAC)
+
 using LocalStorageLifecycle = storage::mojom::LocalStorageLifecycle;
-#endif  // BUILDFLAG(IS_MAC)
+
 namespace {
 
 void AdaptSessionStorageUsageInfo(
@@ -74,7 +74,6 @@
   std::move(callback).Run(result);
 }
 
-#if BUILDFLAG(IS_MAC)
 LocalStorageLifecycle GetLocalStorageLifecycle(
     bool recovering,
     bool storage_service_remote_was_bound) {
@@ -88,7 +87,6 @@
                : LocalStorageLifecycle::kInitializingWithUnboundStorageService;
   }
 }
-#endif  // BUILDFLAG(IS_MAC)
 
 }  // namespace
 
@@ -118,18 +116,12 @@
       base::BindRepeating(&DOMStorageContextWrapper::OnMemoryPressure,
                           base::Unretained(this)));
 
-#if BUILDFLAG(IS_MAC)
   // Binding Session or Local storage will result in the storage service getting
   // bound. So, we capture this state before those calls.
   LocalStorageLifecycle lifecycle = GetLocalStorageLifecycle(
       /*recovering=*/false, partition_->IsStorageServiceRemoteValid());
-#endif  // BUILDFLAG(IS_MAC)
   MaybeBindSessionStorageControl();
-#if BUILDFLAG(IS_MAC)
-  MaybeBindLocalStorageControlAndReportLifecycle(lifecycle);
-#else
-  MaybeBindLocalStorageControl();
-#endif  // BUILDFLAG(IS_MAC)
+  MaybeBindLocalStorageControl(lifecycle);
 }
 
 DOMStorageContextWrapper::~DOMStorageContextWrapper() {
@@ -359,18 +351,12 @@
 
 void DOMStorageContextWrapper::RecoverFromStorageServiceCrash() {
   DCHECK(partition_);
-#if BUILDFLAG(IS_MAC)
   // Binding Session or Local storage will result in the storage service getting
   // bound. So, we capture this state before those calls.
   LocalStorageLifecycle lifecycle = GetLocalStorageLifecycle(
       /*recovering=*/true, partition_->IsStorageServiceRemoteValid());
-#endif  // BUILDFLAG(IS_MAC)
   MaybeBindSessionStorageControl();
-#if BUILDFLAG(IS_MAC)
-  MaybeBindLocalStorageControlAndReportLifecycle(lifecycle);
-#else
-  MaybeBindLocalStorageControl();
-#endif  // BUILDFLAG(IS_MAC)
+  MaybeBindLocalStorageControl(lifecycle);
 
   // Make sure the service is aware of namespaces we asked a previous instance
   // to create, so it can properly service renderers trying to manipulate those
@@ -389,26 +375,15 @@
       session_storage_control_.BindNewPipeAndPassReceiver());
 }
 
-void DOMStorageContextWrapper::MaybeBindLocalStorageControl() {
-  if (!partition_)
-    return;
-  local_storage_control_.reset();
-  partition_->GetStorageServicePartition()->BindLocalStorageControl(
-      local_storage_control_.BindNewPipeAndPassReceiver());
-}
-
-#if BUILDFLAG(IS_MAC)
-void DOMStorageContextWrapper::MaybeBindLocalStorageControlAndReportLifecycle(
+void DOMStorageContextWrapper::MaybeBindLocalStorageControl(
     LocalStorageLifecycle lifecycle) {
   if (!partition_) {
     return;
   }
   local_storage_control_.reset();
-  partition_->GetStorageServicePartition()
-      ->BindLocalStorageControlAndReportLifecycle(
-          lifecycle, local_storage_control_.BindNewPipeAndPassReceiver());
+  partition_->GetStorageServicePartition()->BindLocalStorageControl(
+      lifecycle, local_storage_control_.BindNewPipeAndPassReceiver());
 }
-#endif  // BUILDFLAG(IS_MAC)
 
 scoped_refptr<SessionStorageNamespaceImpl>
 DOMStorageContextWrapper::MaybeGetExistingNamespace(
diff --git a/content/browser/dom_storage/dom_storage_context_wrapper.h b/content/browser/dom_storage/dom_storage_context_wrapper.h
index 6289ed4..e757b50 100644
--- a/content/browser/dom_storage/dom_storage_context_wrapper.h
+++ b/content/browser/dom_storage/dom_storage_context_wrapper.h
@@ -141,11 +141,8 @@
   ~DOMStorageContextWrapper() override;
 
   void MaybeBindSessionStorageControl();
-  void MaybeBindLocalStorageControl();
-#if BUILDFLAG(IS_MAC)
-  void MaybeBindLocalStorageControlAndReportLifecycle(
+  void MaybeBindLocalStorageControl(
       storage::mojom::LocalStorageLifecycle lifecycle);
-#endif  // BUILDFLAG(IS_MAC)
   scoped_refptr<SessionStorageNamespaceImpl> MaybeGetExistingNamespace(
       const std::string& namespace_id) const;
 
diff --git a/content/browser/hid/hid_service.cc b/content/browser/hid/hid_service.cc
index 5c153a6..9334efaa 100644
--- a/content/browser/hid/hid_service.cc
+++ b/content/browser/hid/hid_service.cc
@@ -9,7 +9,6 @@
 #include <utility>
 
 #include "base/containers/contains.h"
-#include "base/debug/stack_trace.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/functional/callback_helpers.h"
diff --git a/content/browser/indexed_db/instance/sqlite/database_connection.cc b/content/browser/indexed_db/instance/sqlite/database_connection.cc
index 6b94c06..effa745 100644
--- a/content/browser/indexed_db/instance/sqlite/database_connection.cc
+++ b/content/browser/indexed_db/instance/sqlite/database_connection.cc
@@ -365,7 +365,7 @@
   std::string encoded_key;
   EncodeSortableIDBKey(key, &encoded_key);
   statement.BindInt64(0, object_store_id);
-  statement.BindBlob(1, encoded_key);
+  statement.BindBlob(1, std::move(encoded_key));
   if (statement.Step()) {
     return BackingStore::RecordIdentifier{statement.ColumnInt64(0)};
   }
@@ -384,7 +384,7 @@
   std::string encoded_key;
   EncodeSortableIDBKey(key, &encoded_key);
   statement.BindInt64(0, object_store_id);
-  statement.BindBlob(1, encoded_key);
+  statement.BindBlob(1, std::move(encoded_key));
   if (statement.Step()) {
     IndexedDBValue value;
     TRANSIENT_CHECK(statement.ColumnBlobAsVector(0, &value.bits));
@@ -406,10 +406,8 @@
   statement.BindInt64(0, object_store_id);
   std::string encoded_key;
   EncodeSortableIDBKey(key, &encoded_key);
-  // TODO(crbug.com/40253999): `move` these into `statement` when
-  // crbug.com/419806592 is fixed.
-  statement.BindBlob(1, encoded_key);
-  statement.BindBlob(2, value.bits);
+  statement.BindBlob(1, std::move(encoded_key));
+  statement.BindBlob(2, std::move(value.bits));
   TRANSIENT_CHECK(statement.Run());
   return BackingStore::RecordIdentifier{db_->GetLastInsertRowId()};
 }
diff --git a/content/browser/interest_group/auction_process_manager.cc b/content/browser/interest_group/auction_process_manager.cc
index ea936b0..866af04a6 100644
--- a/content/browser/interest_group/auction_process_manager.cc
+++ b/content/browser/interest_group/auction_process_manager.cc
@@ -8,7 +8,6 @@
 #include <string>
 
 #include "base/check.h"
-#include "base/debug/stack_trace.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/functional/callback_helpers.h"
diff --git a/content/browser/interest_group/interest_group_storage.cc b/content/browser/interest_group/interest_group_storage.cc
index 3f0f615..99513f2 100644
--- a/content/browser/interest_group/interest_group_storage.cc
+++ b/content/browser/interest_group/interest_group_storage.cc
@@ -6460,7 +6460,7 @@
 
 bool DoSetBiddingAndAuctionServerKeys(sql::Database& db,
                                       const url::Origin& coordinator,
-                                      std::string_view serialized_keys,
+                                      std::string serialized_keys,
                                       base::Time expiration) {
   sql::Statement insert_keys_statement(db.GetCachedStatement(
       SQL_FROM_HERE,
@@ -6476,7 +6476,7 @@
   insert_keys_statement.Reset(true);
   insert_keys_statement.BindString(0, Serialize(coordinator));
 
-  insert_keys_statement.BindBlob(1, serialized_keys);
+  insert_keys_statement.BindBlob(1, std::move(serialized_keys));
   insert_keys_statement.BindTime(2, expiration);
   return insert_keys_statement.Run();
 }
@@ -7460,14 +7460,14 @@
 
 void InterestGroupStorage::SetBiddingAndAuctionServerKeys(
     const url::Origin& coordinator,
-    std::string_view serialized_keys,
+    std::string serialized_keys,
     base::Time expiration) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!EnsureDBInitialized()) {
     return;
   }
-  DoSetBiddingAndAuctionServerKeys(*db_, coordinator, serialized_keys,
-                                   expiration);
+  DoSetBiddingAndAuctionServerKeys(*db_, coordinator,
+                                   std::move(serialized_keys), expiration);
 }
 
 std::pair<base::Time, std::string>
diff --git a/content/browser/interest_group/interest_group_storage.h b/content/browser/interest_group/interest_group_storage.h
index 023bf413..2a5665eb 100644
--- a/content/browser/interest_group/interest_group_storage.h
+++ b/content/browser/interest_group/interest_group_storage.h
@@ -219,7 +219,7 @@
   // Update B&A keys for a coordinator. This function will overwrite any
   // existing keys for the coordinator.
   void SetBiddingAndAuctionServerKeys(const url::Origin& coordinator,
-                                      std::string_view serialized_keys,
+                                      std::string serialized_keys,
                                       base::Time expiration);
   // Load stored B&A server keys for a coordinator along with the keys'
   // expiration.
diff --git a/content/browser/media/cdm_registry_impl_unittest.cc b/content/browser/media/cdm_registry_impl_unittest.cc
index a578cba8..81df86c 100644
--- a/content/browser/media/cdm_registry_impl_unittest.cc
+++ b/content/browser/media/cdm_registry_impl_unittest.cc
@@ -248,9 +248,9 @@
 #if BUILDFLAG(IS_ANDROID)
   // On Android checking for key system support can be run on a separate
   // thread. Disable this for testing.
-  void DisableMediaCodecCallsInSeparateThread() {
+  void DisableMediaDrmQueryInSeparateProcess() {
     scoped_feature_list_.InitAndDisableFeature(
-        media::kAllowMediaCodecCallsInSeparateProcess);
+        media::kMediaDrmQueryInSeparateProcess);
   }
 #endif
 
@@ -1072,7 +1072,7 @@
 
 TEST_F(CdmRegistryImplTest, KeySystemCapabilities_NoOverride) {
 #if BUILDFLAG(IS_ANDROID)
-  DisableMediaCodecCallsInSeparateThread();
+  DisableMediaDrmQueryInSeparateProcess();
 #endif
 
   // kTestKeySystem doesn't exist on any platform, but this should at least
diff --git a/content/browser/media/key_system_support_android.cc b/content/browser/media/key_system_support_android.cc
index a7c427b9..e0ff5dd 100644
--- a/content/browser/media/key_system_support_android.cc
+++ b/content/browser/media/key_system_support_android.cc
@@ -300,8 +300,7 @@
   // Calls to MediaDrm.isCryptoSchemeSupported() are known to crash
   // (see b/308692917), so calling them via a utility process to avoid
   // crashing the browser if allowed.
-  if (base::FeatureList::IsEnabled(
-          media::kAllowMediaCodecCallsInSeparateProcess)) {
+  if (base::FeatureList::IsEnabled(media::kMediaDrmQueryInSeparateProcess)) {
     // The class CheckCdmCompatibility will manage it's own lifetime
     // (destruct after calling `cdm_capability_cb`).
     auto* check_cdm_compatibility = new CheckCdmCompatibility(
diff --git a/content/browser/media/key_system_support_android_unittest.cc b/content/browser/media/key_system_support_android_unittest.cc
index 362753a..fbb2596 100644
--- a/content/browser/media/key_system_support_android_unittest.cc
+++ b/content/browser/media/key_system_support_android_unittest.cc
@@ -28,7 +28,7 @@
   base::test::SingleThreadTaskEnvironment task_environment;
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitWithFeatureStates(
-      {{media::kAllowMediaCodecCallsInSeparateProcess, false},
+      {{media::kMediaDrmQueryInSeparateProcess, false},
        {media::kUseSecurityLevelWhenCheckingMediaDrmVersion, false}});
 
   base::test::TestFuture<media::CdmCapabilityOrStatus> capability;
@@ -47,7 +47,7 @@
   base::test::SingleThreadTaskEnvironment task_environment;
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitWithFeatureStates(
-      {{media::kAllowMediaCodecCallsInSeparateProcess, false},
+      {{media::kMediaDrmQueryInSeparateProcess, false},
        {media::kUseSecurityLevelWhenCheckingMediaDrmVersion, false}});
 
   base::test::TestFuture<media::CdmCapabilityOrStatus> capability;
@@ -65,7 +65,7 @@
   base::test::SingleThreadTaskEnvironment task_environment;
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitWithFeatureStates(
-      {{media::kAllowMediaCodecCallsInSeparateProcess, false},
+      {{media::kMediaDrmQueryInSeparateProcess, false},
        {media::kUseSecurityLevelWhenCheckingMediaDrmVersion, false}});
 
   base::test::TestFuture<media::CdmCapabilityOrStatus> capability;
diff --git a/content/browser/preloading/prefetch/prefetch_response_reader.cc b/content/browser/preloading/prefetch/prefetch_response_reader.cc
index 689e2cd6..dad8507 100644
--- a/content/browser/preloading/prefetch/prefetch_response_reader.cc
+++ b/content/browser/preloading/prefetch/prefetch_response_reader.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 
+#include "base/debug/alias.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/notreached.h"
 #include "base/strings/string_util.h"
diff --git a/content/browser/resources/traces_internals/OWNERS b/content/browser/resources/traces_internals/OWNERS
index b7583fb..3fe23e3 100644
--- a/content/browser/resources/traces_internals/OWNERS
+++ b/content/browser/resources/traces_internals/OWNERS
@@ -1 +1 @@
-file://content/browser/tracing/trace_report/OWNERS
\ No newline at end of file
+file://content/browser/tracing/traces_internals/OWNERS
\ No newline at end of file
diff --git a/content/browser/resources/traces_internals/tracing_scenarios_config.html.ts b/content/browser/resources/traces_internals/tracing_scenarios_config.html.ts
index 859e1aa..33f4eb5 100644
--- a/content/browser/resources/traces_internals/tracing_scenarios_config.html.ts
+++ b/content/browser/resources/traces_internals/tracing_scenarios_config.html.ts
@@ -13,7 +13,7 @@
   }
 
   return html`
-    <h2>Local Scenarios</h2>
+  <h2>Local Scenarios</h2>
   <div class="scenario-list-container">
   ${this.localConfig_.map((item) => html`
     <tracing-scenario
@@ -21,8 +21,8 @@
         .enabled="${this.isScenarioEnabled_(item)}"
         @value-changed="${this.valueDidChange_}"
         data-key="${item.scenarioName}">
-      </tracing-scenario>`)}
-    </div>`;
+    </tracing-scenario>`)}
+  </div>`;
   // clang-format on
 }
 
@@ -33,15 +33,16 @@
   }
 
   return html`
-    <div class="scenario-list-container">
-    ${this.fieldConfig_.map((item, index) => html`
-      <tracing-scenario
-          .scenario="${item}"
-          .enabled="${item.isEnabled}"
-          data-index="${index}">
-      </tracing-scenario>
-    `)}
-    </div>`;
+  <h2>Field Scenarios</h2>
+  <div class="scenario-list-container">
+  ${this.fieldConfig_.map((item, index) => html`
+    <tracing-scenario
+        .scenario="${item}"
+        .enabled="${item.isEnabled}"
+        data-index="${index}">
+    </tracing-scenario>
+  `)}
+  </div>`;
   // clang-format on
 }
 
@@ -116,8 +117,8 @@
       @change="${this.onAddConfig_}">
   </input>
   ${this.isLoading_ ? html`<div class="spinner"></div>` : html`
-  ${getPresetConfigHtml.bind(this)()}
   ${getFieldConfigHtml.bind(this)()}
+  ${getPresetConfigHtml.bind(this)()}
   <div class="action-panel">
     <cr-button class="action-button"
         @click="${this.resetAllClick_}">
diff --git a/content/browser/service_worker/service_worker_client.cc b/content/browser/service_worker/service_worker_client.cc
index feecdd8..fa91cc7 100644
--- a/content/browser/service_worker/service_worker_client.cc
+++ b/content/browser/service_worker/service_worker_client.cc
@@ -10,6 +10,7 @@
 #include "base/check_is_test.h"
 #include "base/containers/adapters.h"
 #include "base/containers/contains.h"
+#include "base/debug/alias.h"
 #include "base/debug/crash_logging.h"
 #include "base/functional/overloaded.h"
 #include "base/notreached.h"
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 41d98d8..0a6afe30 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -2485,11 +2485,9 @@
       top_frame_origin, std::move(event_record));
 }
 
-#if BUILDFLAG(IS_MAC)
 bool StoragePartitionImpl::IsStorageServiceRemoteValid() const {
   return GetStorageServiceRemoteStorage().is_bound();
 }
-#endif  // BUILDFLAG(IS_MAC)
 
 void StoragePartitionImpl::Clone(
     mojo::PendingReceiver<network::mojom::URLLoaderNetworkServiceObserver>
@@ -3060,7 +3058,6 @@
 #endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
 
   if (remove_mask_ & REMOVE_DATA_MASK_INDEXEDDB ||
-      remove_mask_ & REMOVE_DATA_MASK_WEBSQL ||
       remove_mask_ & REMOVE_DATA_MASK_FILE_SYSTEMS ||
       remove_mask_ & REMOVE_DATA_MASK_SERVICE_WORKERS ||
       remove_mask_ & REMOVE_DATA_MASK_CACHE_STORAGE) {
diff --git a/content/browser/storage_partition_impl.h b/content/browser/storage_partition_impl.h
index 03bbb73..0c7c75e 100644
--- a/content/browser/storage_partition_impl.h
+++ b/content/browser/storage_partition_impl.h
@@ -407,9 +407,7 @@
     return shared_storage_header_observer_.get();
   }
 
-#if BUILDFLAG(IS_MAC)
   bool IsStorageServiceRemoteValid() const;
-#endif  // BUILDFLAG(IS_MAC)
 
   // Can return nullptr while `this` is being destroyed.
   BrowserContext* browser_context() const;
diff --git a/content/browser/storage_partition_impl_unittest.cc b/content/browser/storage_partition_impl_unittest.cc
index c8faee42..4d97ef6f 100644
--- a/content/browser/storage_partition_impl_unittest.cc
+++ b/content/browser/storage_partition_impl_unittest.cc
@@ -139,8 +139,7 @@
 
 const uint32_t kAllQuotaRemoveMask =
     StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS |
-    StoragePartition::REMOVE_DATA_MASK_INDEXEDDB |
-    StoragePartition::REMOVE_DATA_MASK_WEBSQL;
+    StoragePartition::REMOVE_DATA_MASK_INDEXEDDB;
 class RemoveCookieTester {
  public:
   explicit RemoveCookieTester(StoragePartition* storage_partition)
@@ -1979,8 +1978,7 @@
 
 TEST_F(StoragePartitionImplTest, DataRemovalObserver) {
   const uint32_t kTestClearMask =
-      content::StoragePartition::REMOVE_DATA_MASK_INDEXEDDB |
-      content::StoragePartition::REMOVE_DATA_MASK_WEBSQL;
+      content::StoragePartition::REMOVE_DATA_MASK_INDEXEDDB;
   const uint32_t kTestQuotaClearMask = 0;
   const auto kTestOrigin = GURL("https://example.com");
   const auto kBeginTime = base::Time() + base::Hours(1);
@@ -2317,6 +2315,7 @@
     remote_service->BindPartition(
         temp_dir.GetPath(), persistent_partition.BindNewPipeAndPassReceiver());
     persistent_partition->BindLocalStorageControl(
+        storage::mojom::LocalStorageLifecycle::kInitializing,
         storage_control.BindNewPipeAndPassReceiver());
     storage_control.FlushForTesting();
   }
diff --git a/content/browser/tracing/traces_internals/DEPS b/content/browser/tracing/traces_internals/DEPS
new file mode 100644
index 0000000..cf299dc5
--- /dev/null
+++ b/content/browser/tracing/traces_internals/DEPS
@@ -0,0 +1,5 @@
+specific_include_rules = {
+  "traces_internals_handler\.cc": [
+    "+v8"
+  ]
+}
\ No newline at end of file
diff --git a/content/browser/tracing/traces_internals/traces_internals.mojom b/content/browser/tracing/traces_internals/traces_internals.mojom
index c791ac6..9051752 100644
--- a/content/browser/tracing/traces_internals/traces_internals.mojom
+++ b/content/browser/tracing/traces_internals/traces_internals.mojom
@@ -87,6 +87,13 @@
   TracingScenarioState current_state;
 };
 
+struct TraceCategory {
+  string name;
+  bool is_group;
+  string description;
+  array<string> tags;
+};
+
 // Used by the WebUI page to bootstrap bidirectional communication.
 interface TracesInternalsHandlerFactory {
   // The WebUI calls this method when the page is first initialized.
@@ -110,6 +117,9 @@
   // payload and return it via Page::OnTraceComplete().
   StopTraceSession() => (bool success);
 
+  // Return the list of the available tracing categories.
+  GetTrackEventCategories() => (array<TraceCategory> categories);
+
   // Returns buffer usage stats for the current tracing session. PageHandler
   // supports only one request at a time.
   GetBufferUsage()
diff --git a/content/browser/tracing/traces_internals/traces_internals_handler.cc b/content/browser/tracing/traces_internals/traces_internals_handler.cc
index ae57abb..22ea0347 100644
--- a/content/browser/tracing/traces_internals/traces_internals_handler.cc
+++ b/content/browser/tracing/traces_internals/traces_internals_handler.cc
@@ -24,6 +24,8 @@
 #include "services/tracing/public/cpp/perfetto/perfetto_session.h"
 #include "third_party/perfetto/protos/perfetto/config/trace_config.gen.h"
 #include "third_party/snappy/src/snappy.h"
+#include "third_party/webrtc_overrides/init_webrtc.h"
+#include "v8/include/v8-trace-categories.h"
 
 #if BUILDFLAG(IS_WIN)
 #include "base/functional/bind.h"
@@ -99,6 +101,29 @@
   ~TraceReader() = default;
 };
 
+void AddCategoriesToList(
+    const perfetto::internal::TrackEventCategoryRegistry& registry,
+    std::vector<traces_internals::mojom::TraceCategoryPtr>& categories) {
+  for (size_t i = 0; i < registry.category_count(); ++i) {
+    const auto* category = registry.GetCategory(i);
+    auto mojom_category = traces_internals::mojom::TraceCategory::New();
+    mojom_category->name = category->name;
+    mojom_category->is_group = category->IsGroup();
+    if (category->description) {
+      mojom_category->description = category->description;
+    }
+    std::vector<std::string> tags_vector;
+    for (const char* tag : category->tags) {
+      if (!tag) {
+        break;
+      }
+      tags_vector.push_back(tag);
+    }
+    mojom_category->tags = std::move(tags_vector);
+    categories.push_back(std::move(mojom_category));
+  }
+}
+
 }  // namespace
 
 TracesInternalsHandler::TracesInternalsHandler(
@@ -239,6 +264,18 @@
   tracing_session_->Stop();
 }
 
+void TracesInternalsHandler::GetTrackEventCategories(
+    GetTrackEventCategoriesCallback callback) {
+  std::vector<traces_internals::mojom::TraceCategoryPtr> categories;
+
+  AddCategoriesToList(base::perfetto_track_event::internal::kCategoryRegistry,
+                      categories);
+  AddCategoriesToList(v8::GetTrackEventCategoryRegistry(), categories);
+  AddCategoriesToList(GetWebRtcTrackEventCategoryRegistry(), categories);
+
+  std::move(callback).Run(std::move(categories));
+}
+
 void TracesInternalsHandler::GetBufferUsage(GetBufferUsageCallback callback) {
   if (!tracing_session_ || on_buffer_usage_callback_) {
     std::move(callback).Run(false, 0, false);
diff --git a/content/browser/tracing/traces_internals/traces_internals_handler.h b/content/browser/tracing/traces_internals/traces_internals_handler.h
index d0694555..77320be9 100644
--- a/content/browser/tracing/traces_internals/traces_internals_handler.h
+++ b/content/browser/tracing/traces_internals/traces_internals_handler.h
@@ -39,6 +39,8 @@
                          StartTraceSessionCallback callback) override;
   void CloneTraceSession(CloneTraceSessionCallback callback) override;
   void StopTraceSession(StopTraceSessionCallback callback) override;
+  void GetTrackEventCategories(
+      GetTrackEventCategoriesCallback callback) override;
   void GetBufferUsage(GetBufferUsageCallback callback) override;
   void GetAllTraceReports(GetAllTraceReportsCallback callback) override;
   void DeleteSingleTrace(const base::Token& uuid,
@@ -106,6 +108,7 @@
   std::unique_ptr<perfetto::TracingSession> tracing_session_;
   StartTraceSessionCallback start_callback_;
   StopTraceSessionCallback stop_callback_;
+  GetTrackEventCategoriesCallback get_track_event_categories_callback_;
   GetBufferUsageCallback on_buffer_usage_callback_;
 
   base::WeakPtrFactory<TracesInternalsHandler> weak_factory_{this};
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc
index 5458912..053d047 100644
--- a/content/browser/webid/federated_auth_request_impl.cc
+++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -2106,7 +2106,11 @@
   request_dialog_controller_->CloseModalDialog();
 
   // If we have not gotten a signin status change, abort the flow.
-  if (idps_user_tried_to_signin_to_.empty() &&
+  // The same goes if we did get a status change but the accounts fetch
+  // failed.
+  if ((idps_user_tried_to_signin_to_.empty() ||
+       (fetch_data_.pending_idps.empty() &&
+        !fetch_data_.did_succeed_for_at_least_one_idp)) &&
       dialog_type_ == kLoginToIdpPopup) {
     CompleteRequestWithError(FederatedAuthRequestResult::kError,
                              TokenStatus::kLoginPopupClosedWithoutSignin,
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index da0e8c7..ea49357 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -673,6 +673,10 @@
     "//url/mojom:url_mojom_origin",
   ]
 
+  if (is_android) {
+    public_deps += [ "//components/origin_matcher:mojom" ]
+  }
+
   if (is_mac) {
     sources += [
       "render_widget_host_ns_view.mojom",
diff --git a/content/common/gin_java_bridge.mojom b/content/common/gin_java_bridge.mojom
index a741768..5ed06bf 100644
--- a/content/common/gin_java_bridge.mojom
+++ b/content/common/gin_java_bridge.mojom
@@ -4,6 +4,7 @@
 
 module content.mojom;
 
+import "components/origin_matcher/origin_matcher.mojom";
 import "mojo/public/mojom/base/values.mojom";
 
 // Descriptive errors raised by the gin java bridge.
@@ -25,7 +26,8 @@
 interface GinJavaBridge {
   // Sent from browser to renderer to add a Java object with the given name.
   // Object IDs are generated on the browser side.
-  AddNamedObject(string name, int32 object_id, string matcher);
+  AddNamedObject(string name, int32 object_id,
+    origin_matcher.mojom.OriginMatcher origin_matcher);
 
   // Sent from browser to renderer to remove a Java object with the given name.
   RemoveNamedObject(string name);
diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc
index 5cf9e02..1fb1448 100644
--- a/content/gpu/gpu_main.cc
+++ b/content/gpu/gpu_main.cc
@@ -77,7 +77,6 @@
 #include "ui/gl/gl_switches.h"
 #include "ui/gl/gpu_switching_manager.h"
 #include "ui/gl/init/gl_factory.h"
-#include "ui/gl/startup_trace.h"
 
 #if BUILDFLAG(IS_WIN)
 #include <windows.h>
@@ -154,7 +153,7 @@
  private:
   // SandboxHelper:
   void PreSandboxStartup(const gpu::GpuPreferences& gpu_prefs) override {
-    GPU_STARTUP_TRACE_EVENT("gpu_main::PreSandboxStartup");
+    TRACE_EVENT("gpu,startup", "gpu_main::PreSandboxStartup");
     // Warm up resources that don't need access to GPUInfo.
     {
       TRACE_EVENT0("gpu", "Warm up rand");
@@ -187,7 +186,7 @@
   bool EnsureSandboxInitialized(gpu::GpuWatchdogThread* watchdog_thread,
                                 const gpu::GPUInfo* gpu_info,
                                 const gpu::GpuPreferences& gpu_prefs) override {
-    GPU_STARTUP_TRACE_EVENT("gpu_main::EnsureSandboxInitialized");
+    TRACE_EVENT("gpu,startup", "gpu_main::EnsureSandboxInitialized");
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
     return StartSandboxLinux(watchdog_thread, gpu_info, gpu_prefs);
 #elif BUILDFLAG(IS_WIN)
@@ -353,7 +352,7 @@
   // Since GPU initialization calls into skia, it's important to initialize skia
   // before it.
   {
-    GPU_STARTUP_TRACE_EVENT("gpu_main::InitializeSkia");
+    TRACE_EVENT("gpu,startup", "gpu_main::InitializeSkia");
     InitializeSkia();
   }
 
@@ -465,7 +464,7 @@
 bool StartSandboxLinux(gpu::GpuWatchdogThread* watchdog_thread,
                        const gpu::GPUInfo* gpu_info,
                        const gpu::GpuPreferences& gpu_prefs) {
-  GPU_STARTUP_TRACE_EVENT("Initialize sandbox");
+  TRACE_EVENT("gpu,startup", "Initialize sandbox");
 
   if (watchdog_thread) {
     // SandboxLinux needs to be able to ensure that the thread
@@ -563,7 +562,7 @@
 
 #if BUILDFLAG(IS_WIN)
 bool StartSandboxWindows(const sandbox::SandboxInterfaceInfo* sandbox_info) {
-  GPU_STARTUP_TRACE_EVENT("Lower token");
+  TRACE_EVENT("gpu,startup", "Lower token");
 
   // For Windows, if the target_services interface is not zero, the process
   // is sandboxed and we must call LowerToken() before rendering untrusted
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index b447f914..3e69d8d 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -173,6 +173,7 @@
     "//cc:cc_java",
     "//cc/mojom:mojom_java",
     "//components/download/public/common:public_java",
+    "//components/origin_matcher/android:java",
     "//components/payments/mojom:mojom_java",
     "//content/public/common:common_java",
     "//device/bluetooth:java",
diff --git a/content/public/android/java/src/org/chromium/content/browser/JavascriptInjectorImpl.java b/content/public/android/java/src/org/chromium/content/browser/JavascriptInjectorImpl.java
index 7309898..0517bb9b 100644
--- a/content/public/android/java/src/org/chromium/content/browser/JavascriptInjectorImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/JavascriptInjectorImpl.java
@@ -13,12 +13,12 @@
 import org.chromium.build.annotations.DoNotInline;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
+import org.chromium.components.origin_matcher.OriginMatcher;
 import org.chromium.content_public.browser.JavascriptInjector;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.WebContents.UserDataFactory;
 
 import java.lang.annotation.Annotation;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -77,25 +77,41 @@
     }
 
     @Override
-    public List<String> addPossiblyUnsafeInterface(
+    public void addPossiblyUnsafeInterfaceToOrigins(
             @Nullable Object object,
             String name,
             @Nullable Class<? extends Annotation> requiredAnnotation,
-            List<String> originAllowlist) {
+            OriginMatcher matcher) {
         if (object == null || mNativePtr == 0) {
-            return Collections.emptyList();
+            return;
         }
 
         mInjectedObjects.put(
-                name, new InjectedInterface(object, requiredAnnotation, originAllowlist));
-        return JavascriptInjectorImplJni.get()
+                name, new InjectedInterface(object, requiredAnnotation, matcher.serialize()));
+        JavascriptInjectorImplJni.get()
                 .addInterface(
                         mNativePtr,
                         JavascriptInjectorImpl.this,
                         object,
                         name,
                         requiredAnnotation,
-                        originAllowlist);
+                        matcher);
+    }
+
+    @Override
+    public void addPossiblyUnsafeInterface(
+            @Nullable Object object,
+            String name,
+            @Nullable Class<? extends Annotation> requiredAnnotation) {
+        OriginMatcher matcher = new OriginMatcher();
+        try {
+            matcher.setRuleList(List.of("*"));
+            addPossiblyUnsafeInterfaceToOrigins(object, name, requiredAnnotation, matcher);
+            // We always need to clean the matcher when we
+            // are done with it.
+        } finally {
+            matcher.destroy();
+        }
     }
 
     @Override
@@ -114,14 +130,13 @@
         void setAllowInspection(
                 long nativeJavascriptInjector, JavascriptInjectorImpl caller, boolean allow);
 
-        @JniType("std::vector<std::string>")
-        List<String> addInterface(
+        void addInterface(
                 long nativeJavascriptInjector,
                 JavascriptInjectorImpl caller,
                 Object object,
                 String name,
                 @Nullable Class requiredAnnotation,
-                @JniType("std::vector<std::string>") List<String> originAllowlist);
+                @JniType("origin_matcher::OriginMatcher") OriginMatcher matcher);
 
         void removeInterface(
                 long nativeJavascriptInjector, JavascriptInjectorImpl caller, String name);
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/JavascriptInjector.java b/content/public/android/java/src/org/chromium/content_public/browser/JavascriptInjector.java
index 56cb9cd1e..cc506181 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/JavascriptInjector.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/JavascriptInjector.java
@@ -6,6 +6,7 @@
 
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
+import org.chromium.components.origin_matcher.OriginMatcher;
 import org.chromium.content.browser.JavascriptInjectorImpl;
 
 import java.lang.annotation.Annotation;
@@ -80,16 +81,22 @@
      * @param name The name used to expose the instance in Javascript.
      * @param requiredAnnotation Restrict exposed methods to ones with this annotation. If {@code
      *     null} all methods are exposed.
-     * @param originAllowlist A list of origins to restrict this interface to. This internally
-     *     relies on the semantics of net::SchemeHostPortMatcher.
-     * @return any bad origins provided to originAllowlist. If this has any entries, the interface
-     *     will not be added.
+     * @param matcher An origin matcher that will be tested against before injecting any origins.
+     *     This matcher will be copied natively so remember to still clean it up after passing over
+     *     to this method.
      */
-    List<String> addPossiblyUnsafeInterface(
+    void addPossiblyUnsafeInterfaceToOrigins(
             Object object,
             String name,
             Class<? extends Annotation> requiredAnnotation,
-            List<String> originAllowlist);
+            OriginMatcher matcher);
+
+    /**
+     * The same as {@link #addPossiblyUnsafeInterface(Object, name, Class<? extends Annotation>,
+     * OriginMatcher)} but will inject on all origins.
+     */
+    void addPossiblyUnsafeInterface(
+            Object object, String name, Class<? extends Annotation> requiredAnnotation);
 
     /**
      * Removes a previously added Javascript interface with the given name.
@@ -101,15 +108,15 @@
     public final class InjectedInterface {
         private final Object mInjectedObject;
         private final @Nullable Class<? extends Annotation> mRequiredAnnotation;
-        private final @Nullable List<String> mOriginAllowlist;
+        private final List<String> mMatcherRules;
 
         public InjectedInterface(
                 Object object,
                 @Nullable Class<? extends Annotation> annotation,
-                @Nullable List<String> allowlist) {
+                List<String> matcherRules) {
             mInjectedObject = object;
             mRequiredAnnotation = annotation;
-            mOriginAllowlist = allowlist;
+            mMatcherRules = matcherRules;
         }
 
         public Object getInjectedObject() {
@@ -120,8 +127,8 @@
             return mRequiredAnnotation;
         }
 
-        public @Nullable List<String> getOriginAllowlist() {
-            return mOriginAllowlist;
+        public List<String> getMatcherRules() {
+            return mMatcherRules;
         }
     }
 }
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeActivityTestRule.java b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeActivityTestRule.java
index e90aabc..3bd974b 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeActivityTestRule.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeActivityTestRule.java
@@ -18,7 +18,6 @@
 import org.chromium.content_shell_apk.ContentShellActivityTestRule;
 
 import java.lang.annotation.Annotation;
-import java.util.List;
 
 /** ActivityTestRule with common functionality for testing the Java Bridge. */
 public class JavaBridgeActivityTestRule extends ContentShellActivityTestRule {
@@ -126,11 +125,10 @@
                             WebContents webContents = getWebContents();
                             JavascriptInjector injector =
                                     JavascriptInjector.fromWebContents(webContents);
-                            injector.addPossiblyUnsafeInterface(
-                                    object1, name1, requiredAnnotation, List.of("*"));
+                            injector.addPossiblyUnsafeInterface(object1, name1, requiredAnnotation);
                             if (object2 != null && name2 != null) {
                                 injector.addPossiblyUnsafeInterface(
-                                        object2, name2, requiredAnnotation, List.of("*"));
+                                        object2, name2, requiredAnnotation);
                             }
                             webContents.getNavigationController().reload(true);
                         }
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBareboneTest.java b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBareboneTest.java
index 79bffa3..08229e9 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBareboneTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBareboneTest.java
@@ -17,8 +17,6 @@
 import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer;
 import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper;
 
-import java.util.List;
-
 /** Common functionality for testing the Java Bridge. */
 @RunWith(BaseJUnit4ClassRunner.class)
 @Batch(JavaBridgeActivityTestRule.BATCH)
@@ -34,7 +32,7 @@
                     public void run() {
                         mActivityTestRule
                                 .getJavascriptInjector()
-                                .addPossiblyUnsafeInterface(new Object(), name, null, List.of("*"));
+                                .addPossiblyUnsafeInterface(new Object(), name, null);
                     }
                 });
     }
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBasicsTest.java b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBasicsTest.java
index 7b50fe9e..0d2b1de5 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBasicsTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBasicsTest.java
@@ -30,7 +30,6 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 import java.lang.ref.WeakReference;
-import java.util.List;
 import java.util.concurrent.CountDownLatch;
 
 /**
@@ -163,7 +162,7 @@
                                 mActivityTestRule
                                         .getJavascriptInjector()
                                         .addPossiblyUnsafeInterface(
-                                                new Object(), "testObject", null, List.of("*"));
+                                                new Object(), "testObject", null);
                             }
                         });
         Assert.assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject"));
@@ -201,8 +200,7 @@
                                                     }
                                                 },
                                                 "testObject",
-                                                null,
-                                                List.of("*"));
+                                                null);
                             }
                         });
         mActivityTestRule.executeJavaScript("testObject.method()");
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeCoercionTest.java b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeCoercionTest.java
index 8be45b69..89cc517 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeCoercionTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeCoercionTest.java
@@ -24,7 +24,6 @@
 
 import java.io.File;
 import java.nio.file.Files;
-import java.util.List;
 
 /**
  * Part of the test suite for the Java Bridge. This class tests that we correctly convert JavaScript
@@ -761,7 +760,7 @@
                             mActivityTestRule
                                     .getJavascriptInjector()
                                     .addPossiblyUnsafeInterface(
-                                            selfConsuming, "selfConsuming", null, List.of("*"));
+                                            selfConsuming, "selfConsuming", null);
                         }
                     });
             mActivityTestRule.synchronousPageReload();
diff --git a/content/public/browser/browsing_data_remover.h b/content/public/browser/browsing_data_remover.h
index 06855e3..8e85f74 100644
--- a/content/public/browser/browsing_data_remover.h
+++ b/content/public/browser/browsing_data_remover.h
@@ -70,7 +70,7 @@
   static constexpr DataType DATA_TYPE_FILE_SYSTEMS = 1 << 1;
   static constexpr DataType DATA_TYPE_INDEXED_DB = 1 << 2;
   static constexpr DataType DATA_TYPE_LOCAL_STORAGE = 1 << 3;
-  static constexpr DataType DATA_TYPE_WEB_SQL = 1 << 4;
+  static constexpr DataType DATA_TYPE_WEB_SQL_DEPRECATED = 1 << 4;
   static constexpr DataType DATA_TYPE_SERVICE_WORKERS = 1 << 5;
   static constexpr DataType DATA_TYPE_CACHE_STORAGE = 1 << 6;
   // This is also persisted, keep with storage datatypes.
@@ -84,7 +84,7 @@
   // and ones defined by the embedder.
   static constexpr DataType DATA_TYPE_DOM_STORAGE =
       DATA_TYPE_FILE_SYSTEMS | DATA_TYPE_INDEXED_DB | DATA_TYPE_LOCAL_STORAGE |
-      DATA_TYPE_WEB_SQL | DATA_TYPE_SERVICE_WORKERS | DATA_TYPE_CACHE_STORAGE |
+      DATA_TYPE_SERVICE_WORKERS | DATA_TYPE_CACHE_STORAGE |
       DATA_TYPE_EMBEDDER_DOM_STORAGE | DATA_TYPE_BACKGROUND_FETCH;
 
   // Other datatypes.
diff --git a/content/public/browser/storage_partition.h b/content/public/browser/storage_partition.h
index 9d7b43e..fa25146 100644
--- a/content/public/browser/storage_partition.h
+++ b/content/public/browser/storage_partition.h
@@ -197,7 +197,7 @@
     REMOVE_DATA_MASK_INDEXEDDB = 1 << 3,
     REMOVE_DATA_MASK_LOCAL_STORAGE = 1 << 4,
     REMOVE_DATA_MASK_SHADER_CACHE = 1 << 5,
-    REMOVE_DATA_MASK_WEBSQL = 1 << 6,
+    REMOVE_DATA_MASK_WEBSQL_DEPRECATED = 1 << 6,
     REMOVE_DATA_MASK_SERVICE_WORKERS = 1 << 7,
     REMOVE_DATA_MASK_CACHE_STORAGE = 1 << 8,
     REMOVE_DATA_MASK_MEDIA_LICENSES = 1 << 9,
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index b8fd9550..9620316b 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -327,6 +327,7 @@
   if (is_android) {
     deps += [
       # Used by Android WebView only.
+      "//components/origin_matcher",
       "//components/viz/service",
       "//third_party/ashmem",
       "//third_party/cpu_features:ndk_compat",
diff --git a/content/renderer/java/gin_java_bridge_dispatcher.cc b/content/renderer/java/gin_java_bridge_dispatcher.cc
index ebcff10..88f39061 100644
--- a/content/renderer/java/gin_java_bridge_dispatcher.cc
+++ b/content/renderer/java/gin_java_bridge_dispatcher.cc
@@ -10,10 +10,10 @@
 #include "base/containers/contains.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "components/origin_matcher/origin_matcher.h"
 #include "content/public/common/content_features.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/renderer/java/gin_java_bridge_object.h"
-#include "net/base/scheme_host_port_matcher.h"
 #include "third_party/blink/public/platform/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 #include "third_party/blink/public/web/web_view.h"
@@ -52,8 +52,7 @@
     // if the origin matches one of the rules.
     url::Origin security_origin = url::Origin(
         render_frame()->GetWebFrame()->GetDocument().GetSecurityOrigin());
-    bool should_inject =
-        iter->second.matcher.Includes(security_origin.GetURL());
+    bool should_inject = iter->second.matcher.Matches(security_origin);
 
     if (should_inject) {
       GinJavaBridgeObject* object = GinJavaBridgeObject::InjectNamed(
@@ -68,16 +67,15 @@
   }
 }
 
-void GinJavaBridgeDispatcher::AddNamedObject(const std::string& name,
-                                             ObjectID object_id,
-                                             const std::string& matcher) {
+void GinJavaBridgeDispatcher::AddNamedObject(
+    const std::string& name,
+    ObjectID object_id,
+    const origin_matcher::OriginMatcher& matcher) {
   // We should already have received the `remote_` via the SetHost method.
   CHECK(remote_);
   // Added objects only become available after page reload, so here they
   // are only added into the internal map.
-  named_objects_.insert(std::make_pair(
-      name, NamedObject{object_id,
-                        net::SchemeHostPortMatcher::FromRawString(matcher)}));
+  named_objects_.insert(std::make_pair(name, NamedObject{object_id, matcher}));
 }
 
 void GinJavaBridgeDispatcher::RemoveNamedObject(const std::string& name) {
diff --git a/content/renderer/java/gin_java_bridge_dispatcher.h b/content/renderer/java/gin_java_bridge_dispatcher.h
index 9ab33f8..1fd15b8f 100644
--- a/content/renderer/java/gin_java_bridge_dispatcher.h
+++ b/content/renderer/java/gin_java_bridge_dispatcher.h
@@ -14,12 +14,12 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/values.h"
+#include "components/origin_matcher/origin_matcher.h"
 #include "content/common/android/gin_java_bridge_errors.h"
 #include "content/common/gin_java_bridge.mojom.h"
 #include "content/public/renderer/render_frame_observer.h"
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "net/base/scheme_host_port_matcher.h"
 
 namespace content {
 
@@ -30,7 +30,7 @@
   using ObjectID = ObjectMap::KeyType;
 
   ObjectID object_id;
-  net::SchemeHostPortMatcher matcher;
+  origin_matcher::OriginMatcher matcher;
 };
 
 // This class handles injecting Java objects into the main frame of a
@@ -64,7 +64,7 @@
 
   void AddNamedObject(const std::string& name,
                       ObjectID object_id,
-                      const std::string& matcher) override;
+                      const origin_matcher::OriginMatcher& matcher) override;
   void RemoveNamedObject(const std::string& name) override;
   void SetHost(mojo::PendingRemote<mojom::GinJavaBridgeHost> host) override;
 
diff --git a/content/renderer/media/win/dcomp_texture_wrapper_impl.cc b/content/renderer/media/win/dcomp_texture_wrapper_impl.cc
index 3894289..c282098 100644
--- a/content/renderer/media/win/dcomp_texture_wrapper_impl.cc
+++ b/content/renderer/media/win/dcomp_texture_wrapper_impl.cc
@@ -192,7 +192,7 @@
         gpu::SHARED_IMAGE_USAGE_DISPLAY_READ |
             gpu::SHARED_IMAGE_USAGE_GLES2_READ |
             gpu::SHARED_IMAGE_USAGE_RASTER_READ,
-        GL_TEXTURE_EXTERNAL_OES);
+        GL_TEXTURE_EXTERNAL_OES, "DCOMPTextureWrapperImpl");
 
     CHECK(shared_image);
     dcomp_texture_resources_ =
diff --git a/content/renderer/media/win/dcomp_texture_wrapper_unittest.cc b/content/renderer/media/win/dcomp_texture_wrapper_unittest.cc
index 5fd9e249..22d2760 100644
--- a/content/renderer/media/win/dcomp_texture_wrapper_unittest.cc
+++ b/content/renderer/media/win/dcomp_texture_wrapper_unittest.cc
@@ -42,7 +42,7 @@
       const gpu::SharedImageInfo& si_info,
       gfx::GpuMemoryBufferHandle handle) override {
     return base::MakeRefCounted<gpu::ClientSharedImage>(
-        gpu::Mailbox::Generate(), si_info.meta, gpu::SyncToken(), holder_,
+        gpu::Mailbox::Generate(), si_info, gpu::SyncToken(), holder_,
         handle.type);
   }
 
@@ -53,7 +53,7 @@
       gfx::BufferUsage buffer_usage,
       gfx::GpuMemoryBufferHandle buffer_handle) override {
     return base::MakeRefCounted<gpu::ClientSharedImage>(
-        gpu::Mailbox::Generate(), si_info.meta, gpu::SyncToken(),
+        gpu::Mailbox::Generate(), si_info, gpu::SyncToken(),
         gpu::GpuMemoryBufferHandleInfo(std::move(buffer_handle),
                                        si_info.meta.format, si_info.meta.size,
                                        buffer_usage),
diff --git a/content/test/navigation_simulator_impl.cc b/content/test/navigation_simulator_impl.cc
index 4d24638..5a29dc6f 100644
--- a/content/test/navigation_simulator_impl.cc
+++ b/content/test/navigation_simulator_impl.cc
@@ -6,7 +6,6 @@
 
 #include <utility>
 
-#include "base/debug/stack_trace.h"
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/memory/ptr_util.h"
diff --git a/content/utility/services.cc b/content/utility/services.cc
index 1fd84ea4d6..921dab3 100644
--- a/content/utility/services.cc
+++ b/content/utility/services.cc
@@ -291,8 +291,7 @@
 #endif  // BUILDFLAG(ENABLE_ACCESSIBILITY_SERVICE)
 
 #if BUILDFLAG(IS_WIN)
-std::unique_ptr<media::MediaFoundationServiceBroker>
-RunMediaFoundationServiceBroker(
+auto RunMediaFoundationServiceBroker(
     mojo::PendingReceiver<media::mojom::MediaFoundationServiceBroker>
         receiver) {
   return std::make_unique<media::MediaFoundationServiceBroker>(
@@ -444,7 +443,9 @@
 #endif  // BUILDFLAG(IS_WIN)
 
 #if BUILDFLAG(IS_ANDROID)
-  services.Add(RunMediaDrmSupportService);
+  if (base::FeatureList::IsEnabled(media::kMediaDrmQueryInSeparateProcess)) {
+    services.Add(RunMediaDrmSupportService);
+  }
 #endif  // BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(ENABLE_VR) && !BUILDFLAG(IS_ANDROID)
diff --git a/content/utility/utility_main.cc b/content/utility/utility_main.cc
index d2e53b1..86cb9fd 100644
--- a/content/utility/utility_main.cc
+++ b/content/utility/utility_main.cc
@@ -6,6 +6,7 @@
 
 #include "base/allocator/partition_alloc_support.h"
 #include "base/command_line.h"
+#include "base/debug/alias.h"
 #include "base/debug/leak_annotations.h"
 #include "base/functional/bind.h"
 #include "base/message_loop/message_pump_type.h"
diff --git a/crypto/nss_util_chromeos.cc b/crypto/nss_util_chromeos.cc
index 878b766..3f33ccc 100644
--- a/crypto/nss_util_chromeos.cc
+++ b/crypto/nss_util_chromeos.cc
@@ -18,7 +18,6 @@
 
 #include "base/callback_list.h"
 #include "base/containers/contains.h"
-#include "base/debug/stack_trace.h"
 #include "base/files/file_enumerator.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
diff --git a/docs/website b/docs/website
index ecd9c32..c53221e 160000
--- a/docs/website
+++ b/docs/website
@@ -1 +1 @@
-Subproject commit ecd9c325be30d2d9a9fe22964c5728f16de183b2
+Subproject commit c53221ec59afc47168647f603ba0ed197707d015
diff --git a/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc b/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc
index 7792685..2b15c3c 100644
--- a/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc
+++ b/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc
@@ -61,7 +61,6 @@
 constexpr std::string_view kFileSystemsKey = "fileSystems";
 constexpr std::string_view kIndexedDBKey = "indexedDB";
 constexpr std::string_view kLocalStorageKey = "localStorage";
-constexpr std::string_view kWebSQLKey = "webSQL";
 constexpr std::string_view kSinceKey = "since";
 const char kLoadFileError[] = "Failed to load file: \"*\". ";
 const char kHostIDError[] = "Failed to generate HostID.";
@@ -93,9 +92,6 @@
   if (key == kLocalStorageKey) {
     return webview::WEB_VIEW_REMOVE_DATA_MASK_LOCAL_STORAGE;
   }
-  if (key == kWebSQLKey) {
-    return webview::WEB_VIEW_REMOVE_DATA_MASK_WEBSQL;
-  }
   return 0;
 }
 
diff --git a/extensions/browser/api/storage/storage_frontend.cc b/extensions/browser/api/storage/storage_frontend.cc
index 391efd7..dfebd12d9 100644
--- a/extensions/browser/api/storage/storage_frontend.cc
+++ b/extensions/browser/api/storage/storage_frontend.cc
@@ -14,6 +14,7 @@
 
 #include "base/barrier_closure.h"
 #include "base/containers/contains.h"
+#include "base/debug/alias.h"
 #include "base/files/file_path.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
diff --git a/extensions/browser/extension_registrar.cc b/extensions/browser/extension_registrar.cc
index 836ccbf..e46ded6 100644
--- a/extensions/browser/extension_registrar.cc
+++ b/extensions/browser/extension_registrar.cc
@@ -6,6 +6,7 @@
 
 #include "base/check_op.h"
 #include "base/containers/contains.h"
+#include "base/debug/alias.h"
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
diff --git a/extensions/browser/guest_view/web_view/web_view_constants.h b/extensions/browser/guest_view/web_view/web_view_constants.h
index 92c442c1..044b3461 100644
--- a/extensions/browser/guest_view/web_view/web_view_constants.h
+++ b/extensions/browser/guest_view/web_view/web_view_constants.h
@@ -97,9 +97,8 @@
 inline constexpr uint32_t WEB_VIEW_REMOVE_DATA_MASK_FILE_SYSTEMS = 1 << 2;
 inline constexpr uint32_t WEB_VIEW_REMOVE_DATA_MASK_INDEXEDDB = 1 << 3;
 inline constexpr uint32_t WEB_VIEW_REMOVE_DATA_MASK_LOCAL_STORAGE = 1 << 4;
-inline constexpr uint32_t WEB_VIEW_REMOVE_DATA_MASK_WEBSQL = 1 << 5;
-inline constexpr uint32_t WEB_VIEW_REMOVE_DATA_MASK_SESSION_COOKIES = 1 << 6;
-inline constexpr uint32_t WEB_VIEW_REMOVE_DATA_MASK_PERSISTENT_COOKIES = 1 << 7;
+inline constexpr uint32_t WEB_VIEW_REMOVE_DATA_MASK_SESSION_COOKIES = 1 << 5;
+inline constexpr uint32_t WEB_VIEW_REMOVE_DATA_MASK_PERSISTENT_COOKIES = 1 << 6;
 
 // Other.
 extern const char kWebViewContentScriptManagerKeyName[];
diff --git a/extensions/browser/guest_view/web_view/web_view_guest.cc b/extensions/browser/guest_view/web_view/web_view_guest.cc
index 4b74ff40..a4f89c5 100644
--- a/extensions/browser/guest_view/web_view/web_view_guest.cc
+++ b/extensions/browser/guest_view/web_view/web_view_guest.cc
@@ -150,9 +150,6 @@
       webview::WEB_VIEW_REMOVE_DATA_MASK_LOCAL_STORAGE) {
     mask |= StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE;
   }
-  if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_WEBSQL) {
-    mask |= StoragePartition::REMOVE_DATA_MASK_WEBSQL;
-  }
 
   return mask;
 }
diff --git a/gpu/command_buffer/client/client_shared_image.cc b/gpu/command_buffer/client/client_shared_image.cc
index 0153280..7909bd1a 100644
--- a/gpu/command_buffer/client/client_shared_image.cc
+++ b/gpu/command_buffer/client/client_shared_image.cc
@@ -265,12 +265,13 @@
 
 ClientSharedImage::ClientSharedImage(
     const Mailbox& mailbox,
-    const SharedImageMetadata& metadata,
+    const SharedImageInfo& info,
     const SyncToken& sync_token,
     scoped_refptr<SharedImageInterfaceHolder> sii_holder,
     gfx::GpuMemoryBufferType gmb_type)
     : mailbox_(mailbox),
-      metadata_(metadata),
+      metadata_(info.meta),
+      debug_label_(info.debug_label),
       creation_sync_token_(sync_token),
       sii_holder_(std::move(sii_holder)) {
   CHECK(!mailbox.IsZero());
@@ -281,12 +282,12 @@
 
 ClientSharedImage::ClientSharedImage(
     const Mailbox& mailbox,
-    const SharedImageMetadata& metadata,
+    const SharedImageInfo& info,
     const SyncToken& sync_token,
     scoped_refptr<SharedImageInterfaceHolder> sii_holder,
     base::WritableSharedMemoryMapping mapping)
     : ClientSharedImage(mailbox,
-                        metadata,
+                        info,
                         sync_token,
                         sii_holder,
                         gfx::SHARED_MEMORY_BUFFER) {
@@ -296,12 +297,13 @@
 
 ClientSharedImage::ClientSharedImage(
     const Mailbox& mailbox,
-    const SharedImageMetadata& metadata,
+    const SharedImageInfo& info,
     const SyncToken& sync_token,
     scoped_refptr<SharedImageInterfaceHolder> sii_holder,
     uint32_t texture_target)
     : mailbox_(mailbox),
-      metadata_(metadata),
+      metadata_(info.meta),
+      debug_label_(info.debug_label),
       creation_sync_token_(sync_token),
       sii_holder_(std::move(sii_holder)),
       texture_target_(texture_target) {
@@ -318,6 +320,7 @@
     scoped_refptr<SharedImageInterfaceHolder> sii_holder)
     : mailbox_(exported_si.mailbox_),
       metadata_(exported_si.metadata_),
+      debug_label_(exported_si.debug_label_),
       creation_sync_token_(exported_si.creation_sync_token_),
       buffer_usage_(exported_si.buffer_usage_),
       sii_holder_(std::move(sii_holder)),
@@ -347,6 +350,7 @@
 ClientSharedImage::ClientSharedImage(ExportedSharedImage exported_si)
     : mailbox_(exported_si.mailbox_),
       metadata_(exported_si.metadata_),
+      debug_label_(exported_si.debug_label_),
       creation_sync_token_(exported_si.creation_sync_token_),
       buffer_usage_(exported_si.buffer_usage_),
       texture_target_(exported_si.texture_target_) {
@@ -373,13 +377,14 @@
 
 ClientSharedImage::ClientSharedImage(
     const Mailbox& mailbox,
-    const SharedImageMetadata& metadata,
+    const SharedImageInfo& info,
     const SyncToken& sync_token,
     GpuMemoryBufferHandleInfo handle_info,
     scoped_refptr<SharedImageInterfaceHolder> sii_holder,
     scoped_refptr<base::UnsafeSharedMemoryPool> shared_memory_pool)
     : mailbox_(mailbox),
-      metadata_(metadata),
+      metadata_(info.meta),
+      debug_label_(info.debug_label),
       creation_sync_token_(sync_token),
       gpu_memory_buffer_manager_(
 #if BUILDFLAG(IS_WIN)
@@ -477,8 +482,8 @@
     buffer_usage = buffer_usage_.value();
   }
   return ExportedSharedImage(mailbox_, metadata_, creation_sync_token_,
-                             std::move(buffer_handle), buffer_usage,
-                             texture_target_);
+                             debug_label_, std::move(buffer_handle),
+                             buffer_usage, texture_target_);
 }
 
 scoped_refptr<ClientSharedImage> ClientSharedImage::ImportUnowned(
@@ -591,9 +596,9 @@
 scoped_refptr<ClientSharedImage> ClientSharedImage::CreateForTesting(
     const SharedImageMetadata& metadata,
     uint32_t texture_target) {
-  return ImportUnowned(ExportedSharedImage(Mailbox::Generate(), metadata,
-                                           SyncToken(), std::nullopt,
-                                           std::nullopt, texture_target));
+  return ImportUnowned(ExportedSharedImage(
+      Mailbox::Generate(), metadata, SyncToken(), "CSICreateForTesting",
+      std::nullopt, std::nullopt, texture_target));
 }
 
 ClientSharedImage::HelperGpuMemoryBufferManager::HelperGpuMemoryBufferManager(
@@ -675,12 +680,14 @@
     const Mailbox& mailbox,
     const SharedImageMetadata& metadata,
     const SyncToken& sync_token,
+    std::string debug_label,
     std::optional<gfx::GpuMemoryBufferHandle> buffer_handle,
     std::optional<gfx::BufferUsage> buffer_usage,
     uint32_t texture_target)
     : mailbox_(mailbox),
       metadata_(metadata),
       creation_sync_token_(sync_token),
+      debug_label_(debug_label),
       buffer_handle_(std::move(buffer_handle)),
       buffer_usage_(buffer_usage),
       texture_target_(texture_target) {}
@@ -691,7 +698,8 @@
     handle = buffer_handle_->Clone();
   }
   return ExportedSharedImage(mailbox_, metadata_, creation_sync_token_,
-                             std::move(handle), buffer_usage_, texture_target_);
+                             debug_label_, std::move(handle), buffer_usage_,
+                             texture_target_);
 }
 
 SharedImageTexture::ScopedAccess::ScopedAccess(SharedImageTexture* texture,
diff --git a/gpu/command_buffer/client/client_shared_image.h b/gpu/command_buffer/client/client_shared_image.h
index 4c368cf..b0d1179 100644
--- a/gpu/command_buffer/client/client_shared_image.h
+++ b/gpu/command_buffer/client/client_shared_image.h
@@ -95,14 +95,14 @@
 
   // `sii_holder` must not be null.
   ClientSharedImage(const Mailbox& mailbox,
-                    const SharedImageMetadata& metadata,
+                    const SharedImageInfo& info,
                     const SyncToken& sync_token,
                     scoped_refptr<SharedImageInterfaceHolder> sii_holder,
                     gfx::GpuMemoryBufferType gmb_type);
 
   // `sii_holder` must not be null.
   ClientSharedImage(const Mailbox& mailbox,
-                    const SharedImageMetadata& metadata,
+                    const SharedImageInfo& info,
                     const SyncToken& sync_token,
                     scoped_refptr<SharedImageInterfaceHolder> sii_holder,
                     base::WritableSharedMemoryMapping mapping);
@@ -111,7 +111,7 @@
   // used on windows platform.
   ClientSharedImage(
       const Mailbox& mailbox,
-      const SharedImageMetadata& metadata,
+      const SharedImageInfo& info,
       const SyncToken& sync_token,
       GpuMemoryBufferHandleInfo handle_info,
       scoped_refptr<SharedImageInterfaceHolder> sii_holder,
@@ -125,6 +125,7 @@
   SkAlphaType alpha_type() const { return metadata_.alpha_type; }
   SharedImageUsageSet usage() const { return metadata_.usage; }
   std::optional<gfx::BufferUsage> buffer_usage() const { return buffer_usage_; }
+  std::string debug_label() const { return debug_label_; }
   bool is_software() const { return is_software_; }
 
   bool HasHolder() { return sii_holder_ != nullptr; }
@@ -216,9 +217,9 @@
       std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer,
       gfx::BufferUsage buffer_usage,
       scoped_refptr<SharedImageInterfaceHolder> sii_holder) {
+    SharedImageInfo info(metadata, "CSICreateForTesting");
     auto client_si = base::MakeRefCounted<ClientSharedImage>(
-        mailbox, metadata, sync_token, sii_holder,
-        gpu_memory_buffer->GetType());
+        mailbox, info, sync_token, sii_holder, gpu_memory_buffer->GetType());
     client_si->gpu_memory_buffer_ = std::move(gpu_memory_buffer);
     client_si->buffer_usage_ = buffer_usage;
     return client_si;
@@ -325,7 +326,7 @@
   friend class TestSharedImageInterface;
   friend class media::VideoFrame;
   ClientSharedImage(const Mailbox& mailbox,
-                    const SharedImageMetadata& metadata,
+                    const SharedImageInfo& info,
                     const SyncToken& sync_token,
                     scoped_refptr<SharedImageInterfaceHolder> sii_holder,
                     uint32_t texture_target);
@@ -369,6 +370,7 @@
 
   const Mailbox mailbox_;
   const SharedImageMetadata metadata_;
+  const std::string debug_label_;
   SyncToken creation_sync_token_;
   SyncToken destruction_sync_token_;
   // Helper to hold the instance of GpuMemoryBufferManager.
@@ -416,6 +418,7 @@
   ExportedSharedImage(const Mailbox& mailbox,
                       const SharedImageMetadata& metadata,
                       const SyncToken& sync_token,
+                      std::string debug_label,
                       std::optional<gfx::GpuMemoryBufferHandle> buffer_handle,
                       std::optional<gfx::BufferUsage> buffer_usage,
                       uint32_t texture_target);
@@ -423,6 +426,7 @@
   Mailbox mailbox_;
   SharedImageMetadata metadata_;
   SyncToken creation_sync_token_;
+  std::string debug_label_;
   std::optional<gfx::GpuMemoryBufferHandle> buffer_handle_;
   std::optional<gfx::BufferUsage> buffer_usage_;
   uint32_t texture_target_ = 0;
diff --git a/gpu/command_buffer/client/client_shared_image_unittest.cc b/gpu/command_buffer/client/client_shared_image_unittest.cc
index 22a3888..97f083d5 100644
--- a/gpu/command_buffer/client/client_shared_image_unittest.cc
+++ b/gpu/command_buffer/client/client_shared_image_unittest.cc
@@ -37,9 +37,9 @@
                                kOpaque_SkAlphaType,
                                kUsage};
 
-  auto client_si = ClientSharedImage::ImportUnowned(
-      ExportedSharedImage(mailbox, metadata, SyncToken(), std::nullopt,
-                          std::nullopt, GL_TEXTURE_2D));
+  auto client_si = ClientSharedImage::ImportUnowned(ExportedSharedImage(
+      mailbox, metadata, SyncToken(), "ClientSharedImageTest", std::nullopt,
+      std::nullopt, GL_TEXTURE_2D));
 
   // Check that the ClientSI's state matches the input parameters.
   EXPECT_EQ(client_si->mailbox(), mailbox);
diff --git a/gpu/command_buffer/client/shared_image_interface.cc b/gpu/command_buffer/client/shared_image_interface.cc
index e8237b4..d4566b0 100644
--- a/gpu/command_buffer/client/shared_image_interface.cc
+++ b/gpu/command_buffer/client/shared_image_interface.cc
@@ -91,7 +91,8 @@
     GrSurfaceOrigin /*surface_origin*/,
     SkAlphaType /*alpha_type*/,
     SharedImageUsageSet /*usage*/,
-    uint32_t /*texture_target*/) {
+    uint32_t /*texture_target*/,
+    std::string_view /*debug_label*/) {
   return nullptr;
 }
 
diff --git a/gpu/command_buffer/client/shared_image_interface.h b/gpu/command_buffer/client/shared_image_interface.h
index 021a6ad..8145a25 100644
--- a/gpu/command_buffer/client/shared_image_interface.h
+++ b/gpu/command_buffer/client/shared_image_interface.h
@@ -91,6 +91,8 @@
              kPremul_SkAlphaType,
              usage),
         debug_label(debug_label) {}
+  SharedImageInfo(const SharedImageMetadata& meta, std::string_view debug_label)
+      : meta(meta), debug_label(debug_label) {}
 
   SharedImageMetadata meta;
   std::string debug_label;
@@ -290,7 +292,8 @@
       const gfx::ColorSpace& color_space,
       GrSurfaceOrigin surface_origin,
       SkAlphaType alpha_type,
-      gpu::SharedImageUsageSet usage) = 0;
+      gpu::SharedImageUsageSet usage,
+      std::string_view debug_label) = 0;
 
   // Swaps front and back buffer of a swap chain. Back buffer mailbox still
   // refers to the back buffer of the swap chain after calling PresentSwapChain.
@@ -356,7 +359,8 @@
       GrSurfaceOrigin surface_origin,
       SkAlphaType alpha_type,
       SharedImageUsageSet usage,
-      uint32_t texture_target);
+      uint32_t texture_target,
+      std::string_view debug_label);
 
   virtual const SharedImageCapabilities& GetCapabilities() = 0;
 
diff --git a/gpu/command_buffer/client/test_shared_image_interface.cc b/gpu/command_buffer/client/test_shared_image_interface.cc
index b85f333..262b4f9 100644
--- a/gpu/command_buffer/client/test_shared_image_interface.cc
+++ b/gpu/command_buffer/client/test_shared_image_interface.cc
@@ -158,8 +158,8 @@
   auto gmb_handle_type = emulate_client_provided_native_buffer_
                              ? GetNativeBufferType()
                              : gfx::EMPTY_BUFFER;
-  return base::MakeRefCounted<ClientSharedImage>(
-      mailbox, si_info.meta, sync_token, holder_, gmb_handle_type);
+  return base::MakeRefCounted<ClientSharedImage>(mailbox, si_info, sync_token,
+                                                 holder_, gmb_handle_type);
 }
 
 scoped_refptr<ClientSharedImage>
@@ -170,8 +170,8 @@
   base::AutoLock locked(lock_);
   auto mailbox = Mailbox::Generate();
   shared_images_.insert(mailbox);
-  return base::MakeRefCounted<ClientSharedImage>(
-      mailbox, si_info.meta, sync_token, holder_, gfx::EMPTY_BUFFER);
+  return base::MakeRefCounted<ClientSharedImage>(mailbox, si_info, sync_token,
+                                                 holder_, gfx::EMPTY_BUFFER);
 }
 
 scoped_refptr<ClientSharedImage> TestSharedImageInterface::CreateSharedImage(
@@ -216,9 +216,9 @@
       CreateGMBHandle(buffer_format, si_info.meta.size, buffer_usage);
 
   return base::MakeRefCounted<ClientSharedImage>(
-      mailbox, si_info.meta, sync_token,
+      mailbox, si_info, sync_token,
       GpuMemoryBufferHandleInfo(std::move(gmb_handle), si_info.meta.format,
-                                     si_info.meta.size, buffer_usage),
+                                si_info.meta.size, buffer_usage),
       holder_);
 }
 
@@ -255,10 +255,9 @@
   }
 
   return base::MakeRefCounted<ClientSharedImage>(
-      mailbox, si_info.meta, sync_token,
-      GpuMemoryBufferHandleInfo(std::move(buffer_handle),
-                                     si_info.meta.format, si_info.meta.size,
-                                     buffer_usage),
+      mailbox, si_info, sync_token,
+      GpuMemoryBufferHandleInfo(std::move(buffer_handle), si_info.meta.format,
+                                si_info.meta.size, buffer_usage),
       holder_);
 }
 
@@ -287,8 +286,8 @@
   auto mailbox = Mailbox::Generate();
   shared_images_.insert(mailbox);
   most_recent_size_ = si_info.meta.size;
-  return base::MakeRefCounted<ClientSharedImage>(
-      mailbox, si_info.meta, sync_token, holder_, buffer_handle.type);
+  return base::MakeRefCounted<ClientSharedImage>(mailbox, si_info, sync_token,
+                                                 holder_, buffer_handle.type);
 }
 
 scoped_refptr<ClientSharedImage>
@@ -302,9 +301,8 @@
   shared_images_.insert(mailbox);
   most_recent_size_ = si_info.meta.size;
 
-  return base::MakeRefCounted<ClientSharedImage>(mailbox, si_info.meta,
-                                                 GenUnverifiedSyncToken(),
-                                                 holder_, std::move(mapping));
+  return base::MakeRefCounted<ClientSharedImage>(
+      mailbox, si_info, GenUnverifiedSyncToken(), holder_, std::move(mapping));
 }
 
 scoped_refptr<ClientSharedImage>
@@ -365,22 +363,20 @@
                                           const gfx::ColorSpace& color_space,
                                           GrSurfaceOrigin surface_origin,
                                           SkAlphaType alpha_type,
-                                          gpu::SharedImageUsageSet usage) {
+                                          gpu::SharedImageUsageSet usage,
+                                          std::string_view debug_label) {
   auto front_buffer = Mailbox::Generate();
   auto back_buffer = Mailbox::Generate();
   SyncToken sync_token = GenUnverifiedSyncToken();
   shared_images_.insert(front_buffer);
   shared_images_.insert(back_buffer);
+  SharedImageMetadata metadata(format, size, color_space, surface_origin,
+                               alpha_type, usage);
+  SharedImageInfo info(metadata, debug_label);
   return {base::MakeRefCounted<ClientSharedImage>(
-              front_buffer,
-              SharedImageMetadata(format, size, color_space,
-                                       surface_origin, alpha_type, usage),
-              sync_token, holder_, gfx::EMPTY_BUFFER),
-          base::MakeRefCounted<ClientSharedImage>(
-              back_buffer,
-              SharedImageMetadata(format, size, color_space,
-                                       surface_origin, alpha_type, usage),
-              sync_token, holder_, gfx::EMPTY_BUFFER)};
+              front_buffer, info, sync_token, holder_, gfx::EMPTY_BUFFER),
+          base::MakeRefCounted<ClientSharedImage>(back_buffer, info, sync_token,
+                                                  holder_, gfx::EMPTY_BUFFER)};
 }
 
 void TestSharedImageInterface::PresentSwapChain(
diff --git a/gpu/command_buffer/client/test_shared_image_interface.h b/gpu/command_buffer/client/test_shared_image_interface.h
index 4a9b558c..bc3d761 100644
--- a/gpu/command_buffer/client/test_shared_image_interface.h
+++ b/gpu/command_buffer/client/test_shared_image_interface.h
@@ -96,7 +96,8 @@
                                         const gfx::ColorSpace& color_space,
                                         GrSurfaceOrigin surface_origin,
                                         SkAlphaType alpha_type,
-                                        SharedImageUsageSet usage) override;
+                                        SharedImageUsageSet usage,
+                                        std::string_view debug_label) override;
   void PresentSwapChain(const SyncToken& sync_token,
                         const Mailbox& mailbox) override;
 
diff --git a/gpu/command_buffer/service/shared_context_state.cc b/gpu/command_buffer/service/shared_context_state.cc
index 3257560..c4050eb 100644
--- a/gpu/command_buffer/service/shared_context_state.cc
+++ b/gpu/command_buffer/service/shared_context_state.cc
@@ -348,22 +348,15 @@
   // and also when using Graphite.
   DCHECK(!owned_gr_context_ || owned_gr_context_->unique());
 
-  // GPU memory allocations except skia_resource_cache_size_ tracked by this
-  // memory_tracker_ should have beenreleased.
-  DCHECK_EQ(skia_resource_cache_size_,
-            memory_tracker_shared_context_state_->GetSize());
-
   // gr_context_ and all resources owned by it will be released soon, so set it
   // to null.
   gr_context_ = nullptr;
 
-  // Null out `graphite_shared_context_` as well to ensure that the below call
-  // clears memory usage.
-  graphite_shared_context_ = nullptr;
-
-  // UpdateSkiaOwnedMemorySize() will update skia memory usage to 0, to ensure
-  // that PeakGpuMemoryMonitor sees 0 allocated memory.
-  UpdateSkiaOwnedMemorySize();
+  // GPU memory allocations except memory_tracker_shared_context_state_ should
+  // have been released. Ensure that PeakGpuMemoryMonitor sees 0 allocated
+  // memory for Skia memory in SHARED_CONTEXT_STATE.
+  int64_t delta = 0 - memory_tracker_shared_context_state_->GetSize();
+  memory_tracker_shared_context_state_->TrackMemoryAllocatedChange(delta);
 
   // Delete the GrContext. This will either do cleanup if the context is
   // current, or the GrContext was already abandoned if the GLContext was lost.
@@ -377,6 +370,21 @@
       this);
 }
 
+gpu::GraphiteSharedContext* SharedContextState::graphite_shared_context()
+    const {
+#if BUILDFLAG(SKIA_USE_DAWN)
+  if (dawn_context_provider_) {
+    return dawn_context_provider_->GetGraphiteSharedContext();
+  }
+#endif
+#if BUILDFLAG(SKIA_USE_METAL)
+  if (metal_context_provider_) {
+    return metal_context_provider_->GetGraphiteSharedContext();
+  }
+#endif
+  return nullptr;
+}
+
 bool SharedContextState::IsUsingGL() const {
   // If context type is none then SharedContextState exists for WebGL fallback
   // to hold a GL context.
@@ -576,11 +584,12 @@
   const skgpu::graphite::ContextOptions context_options =
       GetDefaultGraphiteContextOptions(workarounds);
 
+  gpu::GraphiteSharedContext* graphite_shared_context = nullptr;
   if (gr_context_type_ == GrContextType::kGraphiteDawn) {
 #if BUILDFLAG(SKIA_USE_DAWN)
     CHECK(dawn_context_provider_);
     if (dawn_context_provider_->InitializeGraphiteContext(context_options)) {
-      graphite_shared_context_ =
+      graphite_shared_context =
           dawn_context_provider_->GetGraphiteSharedContext();
     } else {
       // There is currently no way for the GPU process to gracefully handle
@@ -597,7 +606,7 @@
 #if BUILDFLAG(SKIA_USE_METAL)
     if (metal_context_provider_ &&
         metal_context_provider_->InitializeGraphiteContext(context_options)) {
-      graphite_shared_context_ =
+      graphite_shared_context =
           metal_context_provider_->GetGraphiteSharedContext();
     } else {
       DLOG(ERROR) << "Failed to create Graphite Context for Metal";
@@ -605,15 +614,15 @@
     }
 #endif
   }
-  if (!graphite_shared_context_) {
+  if (!graphite_shared_context) {
     LOG(ERROR) << "Skia Graphite disabled: Graphite Context creation failed.";
     return false;
   }
 
   if (gpu_preferences.perform_graphite_precompilation) {
-    InitiatePrecompilation(graphite_shared_context());
+    InitiatePrecompilation(graphite_shared_context);
 
-    precompile_context_ = graphite_shared_context()->makePrecompileContext();
+    precompile_context_ = graphite_shared_context->makePrecompileContext();
 
     // Every 5 minutes report how many new pipelines have been encountered
     // since the last call
@@ -635,7 +644,7 @@
       &max_viz_compositor_image_provider_cache_bytes);
 
   gpu_main_graphite_recorder_ = MakeGraphiteRecorder(
-      graphite_shared_context(), context_options.fGpuBudgetInBytes,
+      graphite_shared_context, context_options.fGpuBudgetInBytes,
       max_gpu_main_image_provider_cache_bytes);
 
   const bool can_handle_context_resources =
@@ -656,7 +665,7 @@
     // be inserted in order, so this grants the Viz thread more flexibility
     // without any negative impact. See https://crbug.com/406292843
     viz_compositor_graphite_recorder_ = MakeGraphiteRecorder(
-        graphite_shared_context(), context_options.fGpuBudgetInBytes,
+        graphite_shared_context, context_options.fGpuBudgetInBytes,
         max_viz_compositor_image_provider_cache_bytes,
         /*require_ordered_recordings=*/false);
   }
@@ -1106,16 +1115,17 @@
 }
 
 void SharedContextState::UpdateSkiaOwnedMemorySize() {
-  // NOTE: If `graphite_shared_context_` is null, then either (a) it was not
-  // successfully created or (b) this instance is being destroyed. In the former
-  // case, the Graphite GPU main recorder will also not have been created, while
-  // in the latter case, it will imminently be destroyed.
+  // Ensure PeakGpuMemoryMonitor sees 0 allocated memory.
+  // NOTE: If `graphite_shared_context_` is null, then it was not
+  // successfully created. The Graphite GPU main recorder will also not have
+  // been created. When this instance is being destroyed,
+  // memory_tracker_shared_context_state_ is updated in SharedContextState dtor.
   if (!gr_context_ && !graphite_shared_context()) {
-    memory_tracker_shared_context_state_->TrackMemoryAllocatedChange(
-        0u - skia_resource_cache_size_);
-    skia_resource_cache_size_ = 0u;
+    int64_t delta = 0 - memory_tracker_shared_context_state_->GetSize();
+    memory_tracker_shared_context_state_->TrackMemoryAllocatedChange(delta);
     return;
   }
+
   size_t new_size;
   if (gr_context_) {
     gr_context_->getResourceCacheUsage(nullptr /* resourceCount */, &new_size);
@@ -1134,12 +1144,11 @@
       new_size += graphite_shared_context()->currentBudgetedBytes();
     }
   }
-  // Skia does not have a CommandBufferId. PeakMemoryMonitor currently does not
-  // use CommandBufferId to identify source, so use zero here to separate
-  // prevent confusion.
-  memory_tracker_shared_context_state_->TrackMemoryAllocatedChange(
-      static_cast<int64_t>(new_size) - skia_resource_cache_size_);
-  skia_resource_cache_size_ = static_cast<uint64_t>(new_size);
+
+  // Update for PeakGpuMemoryMonitor.
+  int64_t delta = static_cast<int64_t>(new_size) -
+                  memory_tracker_shared_context_state_->GetSize();
+  memory_tracker_shared_context_state_->TrackMemoryAllocatedChange(delta);
 }
 
 void SharedContextState::PessimisticallyResetGrContext() const {
diff --git a/gpu/command_buffer/service/shared_context_state.h b/gpu/command_buffer/service/shared_context_state.h
index 5796e6d..77abd0b1 100644
--- a/gpu/command_buffer/service/shared_context_state.h
+++ b/gpu/command_buffer/service/shared_context_state.h
@@ -190,9 +190,7 @@
   gl::ProgressReporter* progress_reporter() const { return progress_reporter_; }
   // Ganesh/Graphite contexts may only be used on the GPU main thread.
   GrDirectContext* gr_context() const { return gr_context_; }
-  gpu::GraphiteSharedContext* graphite_shared_context() const {
-    return graphite_shared_context_;
-  }
+  gpu::GraphiteSharedContext* graphite_shared_context() const;
   // Graphite recorder for GPU main thread, used by RasterDecoder,
   // SkiaOutputSurfaceImplOnGpu, etc.
   skgpu::graphite::Recorder* gpu_main_graphite_recorder() const {
@@ -356,8 +354,6 @@
   bool created_on_compositor_gpu_thread_ = false;
   bool is_drdc_enabled_ = false;
   raw_ptr<GrDirectContext, DanglingUntriaged> gr_context_ = nullptr;
-  raw_ptr<gpu::GraphiteSharedContext, DanglingUntriaged>
-      graphite_shared_context_;
   std::unique_ptr<skgpu::graphite::Recorder> gpu_main_graphite_recorder_;
   std::unique_ptr<skgpu::graphite::Recorder> viz_compositor_graphite_recorder_;
 
@@ -382,7 +378,6 @@
   raw_ptr<gl::ProgressReporter, DanglingUntriaged> progress_reporter_ = nullptr;
   sk_sp<GrDirectContext> owned_gr_context_;
   std::unique_ptr<ServiceTransferCache> transfer_cache_;
-  uint64_t skia_resource_cache_size_ = 0;
   std::vector<uint8_t> scratch_deserialization_buffer_;
   raw_ptr<gpu::raster::GrShaderCache, DanglingUntriaged> gr_shader_cache_ =
       nullptr;
diff --git a/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory_unittest.cc
index 193514683..d4052e8 100644
--- a/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory_unittest.cc
@@ -153,7 +153,7 @@
 // Test to check interaction between GL and skia representations.
 // We write to a GL texture using gl representation and then read from skia
 // representation.
-TEST_F(AHardwareBufferImageBackingFactoryTest, GLWriteSkiaRead) {
+TEST_P(AHardwareBufferImageBackingFactoryTest, GLWriteSkiaRead) {
   // Create a backing using mailbox.
   auto mailbox = Mailbox::Generate();
   auto format = viz::SinglePlaneFormat::kRGBA_8888;
diff --git a/gpu/command_buffer/service/shared_image_interface_in_process.cc b/gpu/command_buffer/service/shared_image_interface_in_process.cc
index 617167b..4c6c573 100644
--- a/gpu/command_buffer/service/shared_image_interface_in_process.cc
+++ b/gpu/command_buffer/service/shared_image_interface_in_process.cc
@@ -239,9 +239,8 @@
             base::Unretained(this), mailbox, si_info, surface_handle),
         /*sync_token_fences=*/{}, MakeSyncToken(next_fence_sync_release_++));
   }
-  return base::MakeRefCounted<ClientSharedImage>(mailbox, si_info.meta,
-                                                 GenUnverifiedSyncToken(),
-                                                 holder_, gfx::EMPTY_BUFFER);
+  return base::MakeRefCounted<ClientSharedImage>(
+      mailbox, si_info, GenUnverifiedSyncToken(), holder_, gfx::EMPTY_BUFFER);
 }
 
 void SharedImageInterfaceInProcess::CreateSharedImageOnGpuThread(
@@ -285,9 +284,8 @@
                     /*sync_token_fences=*/{},
                     MakeSyncToken(next_fence_sync_release_++));
   }
-  return base::MakeRefCounted<ClientSharedImage>(mailbox, si_info.meta,
-                                                 GenUnverifiedSyncToken(),
-                                                 holder_, gfx::EMPTY_BUFFER);
+  return base::MakeRefCounted<ClientSharedImage>(
+      mailbox, si_info, GenUnverifiedSyncToken(), holder_, gfx::EMPTY_BUFFER);
 }
 
 void SharedImageInterfaceInProcess::CreateSharedImageWithDataOnGpuThread(
@@ -344,8 +342,8 @@
     si_info_copy.meta.format.ClearPrefersExternalSampler();
   }
   return base::MakeRefCounted<ClientSharedImage>(
-      mailbox, si_info_copy.meta, GenUnverifiedSyncToken(),
-      std::move(handle_info), holder_);
+      mailbox, si_info_copy, GenUnverifiedSyncToken(), std::move(handle_info),
+      holder_);
 }
 
 void SharedImageInterfaceInProcess::CreateSharedImageWithBufferUsageOnGpuThread(
@@ -451,7 +449,7 @@
   }
 
   return base::MakeRefCounted<ClientSharedImage>(
-      mailbox, si_info.meta, GenUnverifiedSyncToken(),
+      mailbox, si_info, GenUnverifiedSyncToken(),
       GpuMemoryBufferHandleInfo(std::move(client_buffer_handle),
                                 si_info.meta.format, si_info.meta.size,
                                 buffer_usage),
@@ -485,7 +483,7 @@
   }
 
   return base::MakeRefCounted<ClientSharedImage>(
-      mailbox, si_info.meta, GenUnverifiedSyncToken(), holder_, gmb_type);
+      mailbox, si_info, GenUnverifiedSyncToken(), holder_, gmb_type);
 }
 
 scoped_refptr<ClientSharedImage>
@@ -518,9 +516,8 @@
                                    std::move(handle)),
                     /*sync_token_fences=*/{}, sync_token);
   }
-  return base::MakeRefCounted<ClientSharedImage>(mailbox, si_info.meta,
-                                                 GenUnverifiedSyncToken(),
-                                                 holder_, std::move(mapping));
+  return base::MakeRefCounted<ClientSharedImage>(
+      mailbox, si_info, GenUnverifiedSyncToken(), holder_, std::move(mapping));
 }
 
 void SharedImageInterfaceInProcess::CreateSharedImageWithBufferOnGpuThread(
@@ -553,7 +550,8 @@
     const gfx::ColorSpace& color_space,
     GrSurfaceOrigin surface_origin,
     SkAlphaType alpha_type,
-    gpu::SharedImageUsageSet usage) {
+    gpu::SharedImageUsageSet usage,
+    std::string_view debug_label) {
   NOTREACHED();
 }
 
diff --git a/gpu/command_buffer/service/shared_image_interface_in_process.h b/gpu/command_buffer/service/shared_image_interface_in_process.h
index eb4065d..23ee7350 100644
--- a/gpu/command_buffer/service/shared_image_interface_in_process.h
+++ b/gpu/command_buffer/service/shared_image_interface_in_process.h
@@ -117,7 +117,8 @@
                                         const gfx::ColorSpace& color_space,
                                         GrSurfaceOrigin surface_origin,
                                         SkAlphaType alpha_type,
-                                        SharedImageUsageSet usage) override;
+                                        SharedImageUsageSet usage,
+                                        std::string_view debug_label) override;
   void PresentSwapChain(const SyncToken& sync_token,
                         const Mailbox& mailbox) override;
 #if BUILDFLAG(IS_FUCHSIA)
diff --git a/gpu/config/gpu_info_collector.cc b/gpu/config/gpu_info_collector.cc
index 2dec09de..d7d9ffc5 100644
--- a/gpu/config/gpu_info_collector.cc
+++ b/gpu/config/gpu_info_collector.cc
@@ -41,7 +41,6 @@
 #include "ui/gl/gl_version_info.h"
 #include "ui/gl/init/create_gr_gl_interface.h"
 #include "ui/gl/init/gl_factory.h"
-#include "ui/gl/startup_trace.h"
 
 #if BUILDFLAG(IS_MAC)
 #include "base/apple/bundle_locations.h"
@@ -90,7 +89,7 @@
 }
 
 scoped_refptr<gl::GLContext> InitializeGLContext(gl::GLSurface* surface) {
-  GPU_STARTUP_TRACE_EVENT("gpu_info_collector::InitializeGLContext");
+  TRACE_EVENT("gpu,startup", "gpu_info_collector::InitializeGLContext");
   gl::GLContextAttribs attribs;
   attribs.client_major_es_version = 2;
   scoped_refptr<gl::GLContext> context(
@@ -536,7 +535,7 @@
 
 bool CollectBasicGraphicsInfo(const base::CommandLine* command_line,
                               GPUInfo* gpu_info) {
-  GPU_STARTUP_TRACE_EVENT("gpu_info_collector::CollectBasicGraphicsInfo");
+  TRACE_EVENT("gpu,startup", "gpu_info_collector::CollectBasicGraphicsInfo");
   // In the info-collection GPU process on Windows, we get the device info from
   // the browser.
   if (CollectGraphicsDeviceInfoFromCommandLine(command_line, gpu_info)) {
@@ -585,7 +584,7 @@
 }
 
 bool CollectGraphicsInfoGL(GPUInfo* gpu_info, gl::GLDisplay* display) {
-  GPU_STARTUP_TRACE_EVENT("gpu_info_collector::CollectGraphicsInfoGL");
+  TRACE_EVENT("gpu,startup", "gpu_info_collector::CollectGraphicsInfoGL");
   DCHECK_NE(gl::GetGLImplementationParts(), gl::kGLImplementationNone);
   gl::GLDisplayEGL* egl_display = display->GetAs<gl::GLDisplayEGL>();
 
@@ -806,7 +805,7 @@
 
 bool CollectGpuExtraInfo(gfx::GpuExtraInfo* gpu_extra_info,
                          const GpuPreferences& prefs) {
-  GPU_STARTUP_TRACE_EVENT("gpu_info_collector::CollectGpuExtraInfo");
+  TRACE_EVENT("gpu,startup", "gpu_info_collector::CollectGpuExtraInfo");
   // Populate the list of ANGLE features by querying the functions exposed by
   // EGL_ANGLE_feature_control if it's available.
   if (gl::g_driver_egl.client_ext.b_EGL_ANGLE_feature_control) {
@@ -839,7 +838,7 @@
 void CollectDawnInfo(const gpu::GpuPreferences& gpu_preferences,
                      bool collect_metrics,
                      std::vector<std::string>* dawn_info_list) {
-  GPU_STARTUP_TRACE_EVENT("gpu_info_collector::CollectDawnInfo");
+  TRACE_EVENT("gpu,startup", "gpu_info_collector::CollectDawnInfo");
 #if BUILDFLAG(USE_DAWN)
   DawnProcTable procs = dawn::native::GetProcs();
   dawnProcSetProcs(&procs);
diff --git a/gpu/config/gpu_util.cc b/gpu/config/gpu_util.cc
index daf653a..09d001e 100644
--- a/gpu/config/gpu_util.cc
+++ b/gpu/config/gpu_util.cc
@@ -8,7 +8,6 @@
 
 #include "build/build_config.h"
 #include "ui/gl/gpu_preference.h"
-#include "ui/gl/startup_trace.h"
 
 #if BUILDFLAG(IS_WIN)
 #include <windows.h>
@@ -37,6 +36,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
 #include "base/system/sys_info.h"
+#include "base/trace_event/trace_event.h"
 #include "gpu/config/device_perf_info.h"
 #include "gpu/config/gpu_blocklist.h"
 #include "gpu/config/gpu_crash_keys.h"
@@ -497,7 +497,7 @@
                                      const GpuPreferences& gpu_preferences,
                                      base::CommandLine* command_line,
                                      bool* needs_more_info) {
-  GPU_STARTUP_TRACE_EVENT("gpu_util::ComputeGpuFeatureInfo");
+  TRACE_EVENT("gpu,startup", "gpu_util::ComputeGpuFeatureInfo");
   bool use_software_gl = false;
   bool blocklist_needs_more_info = false;
 
diff --git a/gpu/ipc/client/client_shared_image_interface.cc b/gpu/ipc/client/client_shared_image_interface.cc
index 00ac547..acd14e8 100644
--- a/gpu/ipc/client/client_shared_image_interface.cc
+++ b/gpu/ipc/client/client_shared_image_interface.cc
@@ -99,9 +99,9 @@
   DCHECK(gpu::IsValidClientUsage(si_info.meta.usage))
       << uint32_t(si_info.meta.usage);
   auto mailbox = proxy_->CreateSharedImage(si_info, std::move(pool_id));
-  return base::MakeRefCounted<ClientSharedImage>(
-      AddMailbox(mailbox), si_info.meta, GenUnverifiedSyncToken(), holder_,
-      gfx::EMPTY_BUFFER);
+  return base::MakeRefCounted<ClientSharedImage>(AddMailbox(mailbox), si_info,
+                                                 GenUnverifiedSyncToken(),
+                                                 holder_, gfx::EMPTY_BUFFER);
 }
 
 scoped_refptr<ClientSharedImage> ClientSharedImageInterface::CreateSharedImage(
@@ -123,9 +123,9 @@
     return nullptr;
   }
 
-  return base::MakeRefCounted<ClientSharedImage>(
-      AddMailbox(mailbox), si_info.meta, GenUnverifiedSyncToken(), holder_,
-      gfx::EMPTY_BUFFER);
+  return base::MakeRefCounted<ClientSharedImage>(AddMailbox(mailbox), si_info,
+                                                 GenUnverifiedSyncToken(),
+                                                 holder_, gfx::EMPTY_BUFFER);
 }
 
 scoped_refptr<ClientSharedImage> ClientSharedImageInterface::CreateSharedImage(
@@ -148,7 +148,7 @@
 
   CHECK(!buffer_handle.is_null());
   return base::MakeRefCounted<ClientSharedImage>(
-      AddMailbox(mailbox), si_info_copy.meta, GenUnverifiedSyncToken(),
+      AddMailbox(mailbox), si_info_copy, GenUnverifiedSyncToken(),
       GpuMemoryBufferHandleInfo(std::move(buffer_handle),
                                 si_info_copy.meta.format,
                                 si_info_copy.meta.size, buffer_usage),
@@ -171,7 +171,7 @@
   auto client_buffer_handle = buffer_handle.Clone();
   auto mailbox = proxy_->CreateSharedImage(si_info, std::move(buffer_handle));
   return base::MakeRefCounted<ClientSharedImage>(
-      AddMailbox(mailbox), si_info.meta, GenUnverifiedSyncToken(),
+      AddMailbox(mailbox), si_info, GenUnverifiedSyncToken(),
       GpuMemoryBufferHandleInfo(std::move(client_buffer_handle),
                                 si_info.meta.format, si_info.meta.size,
                                 buffer_usage),
@@ -191,9 +191,9 @@
 #endif
   auto buffer_handle_type = buffer_handle.type;
   auto mailbox = proxy_->CreateSharedImage(si_info, std::move(buffer_handle));
-  return base::MakeRefCounted<ClientSharedImage>(
-      AddMailbox(mailbox), si_info.meta, GenUnverifiedSyncToken(), holder_,
-      buffer_handle_type);
+  return base::MakeRefCounted<ClientSharedImage>(AddMailbox(mailbox), si_info,
+                                                 GenUnverifiedSyncToken(),
+                                                 holder_, buffer_handle_type);
 }
 
 scoped_refptr<ClientSharedImage>
@@ -214,7 +214,7 @@
   }
 
   return base::WrapRefCounted<ClientSharedImage>(new ClientSharedImage(
-      AddMailbox(mailbox), si_info.meta, GenUnverifiedSyncToken(), holder_,
+      AddMailbox(mailbox), si_info, GenUnverifiedSyncToken(), holder_,
       gfx::EMPTY_BUFFER));
 }
 
@@ -227,9 +227,9 @@
 
   auto mailbox = proxy_->CreateSharedImage(si_info, std::move(handle));
 
-  return base::MakeRefCounted<ClientSharedImage>(
-      AddMailbox(mailbox), si_info.meta, GenUnverifiedSyncToken(), holder_,
-      std::move(mapping));
+  return base::MakeRefCounted<ClientSharedImage>(AddMailbox(mailbox), si_info,
+                                                 GenUnverifiedSyncToken(),
+                                                 holder_, std::move(mapping));
 }
 
 void ClientSharedImageInterface::CopyToGpuMemoryBuffer(
@@ -285,24 +285,22 @@
                                             const gfx::ColorSpace& color_space,
                                             GrSurfaceOrigin surface_origin,
                                             SkAlphaType alpha_type,
-                                            gpu::SharedImageUsageSet usage) {
+                                            gpu::SharedImageUsageSet usage,
+                                            std::string_view debug_label) {
   DCHECK(gpu::IsValidClientUsage(usage));
   auto mailboxes = proxy_->CreateSwapChain(format, size, color_space,
                                            surface_origin, alpha_type, usage);
   AddMailbox(mailboxes.front_buffer);
   AddMailbox(mailboxes.back_buffer);
   SyncToken sync_token = GenUnverifiedSyncToken();
+  SharedImageMetadata metadata(format, size, color_space, surface_origin,
+                               alpha_type, usage);
+  SharedImageInfo info(metadata, debug_label);
   return ClientSharedImageInterface::SwapChainSharedImages(
       base::MakeRefCounted<ClientSharedImage>(
-          mailboxes.front_buffer,
-          SharedImageMetadata(format, size, color_space, surface_origin,
-                              alpha_type, usage),
-          sync_token, holder_, gfx::EMPTY_BUFFER),
+          mailboxes.front_buffer, info, sync_token, holder_, gfx::EMPTY_BUFFER),
       base::MakeRefCounted<ClientSharedImage>(
-          mailboxes.back_buffer,
-          SharedImageMetadata(format, size, color_space, surface_origin,
-                              alpha_type, usage),
-          sync_token, holder_, gfx::EMPTY_BUFFER));
+          mailboxes.back_buffer, info, sync_token, holder_, gfx::EMPTY_BUFFER));
 }
 
 void ClientSharedImageInterface::DestroySharedImage(const SyncToken& sync_token,
@@ -351,14 +349,16 @@
     GrSurfaceOrigin surface_origin,
     SkAlphaType alpha_type,
     gpu::SharedImageUsageSet usage,
-    uint32_t texture_target) {
+    uint32_t texture_target,
+    std::string_view debug_label) {
   AddMailbox(mailbox);
   proxy_->NotifyMailboxAdded(mailbox, usage);
 
   SharedImageMetadata metadata(format, size, color_space, surface_origin,
                                alpha_type, usage);
+  SharedImageInfo info(metadata, debug_label);
   return base::WrapRefCounted<ClientSharedImage>(new ClientSharedImage(
-      mailbox, metadata, GenUnverifiedSyncToken(), holder_, texture_target));
+      mailbox, info, GenUnverifiedSyncToken(), holder_, texture_target));
 }
 
 Mailbox ClientSharedImageInterface::AddMailbox(const gpu::Mailbox& mailbox) {
diff --git a/gpu/ipc/client/client_shared_image_interface.h b/gpu/ipc/client/client_shared_image_interface.h
index 3681bdb..d636706 100644
--- a/gpu/ipc/client/client_shared_image_interface.h
+++ b/gpu/ipc/client/client_shared_image_interface.h
@@ -101,7 +101,8 @@
                                         const gfx::ColorSpace& color_space,
                                         GrSurfaceOrigin surface_origin,
                                         SkAlphaType alpha_type,
-                                        SharedImageUsageSet usage) override;
+                                        SharedImageUsageSet usage,
+                                        std::string_view debug_label) override;
   void DestroySharedImage(const SyncToken& sync_token,
                           const Mailbox& mailbox) override;
   void DestroySharedImage(
@@ -115,7 +116,8 @@
       GrSurfaceOrigin surface_origin,
       SkAlphaType alpha_type,
       SharedImageUsageSet usage,
-      uint32_t texture_target) override;
+      uint32_t texture_target,
+      std::string_view debug_label) override;
 
   scoped_refptr<ClientSharedImage> ImportSharedImage(
       ExportedSharedImage exported_shared_image) override;
diff --git a/gpu/ipc/common/exported_shared_image.mojom b/gpu/ipc/common/exported_shared_image.mojom
index f4e9afdd..0221c4ef 100644
--- a/gpu/ipc/common/exported_shared_image.mojom
+++ b/gpu/ipc/common/exported_shared_image.mojom
@@ -14,6 +14,7 @@
   gpu.mojom.Mailbox mailbox;
   gpu.mojom.SharedImageMetadata metadata;
   gpu.mojom.SyncToken creation_sync_token;
+  string debug_label;
 
   // Optional params as they are only needed when passed over VideoFrame
   // GpuMemoryBuffers for exporting/importing mappable shared images.
diff --git a/gpu/ipc/common/exported_shared_image_mojom_traits.h b/gpu/ipc/common/exported_shared_image_mojom_traits.h
index 9048ebf23..b18fef2 100644
--- a/gpu/ipc/common/exported_shared_image_mojom_traits.h
+++ b/gpu/ipc/common/exported_shared_image_mojom_traits.h
@@ -37,6 +37,10 @@
     return shared_image.creation_sync_token_;
   }
 
+  static std::string debug_label(const gpu::ExportedSharedImage& shared_image) {
+    return shared_image.debug_label_;
+  }
+
   static uint32_t texture_target(const gpu::ExportedSharedImage& shared_image) {
     return shared_image.texture_target_;
   }
@@ -55,6 +59,7 @@
                    gpu::ExportedSharedImage* out) {
     if (!data.ReadMailbox(&out->mailbox_) ||
         !data.ReadMetadata(&out->metadata_) ||
+        !data.ReadDebugLabel(&out->debug_label_) ||
         !data.ReadCreationSyncToken(&out->creation_sync_token_) ||
         !data.ReadBufferHandle(&out->buffer_handle_) ||
         !data.ReadBufferUsage(&out->buffer_usage_)) {
diff --git a/gpu/ipc/service/gpu_channel_shared_image_interface.cc b/gpu/ipc/service/gpu_channel_shared_image_interface.cc
index 934766b..22087d7 100644
--- a/gpu/ipc/service/gpu_channel_shared_image_interface.cc
+++ b/gpu/ipc/service/gpu_channel_shared_image_interface.cc
@@ -98,9 +98,9 @@
   shared_image_stub_->factory()->RegisterBacking(
       std::move(shared_image_backing));
 
-  return base::WrapRefCounted<ClientSharedImage>(
-      new ClientSharedImage(mailbox, metadata, GenVerifiedSyncToken(), holder_,
-                            GL_TEXTURE_EXTERNAL_OES));
+  SharedImageInfo info(metadata, /*debug_label=*/"SIForAndroidVideo");
+  return base::WrapRefCounted<ClientSharedImage>(new ClientSharedImage(
+      mailbox, info, GenVerifiedSyncToken(), holder_, GL_TEXTURE_EXTERNAL_OES));
 }
 #endif
 
@@ -145,7 +145,7 @@
   shared_image_stub_->factory()->RegisterBacking(std::move(backing));
 
   return base::WrapRefCounted<ClientSharedImage>(
-      new ClientSharedImage(mailbox, metadata, GenVerifiedSyncToken(), holder_,
+      new ClientSharedImage(mailbox, si_info, GenVerifiedSyncToken(), holder_,
                             GL_TEXTURE_EXTERNAL_OES));
 }
 #endif
@@ -174,9 +174,8 @@
             mailbox, si_info, surface_handle),
         /*sync_token_fences=*/{}, MakeSyncToken(next_fence_sync_release_++));
   }
-  return base::MakeRefCounted<ClientSharedImage>(mailbox, si_info.meta,
-                                                 GenVerifiedSyncToken(),
-                                                 holder_, gfx::EMPTY_BUFFER);
+  return base::MakeRefCounted<ClientSharedImage>(
+      mailbox, si_info, GenVerifiedSyncToken(), holder_, gfx::EMPTY_BUFFER);
 }
 
 void GpuChannelSharedImageInterface::CreateSharedImageOnGpuThread(
@@ -214,9 +213,8 @@
                        this, mailbox, si_info, std::move(pixel_data_copy)),
         /*sync_token_fences=*/{}, MakeSyncToken(next_fence_sync_release_++));
   }
-  return base::MakeRefCounted<ClientSharedImage>(mailbox, si_info.meta,
-                                                 GenVerifiedSyncToken(),
-                                                 holder_, gfx::EMPTY_BUFFER);
+  return base::MakeRefCounted<ClientSharedImage>(
+      mailbox, si_info, GenVerifiedSyncToken(), holder_, gfx::EMPTY_BUFFER);
 }
 
 void GpuChannelSharedImageInterface::CreateSharedImageWithDataOnGpuThread(
@@ -257,7 +255,7 @@
   }
 
   return base::MakeRefCounted<ClientSharedImage>(
-      mailbox, si_info.meta, GenVerifiedSyncToken(),
+      mailbox, si_info, GenVerifiedSyncToken(),
       GetGpuMemoryBufferHandleInfo(mailbox), holder_);
 }
 
@@ -349,7 +347,7 @@
   }
 
   return base::MakeRefCounted<ClientSharedImage>(
-      mailbox, si_info.meta, GenVerifiedSyncToken(),
+      mailbox, si_info, GenVerifiedSyncToken(),
       GpuMemoryBufferHandleInfo(std::move(client_buffer_handle),
                                 si_info.meta.format, si_info.meta.size,
                                 buffer_usage),
@@ -378,7 +376,7 @@
   }
 
   return base::MakeRefCounted<ClientSharedImage>(
-      mailbox, si_info.meta, GenVerifiedSyncToken(), holder_, gmb_type);
+      mailbox, si_info, GenVerifiedSyncToken(), holder_, gmb_type);
 }
 
 scoped_refptr<ClientSharedImage>
@@ -407,9 +405,8 @@
                     MakeSyncToken(next_fence_sync_release_++));
   }
 
-  return base::MakeRefCounted<ClientSharedImage>(mailbox, si_info.meta,
-                                                 GenVerifiedSyncToken(),
-                                                 holder_, std::move(mapping));
+  return base::MakeRefCounted<ClientSharedImage>(
+      mailbox, si_info, GenVerifiedSyncToken(), holder_, std::move(mapping));
 }
 
 void GpuChannelSharedImageInterface::CreateSharedImageWithBufferOnGpuThread(
@@ -439,7 +436,8 @@
     const gfx::ColorSpace& color_space,
     GrSurfaceOrigin surface_origin,
     SkAlphaType alpha_type,
-    SharedImageUsageSet usage) {
+    SharedImageUsageSet usage,
+    std::string_view debug_label) {
   NOTREACHED();
 }
 
diff --git a/gpu/ipc/service/gpu_channel_shared_image_interface.h b/gpu/ipc/service/gpu_channel_shared_image_interface.h
index 581512e..11a23df 100644
--- a/gpu/ipc/service/gpu_channel_shared_image_interface.h
+++ b/gpu/ipc/service/gpu_channel_shared_image_interface.h
@@ -93,7 +93,8 @@
                                         const gfx::ColorSpace& color_space,
                                         GrSurfaceOrigin surface_origin,
                                         SkAlphaType alpha_type,
-                                        SharedImageUsageSet usage) override;
+                                        SharedImageUsageSet usage,
+                                        std::string_view debug_label) override;
   void PresentSwapChain(const SyncToken& sync_token,
                         const Mailbox& mailbox) override;
 #if BUILDFLAG(IS_FUCHSIA)
diff --git a/gpu/ipc/service/gpu_init.cc b/gpu/ipc/service/gpu_init.cc
index 41ef6861..7415ad4 100644
--- a/gpu/ipc/service/gpu_init.cc
+++ b/gpu/ipc/service/gpu_init.cc
@@ -48,7 +48,6 @@
 #include "ui/gl/gl_switches.h"
 #include "ui/gl/gl_utils.h"
 #include "ui/gl/init/gl_factory.h"
-#include "ui/gl/startup_trace.h"
 
 #if BUILDFLAG(IS_MAC)
 #include <GLES2/gl2.h>
@@ -102,7 +101,7 @@
 namespace {
 bool CollectGraphicsInfo(GPUInfo* gpu_info) {
   DCHECK(gpu_info);
-  GPU_STARTUP_TRACE_EVENT("Collect Graphics Info");
+  TRACE_EVENT("gpu,startup", "Collect Graphics Info");
   bool success = CollectContextGraphicsInfo(gpu_info);
   if (!success)
     LOG(ERROR) << "CollectGraphicsInfo failed.";
@@ -111,7 +110,7 @@
 
 void InitializeDawnProcs() {
 #if BUILDFLAG(USE_DAWN) || BUILDFLAG(SKIA_USE_DAWN)
-  GPU_STARTUP_TRACE_EVENT("gpu_init::InitializeDawnProcs");
+  TRACE_EVENT("gpu,startup", "gpu_init::InitializeDawnProcs");
   // Setup the global procs table for GPU process.
   dawnProcSetProcs(&dawn::native::GetProcs());
 #endif  // BUILDFLAG(USE_DAWN) || BUILDFLAG(SKIA_USE_DAWN)
@@ -256,7 +255,7 @@
 // Returns the default GPU's system_device_id.
 void SetupGLDisplayManagerEGL(const GPUInfo& gpu_info,
                               const GpuFeatureInfo& gpu_feature_info) {
-  GPU_STARTUP_TRACE_EVENT("gpu_init::SetupGLDisplayManagerEGL");
+  TRACE_EVENT("gpu,startup", "gpu_init::SetupGLDisplayManagerEGL");
   const GPUInfo::GPUDevice* gpu_high_perf =
       gpu_info.GetGpuByPreference(gl::GpuPreference::kHighPerformance);
   const GPUInfo::GPUDevice* gpu_low_power =
@@ -330,7 +329,7 @@
 
 bool GpuInit::InitializeAndStartSandbox(base::CommandLine* command_line,
                                         const GpuPreferences& gpu_preferences) {
-  GPU_STARTUP_TRACE_EVENT("gpu::GpuInit::InitializeAndStartSandbox");
+  TRACE_EVENT("gpu,startup", "gpu::GpuInit::InitializeAndStartSandbox");
 #if BUILDFLAG(IS_CHROMEOS)
   LOG(WARNING) << "Starting gpu initialization.";
 #endif  //  BUILDFLAG(IS_CHROMEOS)
@@ -424,7 +423,7 @@
   // Start the GPU watchdog only after anything that is expected to be time
   // consuming has completed, otherwise the process is liable to be aborted.
   if (enable_watchdog && !delayed_watchdog_enable) {
-    GPU_STARTUP_TRACE_EVENT("Create GpuWatchdog");
+    TRACE_EVENT("gpu,startup", "Create GpuWatchdog");
     watchdog_thread_ =
         GpuWatchdogThread::Create(gpu_preferences_.watchdog_starts_backgrounded,
                                   gl_use_swiftshader_, "GpuWatchdog");
@@ -656,7 +655,7 @@
     base::FilePath module_path;
     if (base::PathService::Get(base::DIR_MODULE, &module_path)) {
       {
-        GPU_STARTUP_TRACE_EVENT("Load vk_swiftshader.dll");
+        TRACE_EVENT("gpu,startup", "Load vk_swiftshader.dll");
         base::LoadNativeLibrary(module_path.Append(L"vk_swiftshader.dll"),
                                 nullptr);
       }
@@ -665,11 +664,11 @@
       // TODO(crbug.com/40075751): Preload dxil.dll to avoid loader lock issues
       // since dxcompiler.dll loads dxil.dll from DllMain.
       {
-        GPU_STARTUP_TRACE_EVENT("Load dxil.dll");
+        TRACE_EVENT("gpu,startup", "Load dxil.dll");
         base::LoadNativeLibrary(module_path.Append(L"dxil.dll"), nullptr);
       }
       {
-        GPU_STARTUP_TRACE_EVENT("Load dxcompiler.dll");
+        TRACE_EVENT("gpu,startup", "Load dxcompiler.dll");
         base::LoadNativeLibrary(module_path.Append(L"dxcompiler.dll"), nullptr);
       }
 #endif  // defined(DAWN_USE_BUILT_DXC)
@@ -680,7 +679,7 @@
       // DirectML.dll within system folder will be loaded at a later point if
       // the redistributable one fails to be loaded.
       if (command_line->HasSwitch(switches::kUseRedistributableDirectML)) {
-        GPU_STARTUP_TRACE_EVENT("Load directml.dll");
+        TRACE_EVENT("gpu,startup", "Load directml.dll");
         base::LoadNativeLibrary(module_path.Append(L"directml.dll"), nullptr);
       }
     }
@@ -742,7 +741,7 @@
   // issue which breaks the camera preview. (b/166850715)
   std::vector<gfx::BufferFormat> supported_buffer_formats_for_texturing;
   {
-    GPU_STARTUP_TRACE_EVENT("ui::ozone::GetSupportedFormatsForTexturing");
+    TRACE_EVENT("gpu,startup", "ui::ozone::GetSupportedFormatsForTexturing");
     supported_buffer_formats_for_texturing =
         ui::OzonePlatform::GetInstance()
             ->GetSurfaceFactoryOzone()
@@ -1221,7 +1220,7 @@
 
 bool GpuInit::InitializeDawn() {
 #if BUILDFLAG(SKIA_USE_DAWN)
-  GPU_STARTUP_TRACE_EVENT("gpu::GpuInit::InitializeDawn");
+  TRACE_EVENT("gpu,startup", "gpu::GpuInit::InitializeDawn");
   if (gpu_feature_info_.status_values[GPU_FEATURE_TYPE_SKIA_GRAPHITE] !=
           kGpuFeatureStatusEnabled &&
       !gpu::DawnContextProvider::DefaultForceFallbackAdapter()) {
@@ -1281,7 +1280,7 @@
 
 bool GpuInit::InitializeVulkan() {
 #if BUILDFLAG(ENABLE_VULKAN)
-  GPU_STARTUP_TRACE_EVENT("gpu::GpuInit::InitializeVulkan");
+  TRACE_EVENT("gpu,startup", "gpu::GpuInit::InitializeVulkan");
   DCHECK_EQ(gpu_feature_info_.status_values[GPU_FEATURE_TYPE_VULKAN],
             kGpuFeatureStatusEnabled);
   DCHECK_NE(gpu_preferences_.use_vulkan, VulkanImplementationName::kNone);
diff --git a/infra/config/generated/builders/ci/Mac Builder Next/targets/chromium.fyi.json b/infra/config/generated/builders/ci/Mac Builder Next/targets/chromium.fyi.json
index b33038c..31d3fb2 100644
--- a/infra/config/generated/builders/ci/Mac Builder Next/targets/chromium.fyi.json
+++ b/infra/config/generated/builders/ci/Mac Builder Next/targets/chromium.fyi.json
@@ -12,7 +12,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -27,7 +27,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -42,7 +42,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -58,7 +58,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -73,7 +73,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -88,7 +88,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -103,7 +103,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -118,7 +118,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -141,7 +141,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -156,7 +156,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -171,7 +171,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -186,7 +186,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
           "shards": 25
@@ -205,7 +205,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -220,7 +220,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -235,7 +235,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -250,7 +250,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -265,7 +265,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -280,7 +280,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -295,7 +295,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
           "shards": 3
@@ -311,7 +311,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
           "shards": 8
@@ -327,7 +327,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -343,7 +343,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -358,7 +358,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -373,7 +373,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -388,7 +388,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -403,7 +403,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -418,7 +418,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -434,7 +434,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -449,7 +449,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -464,7 +464,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -479,7 +479,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -494,7 +494,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -509,7 +509,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -524,7 +524,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -539,7 +539,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -554,7 +554,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -569,7 +569,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -584,7 +584,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -599,7 +599,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -614,7 +614,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -629,7 +629,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -644,7 +644,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
           "shards": 7
@@ -660,7 +660,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -675,7 +675,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -691,7 +691,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -706,7 +706,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -721,7 +721,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -736,7 +736,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -751,7 +751,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -766,7 +766,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -781,7 +781,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -796,7 +796,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -811,7 +811,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -826,7 +826,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -841,7 +841,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -856,7 +856,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -871,7 +871,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -886,7 +886,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -901,7 +901,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -916,7 +916,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -931,7 +931,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -946,7 +946,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -961,7 +961,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -976,7 +976,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -991,7 +991,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1006,7 +1006,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1021,7 +1021,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
           "shards": 3
@@ -1037,7 +1037,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1052,7 +1052,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1067,7 +1067,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1082,7 +1082,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1097,7 +1097,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1112,7 +1112,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1127,7 +1127,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1142,7 +1142,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1157,7 +1157,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1172,7 +1172,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1187,7 +1187,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1202,7 +1202,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1225,7 +1225,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1246,7 +1246,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1261,7 +1261,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1282,7 +1282,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1301,7 +1301,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1320,7 +1320,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1338,7 +1338,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1353,7 +1353,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1371,7 +1371,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1389,7 +1389,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1411,7 +1411,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -1435,7 +1435,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -1458,7 +1458,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
diff --git a/infra/config/generated/builders/ci/ios-blink-dbg-fyi/targets/chromium.fyi.json b/infra/config/generated/builders/ci/ios-blink-dbg-fyi/targets/chromium.fyi.json
index df30a0bb..cc3315f 100644
--- a/infra/config/generated/builders/ci/ios-blink-dbg-fyi/targets/chromium.fyi.json
+++ b/infra/config/generated/builders/ci/ios-blink-dbg-fyi/targets/chromium.fyi.json
@@ -34,7 +34,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -82,7 +82,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -133,7 +133,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -181,7 +181,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -229,7 +229,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -277,7 +277,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -333,7 +333,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -381,7 +381,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -429,7 +429,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -477,7 +477,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -525,7 +525,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -576,7 +576,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -626,7 +626,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -676,7 +676,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -726,7 +726,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -776,7 +776,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -827,7 +827,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -875,7 +875,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -923,7 +923,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -971,7 +971,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1019,7 +1019,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1067,7 +1067,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1115,7 +1115,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1163,7 +1163,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1213,7 +1213,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1261,7 +1261,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1309,7 +1309,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1357,7 +1357,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1407,7 +1407,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1455,7 +1455,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1503,7 +1503,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1551,7 +1551,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1599,7 +1599,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1647,7 +1647,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1697,7 +1697,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1751,7 +1751,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1805,7 +1805,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1853,7 +1853,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1903,7 +1903,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1951,7 +1951,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1999,7 +1999,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2047,7 +2047,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2095,7 +2095,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2143,7 +2143,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2192,7 +2192,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2240,7 +2240,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2288,7 +2288,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2336,7 +2336,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2384,7 +2384,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2433,7 +2433,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2481,7 +2481,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2529,7 +2529,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2577,7 +2577,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2628,7 +2628,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2676,7 +2676,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2724,7 +2724,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
diff --git a/infra/config/generated/builders/ci/mac-osxbeta-rel/targets/chromium.fyi.json b/infra/config/generated/builders/ci/mac-osxbeta-rel/targets/chromium.fyi.json
index 71fc61d..e528441 100644
--- a/infra/config/generated/builders/ci/mac-osxbeta-rel/targets/chromium.fyi.json
+++ b/infra/config/generated/builders/ci/mac-osxbeta-rel/targets/chromium.fyi.json
@@ -9,7 +9,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -25,7 +25,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -41,7 +41,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -58,7 +58,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -74,7 +74,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -90,7 +90,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -106,7 +106,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -122,7 +122,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -146,7 +146,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -162,7 +162,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -178,7 +178,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -194,7 +194,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -214,7 +214,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -230,7 +230,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -246,7 +246,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -262,7 +262,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -278,7 +278,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -294,7 +294,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -310,7 +310,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -327,7 +327,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -344,7 +344,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -361,7 +361,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -377,7 +377,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -393,7 +393,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -409,7 +409,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -425,7 +425,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -441,7 +441,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -458,7 +458,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -474,7 +474,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -490,7 +490,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -506,7 +506,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -522,7 +522,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -538,7 +538,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -554,7 +554,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -570,7 +570,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -586,7 +586,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -602,7 +602,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -618,7 +618,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -634,7 +634,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -650,7 +650,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -666,7 +666,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -683,7 +683,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -700,7 +700,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -716,7 +716,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -733,7 +733,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -749,7 +749,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -765,7 +765,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -781,7 +781,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -797,7 +797,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -813,7 +813,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -829,7 +829,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -845,7 +845,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -861,7 +861,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -877,7 +877,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -893,7 +893,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -909,7 +909,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -925,7 +925,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -941,7 +941,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -957,7 +957,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -973,7 +973,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -989,7 +989,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1005,7 +1005,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1021,7 +1021,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1037,7 +1037,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1053,7 +1053,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1069,7 +1069,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1085,7 +1085,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -1102,7 +1102,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1118,7 +1118,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1134,7 +1134,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1150,7 +1150,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -1167,7 +1167,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1183,7 +1183,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1199,7 +1199,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1215,7 +1215,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1231,7 +1231,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1247,7 +1247,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1263,7 +1263,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1279,7 +1279,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1303,7 +1303,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1325,7 +1325,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1341,7 +1341,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1363,7 +1363,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1383,7 +1383,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1403,7 +1403,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1422,7 +1422,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1438,7 +1438,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1457,7 +1457,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1476,7 +1476,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "idempotent": false,
@@ -1499,7 +1499,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "idempotent": false,
@@ -1524,7 +1524,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "idempotent": false,
@@ -1548,7 +1548,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/infra/config/generated/builders/try/ios-blink-dbg-fyi/targets/chromium.fyi.json b/infra/config/generated/builders/try/ios-blink-dbg-fyi/targets/chromium.fyi.json
index df30a0bb..cc3315f 100644
--- a/infra/config/generated/builders/try/ios-blink-dbg-fyi/targets/chromium.fyi.json
+++ b/infra/config/generated/builders/try/ios-blink-dbg-fyi/targets/chromium.fyi.json
@@ -34,7 +34,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -82,7 +82,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -133,7 +133,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -181,7 +181,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -229,7 +229,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -277,7 +277,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -333,7 +333,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -381,7 +381,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -429,7 +429,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -477,7 +477,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -525,7 +525,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -576,7 +576,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -626,7 +626,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -676,7 +676,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -726,7 +726,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -776,7 +776,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -827,7 +827,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -875,7 +875,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -923,7 +923,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -971,7 +971,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1019,7 +1019,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1067,7 +1067,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1115,7 +1115,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1163,7 +1163,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1213,7 +1213,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1261,7 +1261,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1309,7 +1309,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1357,7 +1357,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1407,7 +1407,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1455,7 +1455,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1503,7 +1503,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1551,7 +1551,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1599,7 +1599,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1647,7 +1647,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1697,7 +1697,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1751,7 +1751,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1805,7 +1805,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1853,7 +1853,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1903,7 +1903,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1951,7 +1951,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -1999,7 +1999,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2047,7 +2047,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2095,7 +2095,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2143,7 +2143,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2192,7 +2192,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2240,7 +2240,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2288,7 +2288,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2336,7 +2336,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2384,7 +2384,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2433,7 +2433,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2481,7 +2481,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2529,7 +2529,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2577,7 +2577,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2628,7 +2628,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2676,7 +2676,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
@@ -2724,7 +2724,7 @@
           ],
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "named_caches": [
             {
diff --git a/infra/config/generated/builders/try/mac-builder-next/targets/chromium.fyi.json b/infra/config/generated/builders/try/mac-builder-next/targets/chromium.fyi.json
index b33038c..31d3fb2 100644
--- a/infra/config/generated/builders/try/mac-builder-next/targets/chromium.fyi.json
+++ b/infra/config/generated/builders/try/mac-builder-next/targets/chromium.fyi.json
@@ -12,7 +12,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -27,7 +27,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -42,7 +42,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -58,7 +58,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -73,7 +73,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -88,7 +88,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -103,7 +103,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -118,7 +118,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -141,7 +141,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -156,7 +156,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -171,7 +171,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -186,7 +186,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
           "shards": 25
@@ -205,7 +205,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -220,7 +220,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -235,7 +235,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -250,7 +250,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -265,7 +265,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -280,7 +280,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -295,7 +295,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
           "shards": 3
@@ -311,7 +311,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
           "shards": 8
@@ -327,7 +327,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -343,7 +343,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -358,7 +358,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -373,7 +373,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -388,7 +388,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -403,7 +403,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -418,7 +418,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -434,7 +434,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -449,7 +449,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -464,7 +464,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -479,7 +479,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -494,7 +494,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -509,7 +509,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -524,7 +524,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -539,7 +539,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -554,7 +554,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -569,7 +569,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -584,7 +584,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -599,7 +599,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -614,7 +614,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -629,7 +629,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -644,7 +644,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
           "shards": 7
@@ -660,7 +660,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -675,7 +675,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -691,7 +691,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -706,7 +706,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -721,7 +721,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -736,7 +736,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -751,7 +751,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -766,7 +766,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -781,7 +781,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -796,7 +796,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -811,7 +811,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -826,7 +826,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -841,7 +841,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -856,7 +856,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -871,7 +871,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -886,7 +886,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -901,7 +901,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -916,7 +916,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -931,7 +931,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -946,7 +946,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -961,7 +961,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -976,7 +976,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -991,7 +991,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1006,7 +1006,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1021,7 +1021,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
           "shards": 3
@@ -1037,7 +1037,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1052,7 +1052,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1067,7 +1067,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1082,7 +1082,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1097,7 +1097,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1112,7 +1112,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1127,7 +1127,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1142,7 +1142,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1157,7 +1157,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1172,7 +1172,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1187,7 +1187,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1202,7 +1202,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1225,7 +1225,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1246,7 +1246,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1261,7 +1261,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1282,7 +1282,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1301,7 +1301,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1320,7 +1320,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1338,7 +1338,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1353,7 +1353,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1371,7 +1371,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -1389,7 +1389,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1411,7 +1411,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -1435,7 +1435,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -1458,7 +1458,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "arm64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
diff --git a/infra/config/generated/builders/try/mac-osxbeta-rel/targets/chromium.fyi.json b/infra/config/generated/builders/try/mac-osxbeta-rel/targets/chromium.fyi.json
index 71fc61d..e528441 100644
--- a/infra/config/generated/builders/try/mac-osxbeta-rel/targets/chromium.fyi.json
+++ b/infra/config/generated/builders/try/mac-osxbeta-rel/targets/chromium.fyi.json
@@ -9,7 +9,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -25,7 +25,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -41,7 +41,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -58,7 +58,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -74,7 +74,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -90,7 +90,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -106,7 +106,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -122,7 +122,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -146,7 +146,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -162,7 +162,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -178,7 +178,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -194,7 +194,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -214,7 +214,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -230,7 +230,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -246,7 +246,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -262,7 +262,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -278,7 +278,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -294,7 +294,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -310,7 +310,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -327,7 +327,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -344,7 +344,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -361,7 +361,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -377,7 +377,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -393,7 +393,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -409,7 +409,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -425,7 +425,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -441,7 +441,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -458,7 +458,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -474,7 +474,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -490,7 +490,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -506,7 +506,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -522,7 +522,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -538,7 +538,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -554,7 +554,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -570,7 +570,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -586,7 +586,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -602,7 +602,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -618,7 +618,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -634,7 +634,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -650,7 +650,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -666,7 +666,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -683,7 +683,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -700,7 +700,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -716,7 +716,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -733,7 +733,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -749,7 +749,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -765,7 +765,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -781,7 +781,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -797,7 +797,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -813,7 +813,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -829,7 +829,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -845,7 +845,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -861,7 +861,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -877,7 +877,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -893,7 +893,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -909,7 +909,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -925,7 +925,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -941,7 +941,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -957,7 +957,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -973,7 +973,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -989,7 +989,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1005,7 +1005,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1021,7 +1021,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1037,7 +1037,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1053,7 +1053,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1069,7 +1069,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1085,7 +1085,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -1102,7 +1102,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1118,7 +1118,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1134,7 +1134,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1150,7 +1150,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -1167,7 +1167,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1183,7 +1183,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1199,7 +1199,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1215,7 +1215,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1231,7 +1231,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1247,7 +1247,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1263,7 +1263,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1279,7 +1279,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1303,7 +1303,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1325,7 +1325,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1341,7 +1341,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1363,7 +1363,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1383,7 +1383,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1403,7 +1403,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1422,7 +1422,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1438,7 +1438,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1457,7 +1457,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1476,7 +1476,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "idempotent": false,
@@ -1499,7 +1499,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "idempotent": false,
@@ -1524,7 +1524,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "idempotent": false,
@@ -1548,7 +1548,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-15"
+            "os": "Mac-15.5"
           },
           "expiration": 21600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/infra/config/generated/cq-builders.md b/infra/config/generated/cq-builders.md
index 12dc874..47543677 100644
--- a/infra/config/generated/cq-builders.md
+++ b/infra/config/generated/cq-builders.md
@@ -859,7 +859,7 @@
   * Experiment percentage: 10.0
 
 * [mac15-arm64-rel](https://ci.chromium.org/p/chromium/builders/try/mac15-arm64-rel) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""mac15-arm64-rel""))
-  * Experiment percentage: 100.0
+  * Experiment percentage: 1.0
 
 * [tricium-clang-tidy](https://ci.chromium.org/p/chromium/builders/try/tricium-clang-tidy) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""tricium-clang-tidy""))
   * Experiment percentage: 100.0
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg
index abb2fcb..7e91d0a 100644
--- a/infra/config/generated/luci/commit-queue.cfg
+++ b/infra/config/generated/luci/commit-queue.cfg
@@ -6563,7 +6563,7 @@
       builders {
         name: "chromium/try/mac15-arm64-rel"
         disable_reuse_footers: "Include-Ci-Only-Tests"
-        experiment_percentage: 100
+        experiment_percentage: 1
         location_filters {
           gerrit_host_regexp: ".*"
           gerrit_project_regexp: ".*"
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index c26ab1b..a510fc2 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -11734,7 +11734,7 @@
         '    "dawn"'
         '  ]'
         '}'
-      execution_timeout_secs: 14400
+      execution_timeout_secs: 18000
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index 5e351a05..9693fae9 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -4644,6 +4644,7 @@
 job {
   id: "ios19-beta-simulator"
   realm: "ci"
+  schedule: "0 3,7,11,15,19,23 * * *"
   buildbucket {
     server: "cr-buildbucket.appspot.com"
     bucket: "ci"
@@ -4653,6 +4654,7 @@
 job {
   id: "ios19-sdk-simulator"
   realm: "ci"
+  schedule: "0 1,5,9,13,17,21 * * *"
   buildbucket {
     server: "cr-buildbucket.appspot.com"
     bucket: "ci"
@@ -6802,8 +6804,6 @@
   triggers: "ios-simulator-full-configs"
   triggers: "ios-simulator-noncq"
   triggers: "ios18-sdk-device"
-  triggers: "ios19-beta-simulator"
-  triggers: "ios19-sdk-simulator"
   triggers: "linux-angle-chromium-builder"
   triggers: "linux-annotator-rel"
   triggers: "linux-archive-rel"
diff --git a/infra/config/subprojects/chromium/ci/chromium.dawn.star b/infra/config/subprojects/chromium/ci/chromium.dawn.star
index b33f208..1b90aff 100644
--- a/infra/config/subprojects/chromium/ci/chromium.dawn.star
+++ b/infra/config/subprojects/chromium/ci/chromium.dawn.star
@@ -2151,7 +2151,7 @@
         short_name = "asn",
     ),
     # Can hit the default build timeout when building with clean cache.
-    execution_timeout = 4 * time.hour,
+    execution_timeout = 5 * time.hour,
     siso_remote_jobs = siso.remote_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 0774db5..58d1a496 100644
--- a/infra/config/subprojects/chromium/ci/chromium.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -2191,6 +2191,8 @@
 
 fyi_ios_builder(
     name = "ios19-sdk-simulator",
+    schedule = "0 1,5,9,13,17,21 * * *",
+    triggered_by = [],
     builder_spec = builder_config.builder_spec(
         gclient_config = builder_config.gclient_config(config = "ios"),
         chromium_config = builder_config.chromium_config(
@@ -2303,6 +2305,8 @@
 
 fyi_ios_builder(
     name = "ios19-beta-simulator",
+    schedule = "0 3,7,11,15,19,23 * * *",
+    triggered_by = [],
     builder_spec = builder_config.builder_spec(
         gclient_config = builder_config.gclient_config(config = "ios"),
         chromium_config = builder_config.chromium_config(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
index e5e12a7..7a5f35c 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
@@ -361,7 +361,7 @@
         # TODO (crbug.com/415099984): change to 100,
         # then move out of experimental CQ after,
         # mac15-arm64-rel replaces mac14-arm64-rel on CQ.
-        experiment_percentage = 100,
+        experiment_percentage = 1,
     ),
 )
 
diff --git a/infra/config/targets/mixins.star b/infra/config/targets/mixins.star
index 2cdb11f2..d92c5ba 100644
--- a/infra/config/targets/mixins.star
+++ b/infra/config/targets/mixins.star
@@ -1668,7 +1668,7 @@
     swarming = targets.swarming(
         dimensions = {
             "cpu": "arm64",
-            "os": "Mac-15",
+            "os": "Mac-15.5",
         },
     ),
 )
@@ -1791,7 +1791,7 @@
     swarming = targets.swarming(
         dimensions = {
             "cpu": "arm64",
-            "os": "Mac-15",
+            "os": "Mac-15.5",
         },
     ),
 )
@@ -1802,7 +1802,7 @@
     swarming = targets.swarming(
         dimensions = {
             "cpu": "x86-64",
-            "os": "Mac-15",
+            "os": "Mac-15.5",
         },
     ),
 )
diff --git a/internal b/internal
index 56087f1..54ba5bc 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit 56087f1ee64af85f055c3dffe1d540ea90626924
+Subproject commit 54ba5bc847bfa9724ef0b67b1c88da2704467a09
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 5e082fe..6ac32981 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -2720,6 +2720,9 @@
       <message name="IDS_IOS_HOME_CUSTOMIZATION_BACKGROUND_PICKER_PRESET_GALLERY_TITLE" desc="Title of an option in the background customization action sheet that opens a preset background gallery.">
         Browse Gallery
       </message>
+      <message name="IDS_IOS_HOME_CUSTOMIZATION_CONTEXT_MENU_DELETE_RECENT_BACKGROUND_TITLE" desc="Title of the context menu option to delete a background from the recently used list in background customization.">
+        Delete Background
+      </message>
       <message name="IDS_IOS_HOME_CUSTOMIZATION_DISCOVER_TITLE" desc="The title of the Shortcuts toggle in the Home Customization menu's main page.">
         Discover feed
       </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_HOME_CUSTOMIZATION_CONTEXT_MENU_DELETE_RECENT_BACKGROUND_TITLE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_HOME_CUSTOMIZATION_CONTEXT_MENU_DELETE_RECENT_BACKGROUND_TITLE.png.sha1
new file mode 100644
index 0000000..e009307
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_HOME_CUSTOMIZATION_CONTEXT_MENU_DELETE_RECENT_BACKGROUND_TITLE.png.sha1
@@ -0,0 +1 @@
+e16e239aaaf91feb2697a2c449a27609e72c0608
\ No newline at end of file
diff --git a/ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_coordinator.mm b/ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_coordinator.mm
index 655dd9d..7800ac3f 100644
--- a/ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_coordinator.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_coordinator.mm
@@ -60,6 +60,8 @@
   AlertViewController* alertViewController = [[AlertViewController alloc] init];
   alertViewController.modalPresentationStyle =
       UIModalPresentationOverFullScreen;
+  alertViewController.modalTransitionStyle =
+      UIModalTransitionStyleCrossDissolve;
   _alertViewController = alertViewController;
   _mediator->SetConsumer(alertViewController);
   [self.baseViewController presentViewController:alertViewController
diff --git a/ios/chrome/browser/home_customization/coordinator/home_customization_mediator.mm b/ios/chrome/browser/home_customization/coordinator/home_customization_mediator.mm
index c15f2dc..b1c7922 100644
--- a/ios/chrome/browser/home_customization/coordinator/home_customization_mediator.mm
+++ b/ios/chrome/browser/home_customization/coordinator/home_customization_mediator.mm
@@ -270,6 +270,12 @@
   // TODO(crbug.com/408243803): apply NTP background configuration to NTP.
 }
 
+- (void)deleteBackgroundFromRecentlyUsedAtIndex:(NSInteger)index {
+  // TODO(crbug.com/408243803): Remove the background at the given index from
+  // the "Recently Used" list. If the background being removed is also set as
+  // the current NTP background, clear the current background as well.
+}
+
 - (void)fetchBackgroundCustomizationThumbnailURLImage:(GURL)thumbnailURL
                                            completion:
                                                (void (^)(UIImage*))completion {
diff --git a/ios/chrome/browser/home_customization/ui/home_customization_main_view_controller.mm b/ios/chrome/browser/home_customization/ui/home_customization_main_view_controller.mm
index 80bd555..4725cd9 100644
--- a/ios/chrome/browser/home_customization/ui/home_customization_main_view_controller.mm
+++ b/ios/chrome/browser/home_customization/ui/home_customization_main_view_controller.mm
@@ -18,6 +18,7 @@
 #import "ios/chrome/browser/shared/model/browser/browser.h"
 #import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
+#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
 #import "ios/chrome/grit/ios_strings.h"
 #import "ios/public/provider/chrome/browser/ui_utils/ui_utils_api.h"
 #import "ui/base/l10n/l10n_util.h"
@@ -202,6 +203,49 @@
   return nil;
 }
 
+- (UIContextMenuConfiguration*)collectionView:(UICollectionView*)collectionView
+    contextMenuConfigurationForItemAtIndexPath:(NSIndexPath*)indexPath
+                                         point:(CGPoint)point {
+  CustomizationSection* section =
+      [self.diffableDataSource snapshot].sectionIdentifiers[indexPath.section];
+  NSString* itemIdentifier =
+      [self.diffableDataSource itemIdentifierForIndexPath:indexPath];
+
+  if (![section isEqualToString:kCustomizationSectionBackground] ||
+      ![itemIdentifier hasPrefix:kBackgroundCellIdentifier]) {
+    return nil;
+  }
+
+  __weak __typeof(self) weakSelf = self;
+
+  return [UIContextMenuConfiguration
+      configurationWithIdentifier:indexPath
+                  previewProvider:nil
+                   actionProvider:^UIMenu*(
+                       NSArray<UIMenuElement*>* suggestedActions) {
+                     UIAction* deleteAction = [UIAction
+                         actionWithTitle:
+                             l10n_util::GetNSString(
+                                 IDS_IOS_HOME_CUSTOMIZATION_CONTEXT_MENU_DELETE_RECENT_BACKGROUND_TITLE)
+                                   image:DefaultSymbolWithPointSize(
+                                             kBackgroundCustomizationDeleteIcon,
+                                             [[UIFont preferredFontForTextStyle:
+                                                          UIFontTextStyleBody]
+                                                 pointSize])
+                              identifier:nil
+                                 handler:^(UIAction* action) {
+                                   [weakSelf
+                                       handleDeleteBackgroundActionAtIndexPath:
+                                           indexPath];
+                                 }];
+                     deleteAction.attributes =
+                         UIMenuElementAttributesDestructive;
+
+                     return [UIMenu menuWithTitle:@""
+                                         children:@[ deleteAction ]];
+                   }];
+}
+
 - (BOOL)collectionView:(UICollectionView*)collectionView
     shouldSelectItemAtIndexPath:(NSIndexPath*)indexPath {
   CustomizationSection* section =
@@ -345,4 +389,20 @@
   cell.mutator = self.mutator;
 }
 
+// Handles the "Delete Background" context menu action for the given index path.
+// This method removes the background from the model (via the mutator) and
+// updates the collection view by removing the associated item from the
+// diffable data source.
+- (void)handleDeleteBackgroundActionAtIndexPath:(NSIndexPath*)indexPath {
+  NSDiffableDataSourceSnapshot* snapshot = [self.diffableDataSource snapshot];
+  NSString* identifier =
+      [self.diffableDataSource itemIdentifierForIndexPath:indexPath];
+
+  if (identifier) {
+    [self.mutator deleteBackgroundFromRecentlyUsedAtIndex:indexPath.item];
+    [snapshot deleteItemsWithIdentifiers:@[ identifier ]];
+    [_backgroundCustomizationConfigurationMap removeObjectForKey:identifier];
+    [self.diffableDataSource applySnapshot:snapshot animatingDifferences:YES];
+  }
+}
 @end
diff --git a/ios/chrome/browser/home_customization/ui/home_customization_mutator.h b/ios/chrome/browser/home_customization/ui/home_customization_mutator.h
index ff15a4b..8937e1c 100644
--- a/ios/chrome/browser/home_customization/ui/home_customization_mutator.h
+++ b/ios/chrome/browser/home_customization/ui/home_customization_mutator.h
@@ -30,6 +30,10 @@
 - (void)applyBackgroundForConfiguration:
     (BackgroundCustomizationConfiguration*)backgroundConfiguration;
 
+// Removes a background item from the "Recently Used" list at the specified
+// index.
+- (void)deleteBackgroundFromRecentlyUsedAtIndex:(NSInteger)index;
+
 // Downloads and returns a thumbnail image from the given GURL. The image is
 // returned asynchronously through the `completion` block. The method is
 // intended to be used for background customization thumbnails, such as loading
diff --git a/ios/chrome/browser/home_customization/utils/home_customization_constants.h b/ios/chrome/browser/home_customization/utils/home_customization_constants.h
index fe98fc8..9a3a7b7c 100644
--- a/ios/chrome/browser/home_customization/utils/home_customization_constants.h
+++ b/ios/chrome/browser/home_customization/utils/home_customization_constants.h
@@ -67,6 +67,10 @@
 // The identifier for the background picker cell.
 extern NSString* const kBackgroundPickerCellIdentifier;
 
+// The name of the system icon used for the "Delete Background" action in the
+// background customization context menu.
+extern NSString* const kBackgroundCustomizationDeleteIcon;
+
 // The URLs for the links in the Discover submenu.
 extern const char kDiscoverFollowingURL[];
 extern const char kDiscoverHiddenURL[];
diff --git a/ios/chrome/browser/home_customization/utils/home_customization_constants.mm b/ios/chrome/browser/home_customization/utils/home_customization_constants.mm
index db30f84..0a81d5a 100644
--- a/ios/chrome/browser/home_customization/utils/home_customization_constants.mm
+++ b/ios/chrome/browser/home_customization/utils/home_customization_constants.mm
@@ -74,6 +74,8 @@
 NSString* const kBackgroundPickerCellIdentifier =
     @"kBackgroundPickerCellIdentifier";
 
+NSString* const kBackgroundCustomizationDeleteIcon = @"trash";
+
 const char kDiscoverFollowingURL[] =
     "https://google.com/preferences/interests/yourinterests";
 const char kDiscoverHiddenURL[] =
diff --git a/ios/chrome/browser/net/model/ios_chrome_network_delegate.cc b/ios/chrome/browser/net/model/ios_chrome_network_delegate.cc
index 83c35960..5f8e52d 100644
--- a/ios/chrome/browser/net/model/ios_chrome_network_delegate.cc
+++ b/ios/chrome/browser/net/model/ios_chrome_network_delegate.cc
@@ -13,7 +13,6 @@
 #include "base/base_paths.h"
 #include "base/debug/alias.h"
 #include "base/debug/dump_without_crashing.h"
-#include "base/debug/stack_trace.h"
 #include "base/logging.h"
 #include "base/metrics/histogram.h"
 #include "base/path_service.h"
diff --git a/ios/chrome/browser/optimization_guide/model/BUILD.gn b/ios/chrome/browser/optimization_guide/model/BUILD.gn
index 3a15fa9..12a4cf5 100644
--- a/ios/chrome/browser/optimization_guide/model/BUILD.gn
+++ b/ios/chrome/browser/optimization_guide/model/BUILD.gn
@@ -32,6 +32,7 @@
     "//components/optimization_guide/core:prediction",
     "//components/optimization_guide/proto:optimization_guide_proto",
     "//components/prefs",
+    "//components/services/unzip:in_process",
     "//components/signin/public/identity_manager",
     "//components/variations",
     "//ios/chrome/browser/download/model/background_service",
diff --git a/ios/chrome/browser/optimization_guide/model/optimization_guide_service.mm b/ios/chrome/browser/optimization_guide/model/optimization_guide_service.mm
index 0d1e407a0..29437840 100644
--- a/ios/chrome/browser/optimization_guide/model/optimization_guide_service.mm
+++ b/ios/chrome/browser/optimization_guide/model/optimization_guide_service.mm
@@ -6,6 +6,7 @@
 
 #import "base/apple/bundle_locations.h"
 #import "base/files/file_util.h"
+#import "base/functional/bind.h"
 #import "base/functional/callback.h"
 #import "base/metrics/histogram_functions.h"
 #import "base/path_service.h"
@@ -26,6 +27,7 @@
 #import "components/optimization_guide/core/optimization_guide_util.h"
 #import "components/optimization_guide/core/prediction_manager.h"
 #import "components/prefs/pref_service.h"
+#import "components/services/unzip/in_process_unzipper.h"
 #import "components/signin/public/identity_manager/identity_manager.h"
 #import "components/variations/synthetic_trials.h"
 #import "ios/chrome/browser/metrics/model/ios_chrome_metrics_service_accessor.h"
@@ -170,7 +172,8 @@
             base::BindRepeating([]() {
               return GetApplicationContext()->GetLocalState()->GetBoolean(
                   ::prefs::kComponentUpdatesEnabled);
-            }));
+            }),
+            base::BindRepeating(&unzip::LaunchInProcessUnzipper));
   }
 
   if (!off_the_record_) {
diff --git a/ios/chrome/browser/policy/model/configuration_policy_handler_list_factory.mm b/ios/chrome/browser/policy/model/configuration_policy_handler_list_factory.mm
index d8538ba..1a5f2b3 100644
--- a/ios/chrome/browser/policy/model/configuration_policy_handler_list_factory.mm
+++ b/ios/chrome/browser/policy/model/configuration_policy_handler_list_factory.mm
@@ -172,6 +172,9 @@
   { policy::key::kAIModeSearchSuggestSettings,
     omnibox::kAIModeSearchSuggestSettings,
     base::Value::Type::INTEGER },
+  { policy::key::kAIModeSettings,
+    omnibox::kAIModeSettings,
+    base::Value::Type::INTEGER },
 };
 // clang-format on
 
@@ -252,6 +255,10 @@
       omnibox::kAIModeSearchSuggestSettings,
       policy::GenAiDefaultSettingsPolicyHandler::PolicyValueToPrefMap(
           {{0, 0}, {1, 0}, {2, 1}}));
+  gen_ai_default_policies.emplace_back(
+      policy::key::kAIModeSettings, omnibox::kAIModeSettings,
+      policy::GenAiDefaultSettingsPolicyHandler::PolicyValueToPrefMap(
+          {{0, 0}, {1, 0}, {2, 1}}));
   handlers->AddHandler(
       std::make_unique<policy::GenAiDefaultSettingsPolicyHandler>(
           std::move(gen_ai_default_policies)));
diff --git a/ios/chrome/browser/shared/model/prefs/browser_prefs.mm b/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
index 58f6a0f..f4d63702 100644
--- a/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
+++ b/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
@@ -1032,7 +1032,10 @@
   registry->RegisterTimePref(prefs::kIosSyncInfobarErrorLastDismissedTimestamp,
                              base::Time());
 
+  // TODO(crbug.com/422744656): Remove `kAIModeSearchSuggestSettings` pref once
+  // `kAIModeSettings` is implemented.
   registry->RegisterIntegerPref(omnibox::kAIModeSearchSuggestSettings, 0);
+  registry->RegisterIntegerPref(omnibox::kAIModeSettings, 0);
 
   // Deprecated 09/2024 (migrated to localState prefs).
   registry->RegisterBooleanPref(prefs::kIncognitoInterstitialEnabled, false);
diff --git a/ios/chrome/browser/tips_notifications/model/BUILD.gn b/ios/chrome/browser/tips_notifications/model/BUILD.gn
index c68a18c..91a7ef8 100644
--- a/ios/chrome/browser/tips_notifications/model/BUILD.gn
+++ b/ios/chrome/browser/tips_notifications/model/BUILD.gn
@@ -6,6 +6,8 @@
   sources = [
     "tips_notification_client.h",
     "tips_notification_client.mm",
+    "tips_notification_criteria.h",
+    "tips_notification_criteria.mm",
   ]
   deps = [
     ":utils",
@@ -31,6 +33,7 @@
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
+    "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/utils",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/public/features",
@@ -49,6 +52,7 @@
   ]
   deps = [
     "//base",
+    "//components/prefs",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/push_notification/model:constants",
     "//ios/chrome/browser/shared/public/features",
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 676917e..d6e3e5d6 100644
--- a/ios/chrome/browser/tips_notifications/model/tips_notification_client.h
+++ b/ios/chrome/browser/tips_notifications/model/tips_notification_client.h
@@ -82,40 +82,6 @@
                            base::OnceClosure completion);
   void OnNotificationRequested(TipsNotificationType type, NSError* error);
 
-  // Returns true if a notification of the given `type` should be sent.
-  bool ShouldSendNotification(TipsNotificationType type, ProfileIOS* profile);
-
-  // Returns true if a Default Browser notification should be sent.
-  bool ShouldSendDefaultBrowser();
-
-  // Returns true if a Signin notification should be sent.
-  bool ShouldSendSignin(ProfileIOS* profile);
-
-  // Returns true if a WhatsNew notification should be sent.
-  bool ShouldSendWhatsNew(ProfileIOS* profile);
-
-  // Returns true if a SetUpList continuation notification should be sent.
-  bool ShouldSendSetUpListContinuation(ProfileIOS* profile);
-
-  // Returns true if a Docking promo notification should be sent.
-  bool ShouldSendDocking(ProfileIOS* profile);
-
-  // Returns true if an Omnibox Position promo notification should be sent.
-  bool ShouldSendOmniboxPosition();
-
-  // Returns true if a Lens promo notification should be sent.
-  bool ShouldSendLens(ProfileIOS* profile);
-
-  // Returns true if an Enhanced Safe Browsing promo notification should be
-  // sent.
-  bool ShouldSendEnhancedSafeBrowsing(ProfileIOS* profile);
-
-  // Returns true if the CPE notification should be sent.
-  bool ShouldSendCPE();
-
-  // Returns true if the Lens Overlay notification should be sent.
-  bool ShouldSendLensOverlay();
-
   // Returns `true` if there is foreground active browser.
   bool IsSceneLevelForegroundActive();
 
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 66feb93..315398b 100644
--- a/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm
+++ b/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm
@@ -10,27 +10,16 @@
 #import "base/strings/sys_string_conversions.h"
 #import "base/task/bind_post_task.h"
 #import "base/time/time.h"
-#import "components/feature_engagement/public/tracker.h"
-#import "components/password_manager/core/browser/password_manager_util.h"
 #import "components/prefs/pref_registry_simple.h"
 #import "components/prefs/pref_service.h"
-#import "components/safe_browsing/core/common/safe_browsing_prefs.h"
-#import "components/search/search.h"
 #import "components/signin/public/identity_manager/identity_manager.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin_presenter.h"
 #import "ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_commands.h"
-#import "ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/utils.h"
 #import "ios/chrome/browser/default_browser/model/promo_source.h"
-#import "ios/chrome/browser/default_browser/model/utils.h"
-#import "ios/chrome/browser/feature_engagement/model/tracker_factory.h"
-#import "ios/chrome/browser/lens/ui_bundled/lens_availability.h"
-#import "ios/chrome/browser/ntp/model/features.h"
-#import "ios/chrome/browser/ntp/model/set_up_list_prefs.h"
 #import "ios/chrome/browser/push_notification/model/constants.h"
 #import "ios/chrome/browser/push_notification/model/push_notification_client.h"
 #import "ios/chrome/browser/push_notification/model/push_notification_service.h"
 #import "ios/chrome/browser/push_notification/model/push_notification_util.h"
-#import "ios/chrome/browser/search_engines/model/template_url_service_factory.h"
 #import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
 #import "ios/chrome/browser/shared/model/browser/browser.h"
@@ -52,21 +41,12 @@
 #import "ios/chrome/browser/signin/model/chrome_account_manager_service.h"
 #import "ios/chrome/browser/signin/model/chrome_account_manager_service_factory.h"
 #import "ios/chrome/browser/signin/model/identity_manager_factory.h"
+#import "ios/chrome/browser/tips_notifications/model/tips_notification_criteria.h"
 #import "ios/chrome/browser/tips_notifications/model/utils.h"
 #import "ios/public/provider/chrome/browser/lens/lens_api.h"
-#import "ui/base/device_form_factor.h"
 
 namespace {
 
-// The amount of time used to determine if Lens was opened recently.
-const base::TimeDelta kLensOpenedRecency = base::Days(30);
-// The amount of time used to determine if the user used Lens Overlay recently.
-const base::TimeDelta kLensOverlayRecency = base::Days(30);
-// The amount of time used to determine if the CPE promo was displayed recently.
-const base::TimeDelta kCPEPromoRecency = base::Days(7);
-// The amount of time used to determine if the user successfully logged in
-// recently.
-const base::TimeDelta kSuccessfullLoginRecency = base::Days(30);
 // The amount of time used to determine if the user should be classified.
 const base::TimeDelta kClassifyUserRecency = base::Hours(2);
 
@@ -83,66 +63,6 @@
   return nil;
 }
 
-// Returns true if signin is allowed / enabled.
-bool IsSigninEnabled(AuthenticationService* auth_service) {
-  switch (auth_service->GetServiceStatus()) {
-    case AuthenticationService::ServiceStatus::SigninForcedByPolicy:
-    case AuthenticationService::ServiceStatus::SigninAllowed:
-      return true;
-    case AuthenticationService::ServiceStatus::SigninDisabledByUser:
-    case AuthenticationService::ServiceStatus::SigninDisabledByPolicy:
-    case AuthenticationService::ServiceStatus::SigninDisabledByInternal:
-      return false;
-  }
-}
-
-// Returns true if the user can sign in.
-bool CanSignIn(ProfileIOS* profile) {
-  AuthenticationService* auth_service =
-      AuthenticationServiceFactory::GetForProfile(profile);
-  return IsSigninEnabled(auth_service) &&
-         !auth_service->HasPrimaryIdentity(signin::ConsentLevel::kSignin);
-}
-
-// Returns true if a Default Browser Promo was canceled.
-bool DefaultBrowserPromoCanceled() {
-  std::optional<IOSDefaultBrowserPromoAction> action =
-      DefaultBrowserPromoLastAction();
-  if (!action.has_value()) {
-    return false;
-  }
-
-  switch (action.value()) {
-    case IOSDefaultBrowserPromoAction::kCancel:
-      return true;
-    case IOSDefaultBrowserPromoAction::kActionButton:
-    case IOSDefaultBrowserPromoAction::kRemindMeLater:
-    case IOSDefaultBrowserPromoAction::kDismiss:
-      return false;
-  }
-}
-
-// Returns true if the Feature Engagement Tracker has ever triggered for the
-// given `feature`.
-bool FETHasEverTriggered(ProfileIOS* profile, const base::Feature& feature) {
-  feature_engagement::Tracker* tracker =
-      feature_engagement::TrackerFactory::GetForProfile(profile);
-  return tracker->HasEverTriggered(feature, true);
-}
-
-// Returns the user's type stored in local state prefs.
-TipsNotificationUserType GetUserType(PrefService* local_state) {
-  return static_cast<TipsNotificationUserType>(
-      local_state->GetInteger(kTipsNotificationsUserType));
-}
-
-// Sets the user's type in local state prefs, and records a histogram with the
-// type.
-void SetUserType(PrefService* local_state, TipsNotificationUserType user_type) {
-  local_state->SetInteger(kTipsNotificationsUserType, int(user_type));
-  base::UmaHistogramEnumeration("IOS.Notifications.Tips.UserType", user_type);
-}
-
 // Returns true if `time` is less time ago than `delta`.
 bool IsRecent(base::Time time, base::TimeDelta delta) {
   return base::Time::Now() - time < delta;
@@ -165,7 +85,7 @@
   pref_change_registrar_.Add(prefs::kPushNotificationAuthorizationStatus,
                              auth_pref_callback);
   permitted_ = IsPermitted();
-  user_type_ = GetUserType(local_state_);
+  user_type_ = GetTipsNotificationUserType(local_state_);
 }
 
 TipsNotificationClient::~TipsNotificationClient() = default;
@@ -395,6 +315,9 @@
   std::vector<TipsNotificationType> types =
       TipsNotificationsTypesOrder(CanSendReactivation());
 
+  std::unique_ptr<TipsNotificationCriteria> criteria =
+      std::make_unique<TipsNotificationCriteria>(profile, local_state_,
+                                                 CanSendReactivation());
   for (TipsNotificationType type : types) {
     int bit = 1 << int(type);
     if (sent_bitfield & bit) {
@@ -405,7 +328,7 @@
       // This type of notification is not enabled.
       continue;
     }
-    if (ShouldSendNotification(type, profile)) {
+    if (criteria->ShouldSendNotification(type)) {
       RequestNotification(type, profile->GetProfileName(),
                           std::move(completion));
       return;
@@ -475,143 +398,6 @@
   }
 }
 
-bool TipsNotificationClient::ShouldSendNotification(TipsNotificationType type,
-                                                    ProfileIOS* profile) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  switch (type) {
-    case TipsNotificationType::kDefaultBrowser:
-      return ShouldSendDefaultBrowser();
-    case TipsNotificationType::kWhatsNew:
-      return ShouldSendWhatsNew(profile);
-    case TipsNotificationType::kSignin:
-      return ShouldSendSignin(profile);
-    case TipsNotificationType::kSetUpListContinuation:
-      return ShouldSendSetUpListContinuation(profile);
-    case TipsNotificationType::kDocking:
-      return ShouldSendDocking(profile);
-    case TipsNotificationType::kOmniboxPosition:
-      return ShouldSendOmniboxPosition();
-    case TipsNotificationType::kLens:
-      return ShouldSendLens(profile);
-    case TipsNotificationType::kEnhancedSafeBrowsing:
-      return ShouldSendEnhancedSafeBrowsing(profile);
-    case TipsNotificationType::kCPE:
-      return ShouldSendCPE();
-    case TipsNotificationType::kLensOverlay:
-      return ShouldSendLensOverlay();
-    case TipsNotificationType::kIncognitoLock:
-    case TipsNotificationType::kError:
-      NOTREACHED();
-  }
-}
-
-bool TipsNotificationClient::ShouldSendDefaultBrowser() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return !IsChromeLikelyDefaultBrowser() && !DefaultBrowserPromoCanceled();
-}
-
-bool TipsNotificationClient::ShouldSendWhatsNew(ProfileIOS* profile) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return !FETHasEverTriggered(profile,
-                              feature_engagement::kIPHWhatsNewUpdatedFeature);
-}
-
-bool TipsNotificationClient::ShouldSendSignin(ProfileIOS* profile) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return CanSignIn(profile);
-}
-
-bool TipsNotificationClient::ShouldSendSetUpListContinuation(
-    ProfileIOS* profile) {
-  PrefService* local_prefs = GetApplicationContext()->GetLocalState();
-  PrefService* user_prefs = profile->GetPrefs();
-  if (!set_up_list_utils::IsSetUpListActive(local_prefs, user_prefs)) {
-    return false;
-  }
-
-  // This notification should only be requested during the duration of the Set
-  // Up List minus the trigger interval after FirstRun.
-  if (!IsFirstRunRecent(
-          set_up_list::SetUpListDurationPastFirstRun() -
-          TipsNotificationTriggerDelta(CanSendReactivation(), user_type_))) {
-    return false;
-  }
-  return !set_up_list_prefs::AllItemsComplete(local_prefs);
-}
-
-bool TipsNotificationClient::ShouldSendDocking(ProfileIOS* profile) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return !FETHasEverTriggered(profile,
-                              feature_engagement::kIPHiOSDockingPromoFeature) &&
-         !FETHasEverTriggered(
-             profile,
-             feature_engagement::kIPHiOSDockingPromoRemindMeLaterFeature);
-}
-
-bool TipsNotificationClient::ShouldSendOmniboxPosition() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // OmniboxPositionChoice is only available on phones.
-  if (ui::GetDeviceFormFactor() != ui::DEVICE_FORM_FACTOR_PHONE) {
-    return false;
-  }
-  return !GetApplicationContext()->GetLocalState()->GetUserPrefValue(
-      prefs::kBottomOmnibox);
-}
-
-bool TipsNotificationClient::ShouldSendLens(ProfileIOS* profile) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // Early return if Lens is not available or disabled by policy.
-  TemplateURLService* template_url_service =
-      ios::TemplateURLServiceFactory::GetForProfile(profile);
-  bool default_search_is_google =
-      search::DefaultSearchProviderIsGoogle(template_url_service);
-  const bool lens_enabled =
-      lens_availability::CheckAndLogAvailabilityForLensEntryPoint(
-          LensEntrypoint::NewTabPage, default_search_is_google);
-  if (!lens_enabled) {
-    return false;
-  }
-
-  base::Time last_opened =
-      GetApplicationContext()->GetLocalState()->GetTime(prefs::kLensLastOpened);
-  return !IsRecent(last_opened, kLensOpenedRecency);
-}
-
-bool TipsNotificationClient::ShouldSendEnhancedSafeBrowsing(
-    ProfileIOS* profile) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  PrefService* user_prefs = profile->GetPrefs();
-  return user_prefs->GetBoolean(prefs::kAdvancedProtectionAllowed) &&
-         !safe_browsing::IsEnhancedProtectionEnabled(*user_prefs);
-}
-
-bool TipsNotificationClient::ShouldSendCPE() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!local_state_->GetBoolean(
-          prefs::kIosCredentialProviderPromoPolicyEnabled)) {
-    return false;
-  }
-  bool is_credential_provider_enabled =
-      password_manager_util::IsCredentialProviderEnabledOnStartup(local_state_);
-  if (is_credential_provider_enabled) {
-    return false;
-  }
-  base::Time promo_display_time =
-      local_state_->GetTime(prefs::kIosCredentialProviderPromoDisplayTime);
-  if (IsRecent(promo_display_time, kCPEPromoRecency)) {
-    return false;
-  }
-  base::Time login_time =
-      local_state_->GetTime(prefs::kIosSuccessfulLoginWithExistingPassword);
-  return IsRecent(login_time, kSuccessfullLoginRecency);
-}
-
-bool TipsNotificationClient::ShouldSendLensOverlay() {
-  base::Time lens_overlay_last_presented =
-      local_state_->GetTime(prefs::kLensOverlayLastPresented);
-  return !IsRecent(lens_overlay_last_presented, kLensOverlayRecency);
-}
-
 bool TipsNotificationClient::IsSceneLevelForegroundActive() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return GetActiveForegroundBrowser() != nullptr;
@@ -680,7 +466,7 @@
   // The user may have signed in between when the notification was requested
   // and when it triggered. If the user can no longer sign in, then open
   // the account settings.
-  if (!CanSignIn(browser->GetProfile())) {
+  if (!TipsNotificationCriteria::CanSignIn(browser->GetProfile())) {
     [HandlerForProtocol(browser->GetCommandDispatcher(), SettingsCommands)
         showAccountsSettingsFromViewController:nil
                           skipIfUINotAvailable:NO];
@@ -910,7 +696,8 @@
   } else {
     user_type_ = TipsNotificationUserType::kLessEngaged;
   }
-  SetUserType(local_state_, user_type_);
+  SetTipsNotificationUserType(local_state_, user_type_);
+  base::UmaHistogramEnumeration("IOS.Notifications.Tips.UserType", user_type_);
 }
 
 bool TipsNotificationClient::HasIdentitiesOnDevice(ProfileIOS* profile) const {
diff --git a/ios/chrome/browser/tips_notifications/model/tips_notification_criteria.h b/ios/chrome/browser/tips_notifications/model/tips_notification_criteria.h
new file mode 100644
index 0000000..b2e5259
--- /dev/null
+++ b/ios/chrome/browser/tips_notifications/model/tips_notification_criteria.h
@@ -0,0 +1,66 @@
+// Copyright 2025 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_TIPS_NOTIFICATIONS_MODEL_TIPS_NOTIFICATION_CRITERIA_H_
+#define IOS_CHROME_BROWSER_TIPS_NOTIFICATIONS_MODEL_TIPS_NOTIFICATION_CRITERIA_H_
+
+#import "base/feature_list.h"
+#import "base/memory/raw_ptr.h"
+
+class PrefService;
+class ProfileIOS;
+enum class TipsNotificationType;
+enum class TipsNotificationUserType;
+
+// A class that can evaluate the criteria for sending each type of Tips
+// Notification.
+class TipsNotificationCriteria {
+ public:
+  // Constructs a `TipsNotificationCriteria` object with the given `profile`
+  // and `local_state`. `reactivation` indicates whether the app is sending
+  // provisional "reactivation" / "proactive" tips notifications.
+  TipsNotificationCriteria(ProfileIOS* profile,
+                           PrefService* local_state,
+                           bool reactivation = false);
+
+  // Returns true if the given `type` of notification should be sent.
+  bool ShouldSendNotification(TipsNotificationType type);
+
+  // Returns true if the user is allowed to sign in and isn't currently signed
+  // in.
+  static bool CanSignIn(ProfileIOS* profile);
+
+ private:
+  // Helpers that evaluate the criteria for sending each type of Tips
+  // Notification. If they return true, that type of notification is
+  // eligible to be sent.
+  bool ShouldSendDefaultBrowser();
+  bool ShouldSendSignin();
+  bool ShouldSendWhatsNew();
+  bool ShouldSendSetUpListContinuation();
+  bool ShouldSendDocking();
+  bool ShouldSendOmniboxPosition();
+  bool ShouldSendLens();
+  bool ShouldSendEnhancedSafeBrowsing();
+  bool ShouldSendCPE();
+  bool ShouldSendLensOverlay();
+
+  // Returns true if the Feature Engagement Tracker has ever triggered for the
+  // given `feature`.
+  bool FETHasEverTriggered(const base::Feature& feature);
+
+  // Stores the profile that will be used to evaluate criteria.
+  raw_ptr<ProfileIOS> profile_;
+
+  // Stores the prefs for the profile.
+  raw_ptr<PrefService> profile_prefs_;
+
+  // Stores the local state prefs.
+  raw_ptr<PrefService> local_state_;
+
+  // True if the criteria are being evaluated for reactivation notifications.
+  bool reactivation_;
+};
+
+#endif  // IOS_CHROME_BROWSER_TIPS_NOTIFICATIONS_MODEL_TIPS_NOTIFICATION_CRITERIA_H_
diff --git a/ios/chrome/browser/tips_notifications/model/tips_notification_criteria.mm b/ios/chrome/browser/tips_notifications/model/tips_notification_criteria.mm
new file mode 100644
index 0000000..94ac6a2
--- /dev/null
+++ b/ios/chrome/browser/tips_notifications/model/tips_notification_criteria.mm
@@ -0,0 +1,216 @@
+// Copyright 2025 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/tips_notifications/model/tips_notification_criteria.h"
+
+#import "base/time/time.h"
+#import "components/feature_engagement/public/tracker.h"
+#import "components/password_manager/core/browser/password_manager_util.h"
+#import "components/prefs/pref_service.h"
+#import "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#import "components/search/search.h"
+#import "ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/utils.h"
+#import "ios/chrome/browser/default_browser/model/promo_source.h"
+#import "ios/chrome/browser/default_browser/model/utils.h"
+#import "ios/chrome/browser/feature_engagement/model/tracker_factory.h"
+#import "ios/chrome/browser/lens/ui_bundled/lens_availability.h"
+#import "ios/chrome/browser/ntp/model/features.h"
+#import "ios/chrome/browser/ntp/model/set_up_list_prefs.h"
+#import "ios/chrome/browser/search_engines/model/template_url_service_factory.h"
+#import "ios/chrome/browser/shared/model/application_context/application_context.h"
+#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
+#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
+#import "ios/chrome/browser/shared/model/utils/first_run_util.h"
+#import "ios/chrome/browser/signin/model/authentication_service.h"
+#import "ios/chrome/browser/signin/model/authentication_service_factory.h"
+#import "ios/chrome/browser/tips_notifications/model/utils.h"
+#import "ui/base/device_form_factor.h"
+
+using base::Time;
+using base::TimeDelta;
+
+namespace {
+
+// The amount of time used to determine if Lens was opened recently.
+const TimeDelta kLensOpenedRecency = base::Days(30);
+// The amount of time used to determine if the user used Lens Overlay recently.
+const TimeDelta kLensOverlayRecency = base::Days(30);
+// The amount of time used to determine if the CPE promo was displayed recently.
+const TimeDelta kCPEPromoRecency = base::Days(7);
+// The amount of time used to determine if the user successfully logged in
+// recently.
+const TimeDelta kSuccessfullLoginRecency = base::Days(30);
+
+// Returns true if a Default Browser Promo was canceled.
+bool DefaultBrowserPromoCanceled() {
+  std::optional<IOSDefaultBrowserPromoAction> action =
+      DefaultBrowserPromoLastAction();
+  if (!action.has_value()) {
+    return false;
+  }
+
+  switch (action.value()) {
+    case IOSDefaultBrowserPromoAction::kCancel:
+      return true;
+    case IOSDefaultBrowserPromoAction::kActionButton:
+    case IOSDefaultBrowserPromoAction::kRemindMeLater:
+    case IOSDefaultBrowserPromoAction::kDismiss:
+      return false;
+  }
+}
+
+// Returns true if `time` is less time ago than `delta`.
+bool IsRecent(base::Time time, TimeDelta delta) {
+  return base::Time::Now() - time < delta;
+}
+
+}  // namespace
+
+TipsNotificationCriteria::TipsNotificationCriteria(ProfileIOS* profile,
+                                                   PrefService* local_state,
+                                                   bool reactivation)
+    : profile_(profile),
+      profile_prefs_(profile->GetPrefs()),
+      local_state_(local_state),
+      reactivation_(reactivation) {}
+
+bool TipsNotificationCriteria::ShouldSendNotification(
+    TipsNotificationType type) {
+  switch (type) {
+    case TipsNotificationType::kDefaultBrowser:
+      return ShouldSendDefaultBrowser();
+    case TipsNotificationType::kWhatsNew:
+      return ShouldSendWhatsNew();
+    case TipsNotificationType::kSignin:
+      return ShouldSendSignin();
+    case TipsNotificationType::kSetUpListContinuation:
+      return ShouldSendSetUpListContinuation();
+    case TipsNotificationType::kDocking:
+      return ShouldSendDocking();
+    case TipsNotificationType::kOmniboxPosition:
+      return ShouldSendOmniboxPosition();
+    case TipsNotificationType::kLens:
+      return ShouldSendLens();
+    case TipsNotificationType::kEnhancedSafeBrowsing:
+      return ShouldSendEnhancedSafeBrowsing();
+    case TipsNotificationType::kCPE:
+      return ShouldSendCPE();
+    case TipsNotificationType::kLensOverlay:
+      return ShouldSendLensOverlay();
+    case TipsNotificationType::kIncognitoLock:
+    case TipsNotificationType::kError:
+      NOTREACHED();
+  }
+}
+
+bool TipsNotificationCriteria::CanSignIn(ProfileIOS* profile) {
+  AuthenticationService* auth_service =
+      AuthenticationServiceFactory::GetForProfile(profile);
+  switch (auth_service->GetServiceStatus()) {
+    case AuthenticationService::ServiceStatus::SigninForcedByPolicy:
+    case AuthenticationService::ServiceStatus::SigninAllowed:
+      return !auth_service->HasPrimaryIdentity(signin::ConsentLevel::kSignin);
+    case AuthenticationService::ServiceStatus::SigninDisabledByUser:
+    case AuthenticationService::ServiceStatus::SigninDisabledByPolicy:
+    case AuthenticationService::ServiceStatus::SigninDisabledByInternal:
+      return false;
+  }
+}
+
+bool TipsNotificationCriteria::ShouldSendDefaultBrowser() {
+  return !IsChromeLikelyDefaultBrowser() && !DefaultBrowserPromoCanceled();
+}
+
+bool TipsNotificationCriteria::ShouldSendWhatsNew() {
+  return !FETHasEverTriggered(feature_engagement::kIPHWhatsNewUpdatedFeature);
+}
+
+bool TipsNotificationCriteria::ShouldSendSignin() {
+  return CanSignIn(profile_);
+}
+
+bool TipsNotificationCriteria::ShouldSendSetUpListContinuation() {
+  if (!set_up_list_utils::IsSetUpListActive(local_state_, profile_prefs_)) {
+    return false;
+  }
+
+  // This notification should only be requested during the duration of the Set
+  // Up List minus the trigger interval after FirstRun.
+  TimeDelta trigger_delta = TipsNotificationTriggerDelta(
+      reactivation_, GetTipsNotificationUserType(local_state_));
+  if (!IsFirstRunRecent(set_up_list::SetUpListDurationPastFirstRun() -
+                        trigger_delta)) {
+    return false;
+  }
+  return !set_up_list_prefs::AllItemsComplete(local_state_);
+}
+
+bool TipsNotificationCriteria::ShouldSendDocking() {
+  return !FETHasEverTriggered(feature_engagement::kIPHiOSDockingPromoFeature) &&
+         !FETHasEverTriggered(
+             feature_engagement::kIPHiOSDockingPromoRemindMeLaterFeature);
+}
+
+bool TipsNotificationCriteria::ShouldSendOmniboxPosition() {
+  // OmniboxPositionChoice is only available on phones.
+  if (ui::GetDeviceFormFactor() != ui::DEVICE_FORM_FACTOR_PHONE) {
+    return false;
+  }
+  return !local_state_->GetUserPrefValue(prefs::kBottomOmnibox);
+}
+
+bool TipsNotificationCriteria::ShouldSendLens() {
+  // Early return if Lens is not available or disabled by policy.
+  TemplateURLService* template_url_service =
+      ios::TemplateURLServiceFactory::GetForProfile(profile_);
+  bool default_search_is_google =
+      search::DefaultSearchProviderIsGoogle(template_url_service);
+  const bool lens_enabled =
+      lens_availability::CheckAndLogAvailabilityForLensEntryPoint(
+          LensEntrypoint::NewTabPage, default_search_is_google);
+  if (!lens_enabled) {
+    return false;
+  }
+
+  base::Time last_opened = local_state_->GetTime(prefs::kLensLastOpened);
+  return !IsRecent(last_opened, kLensOpenedRecency);
+}
+
+bool TipsNotificationCriteria::ShouldSendEnhancedSafeBrowsing() {
+  return profile_prefs_->GetBoolean(prefs::kAdvancedProtectionAllowed) &&
+         !safe_browsing::IsEnhancedProtectionEnabled(*profile_prefs_);
+}
+
+bool TipsNotificationCriteria::ShouldSendCPE() {
+  if (!local_state_->GetBoolean(
+          prefs::kIosCredentialProviderPromoPolicyEnabled)) {
+    return false;
+  }
+  bool is_credential_provider_enabled =
+      password_manager_util::IsCredentialProviderEnabledOnStartup(local_state_);
+  if (is_credential_provider_enabled) {
+    return false;
+  }
+  base::Time promo_display_time =
+      local_state_->GetTime(prefs::kIosCredentialProviderPromoDisplayTime);
+  if (IsRecent(promo_display_time, kCPEPromoRecency)) {
+    return false;
+  }
+  base::Time login_time =
+      local_state_->GetTime(prefs::kIosSuccessfulLoginWithExistingPassword);
+  return IsRecent(login_time, kSuccessfullLoginRecency);
+}
+
+bool TipsNotificationCriteria::ShouldSendLensOverlay() {
+  base::Time lens_overlay_last_presented =
+      local_state_->GetTime(prefs::kLensOverlayLastPresented);
+  return !IsRecent(lens_overlay_last_presented, kLensOverlayRecency);
+}
+
+bool TipsNotificationCriteria::FETHasEverTriggered(
+    const base::Feature& feature) {
+  feature_engagement::Tracker* tracker =
+      feature_engagement::TrackerFactory::GetForProfile(profile_);
+  return tracker->HasEverTriggered(feature, true);
+}
diff --git a/ios/chrome/browser/tips_notifications/model/utils.h b/ios/chrome/browser/tips_notifications/model/utils.h
index 2939115..ff659188 100644
--- a/ios/chrome/browser/tips_notifications/model/utils.h
+++ b/ios/chrome/browser/tips_notifications/model/utils.h
@@ -16,6 +16,7 @@
 }
 
 enum class NotificationType;
+class PrefService;
 
 // Identifier for the tips notification.
 extern NSString* const kTipsNotificationId;
@@ -125,4 +126,11 @@
 // Returns 0 if it was not set.
 int TipsNotificationTriggerExperimentalSetting();
 
+// Returns the type indicating how the user was classified.
+TipsNotificationUserType GetTipsNotificationUserType(PrefService* local_state);
+
+// Sets the user's classification in local state prefs.
+void SetTipsNotificationUserType(PrefService* local_state,
+                                 TipsNotificationUserType user_type);
+
 #endif  // IOS_CHROME_BROWSER_TIPS_NOTIFICATIONS_MODEL_UTILS_H_
diff --git a/ios/chrome/browser/tips_notifications/model/utils.mm b/ios/chrome/browser/tips_notifications/model/utils.mm
index b6a7c1a5..b324b92 100644
--- a/ios/chrome/browser/tips_notifications/model/utils.mm
+++ b/ios/chrome/browser/tips_notifications/model/utils.mm
@@ -8,6 +8,7 @@
 #import "base/strings/string_split.h"
 #import "base/strings/sys_string_conversions.h"
 #import "base/time/time.h"
+#import "components/prefs/pref_service.h"
 #import "ios/chrome/browser/push_notification/model/constants.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/grit/ios_branded_strings.h"
@@ -290,3 +291,13 @@
   return [[NSUserDefaults standardUserDefaults]
       integerForKey:kTipsNotificationTrigger];
 }
+
+TipsNotificationUserType GetTipsNotificationUserType(PrefService* local_state) {
+  return static_cast<TipsNotificationUserType>(
+      local_state->GetInteger(kTipsNotificationsUserType));
+}
+
+void SetTipsNotificationUserType(PrefService* local_state,
+                                 TipsNotificationUserType user_type) {
+  local_state->SetInteger(kTipsNotificationsUserType, int(user_type));
+}
diff --git a/ios/chrome/test/data/policy/policy_test_bundle_data.filelist b/ios/chrome/test/data/policy/policy_test_bundle_data.filelist
index 86bc0ad3..cdf559d 100644
--- a/ios/chrome/test/data/policy/policy_test_bundle_data.filelist
+++ b/ios/chrome/test/data/policy/policy_test_bundle_data.filelist
@@ -5,6 +5,7 @@
 #       If it requires updating, you should get a presubmit error with
 #       instructions on how to regenerate. Otherwise, do not edit.
 //ios/chrome/test/data/policy/pref_mapping/AIModeSearchSuggestSettings.json
+//ios/chrome/test/data/policy/pref_mapping/AIModeSettings.json
 //ios/chrome/test/data/policy/pref_mapping/AllowChromeDataInBackups.json
 //ios/chrome/test/data/policy/pref_mapping/AppStoreRatingEnabled.json
 //ios/chrome/test/data/policy/pref_mapping/AutofillAddressEnabled.json
diff --git a/ios/chrome/test/data/policy/pref_mapping/AIModeSettings.json b/ios/chrome/test/data/policy/pref_mapping/AIModeSettings.json
new file mode 100644
index 0000000..3c37d8c
--- /dev/null
+++ b/ios/chrome/test/data/policy/pref_mapping/AIModeSettings.json
@@ -0,0 +1,15 @@
+[
+  {
+    "os": [
+      "ios"
+    ],
+    "simple_policy_pref_mapping_test": {
+      "pref_name": "omnibox.ai_mode_settings",
+      "default_value": 0,
+      "values_to_test": [
+        0,
+        1
+      ]
+    }
+  }
+]
diff --git a/ios/chrome/test/data/policy/pref_mapping/GenAiDefaultSettings.json b/ios/chrome/test/data/policy/pref_mapping/GenAiDefaultSettings.json
index b7117ae..e0834287 100644
--- a/ios/chrome/test/data/policy/pref_mapping/GenAiDefaultSettings.json
+++ b/ios/chrome/test/data/policy/pref_mapping/GenAiDefaultSettings.json
@@ -11,6 +11,9 @@
         "prefs": {
           "omnibox.ai_mode_search_suggest_settings": {
             "value": 0
+          },
+          "omnibox.ai_mode_settings": {
+            "value": 0
           }
         }
       },
@@ -21,6 +24,9 @@
         "prefs": {
           "omnibox.ai_mode_search_suggest_settings": {
             "value": 0
+          },
+          "omnibox.ai_mode_settings": {
+            "value": 0
           }
         }
       },
@@ -31,6 +37,9 @@
         "prefs": {
           "omnibox.ai_mode_search_suggest_settings": {
             "value": 1
+          },
+          "omnibox.ai_mode_settings": {
+            "value": 1
           }
         }
       },
@@ -39,6 +48,31 @@
         "prefs": {
           "omnibox.ai_mode_search_suggest_settings": {
             "default_value": 0
+          },
+          "omnibox.ai_mode_settings": {
+            "default_value": 0
+          }
+        }
+      },
+      {
+        "policies": {
+          "GenAiDefaultSettings": 1,
+          "AIModeSettings": 1
+        },
+        "prefs": {
+          "omnibox.ai_mode_settings": {
+            "value": 1
+          }
+        }
+      },
+      {
+        "policies": {
+          "GenAiDefaultSettings": 2,
+          "AIModeSettings": 1
+        },
+        "prefs": {
+          "omnibox.ai_mode_settings": {
+            "value": 1
           }
         }
       }
diff --git a/ios/web/find_in_page/BUILD.gn b/ios/web/find_in_page/BUILD.gn
index 0a45377..3f8a01bf 100644
--- a/ios/web/find_in_page/BUILD.gn
+++ b/ios/web/find_in_page/BUILD.gn
@@ -41,7 +41,6 @@
   sources = [
     "resources/find_in_page.ts",
     "resources/find_in_page_constants.ts",
-    "resources/find_in_page_utils.ts",
   ]
 }
 
diff --git a/ios/web/find_in_page/resources/find_in_page_constants.ts b/ios/web/find_in_page/resources/find_in_page_constants.ts
index 8379b81..ff8ee0b2 100644
--- a/ios/web/find_in_page/resources/find_in_page_constants.ts
+++ b/ios/web/find_in_page/resources/find_in_page_constants.ts
@@ -40,3 +40,8 @@
  * Result passed back to app to indicate pumpSearch has reached timeout.
  */
 export const TIMEOUT: number = -1;
+
+/**
+ * Regex to escape regex special characters in a string.
+ */
+export const REGEX_ESCAPER: RegExp = /([.?*+^$[\]\\(){}|-])/g;
diff --git a/ios/web/find_in_page/resources/find_in_page_native_api.ts b/ios/web/find_in_page/resources/find_in_page_native_api.ts
index 0128136f..a0768d3 100644
--- a/ios/web/find_in_page/resources/find_in_page_native_api.ts
+++ b/ios/web/find_in_page/resources/find_in_page_native_api.ts
@@ -10,11 +10,10 @@
   IGNORE_NODE_NAMES,
   MAX_VISIBLE_ELEMENTS,
   TIMEOUT,
+  REGEX_ESCAPER,
 } from '//ios/web/find_in_page/resources/find_in_page_constants.js';
 import {Match, PartialMatch, Replacement, Section, Timer} from
     '//ios/web/find_in_page/resources/find_in_page.js';
-import {createRegex, escapeHTML} from
-    '//ios/web/find_in_page/resources/find_in_page_utils.js';
 import {gCrWebLegacy} from '//ios/web/public/js_messaging/resources/gcrweb.js';
 // clang-format on
 
@@ -113,12 +112,30 @@
 let searchInProgress_ = false;
 
 /**
-
  * Whether or not search state variables are in a clean empty state.
  * @type {boolean}
  */
 let searchStateIsClean_ = true;
 
+/**
+ * Disables or enables the __gCrWeb.findInPage module.
+ * @type {boolean}
+ */
+let hasInitialized_: boolean = false;
+
+/**
+ * Holds what nodes we have not processed yet during the finding in page
+ * process.
+ * @type {Array<HTMLElement>}
+ */
+let pendingElements_: HTMLElement[] = [];
+
+/**
+ * Holds the regex for the string that we are trying to find in a page.
+ * @type {RegExp}
+ */
+let textToFindRegex_: RegExp|undefined;
+
 // Mark: Private helper functions
 
 /**
@@ -342,9 +359,9 @@
  */
 function findString(string: string, timeout: number): number {
   // Enable findInPage module if hasn't been done yet.
-  if (!gCrWebLegacy.findInPage.hasInitialized) {
+  if (!hasInitialized_) {
     enable();
-    gCrWebLegacy.findInPage.hasInitialized = true;
+    hasInitialized_ = true;
   }
 
   if (!searchStateIsClean_) {
@@ -357,7 +374,7 @@
   }
 
   // Holds what nodes we have not processed yet.
-  gCrWebLegacy.findInPage.stack = [document.body];
+  pendingElements_ = [document.body];
 
   // Number of visible matches found.
   visibleMatchCount_ = 0;
@@ -365,7 +382,7 @@
   // Index tracking variables so search can be broken up into multiple calls.
   visibleMatchesCountIndexIterator_ = 0;
 
-  gCrWebLegacy.findInPage.regex = createRegex(string);
+  textToFindRegex_ = createRegex(string);
 
   searchInProgress_ = true;
 
@@ -391,7 +408,6 @@
  * @return {number} that represents the total matches found.
  */
 function pumpSearch(timeout: number): number {
-  // TODO(crbug.com/41420794): It would be better if this DCHECKed.
   if (!searchInProgress_) {
     return 0;
   }
@@ -401,36 +417,41 @@
   const timer = new Timer(timeout);
 
   // Go through every node in DFS fashion.
-  while (gCrWebLegacy.findInPage.stack.length) {
-    const node = gCrWebLegacy.findInPage.stack.pop();
-    const children = node.childNodes;
-    if (children && children.length) {
-      // add all (reasonable) children
-      for (let i = children.length - 1; i >= 0; --i) {
-        const child = children[i];
-        if ((child.nodeType === 1 || child.nodeType === 3) &&
-            !IGNORE_NODE_NAMES.has(child.nodeName)) {
-          gCrWebLegacy.findInPage.stack.push(children[i]);
+  while (pendingElements_.length) {
+    const node = pendingElements_.pop();
+    if (node) {
+      const children = node.childNodes;
+      if (children && children.length) {
+        // add all (reasonable) children
+        for (let i = children.length - 1; i >= 0; --i) {
+          const child = children[i];
+          if (child) {
+            if ((child.nodeType === 1 || child.nodeType === 3) &&
+                !IGNORE_NODE_NAMES.has(child.nodeName)) {
+              pendingElements_.push(children[i] as HTMLElement);
+            }
+          }
         }
       }
-    }
 
-    // Build up |allText_| and |sections_|.
-    if (node.nodeType === 3 && node.parentNode) {
-      sections_.push(new Section(
-          allText_.length, allText_.length + node.textContent.length, node));
-      allText_ += node.textContent.toLowerCase();
-    }
+      // Build up |allText_| and |sections_|.
+      if (node.nodeType === 3 && node.parentNode) {
+        sections_.push(new Section(
+            allText_.length, allText_.length + (node.textContent?.length ?? 0),
+            node));
+        allText_ += node.textContent?.toLowerCase();
+      }
 
-    if (timer.overtime()) {
-      return TIMEOUT;
+      if (timer.overtime()) {
+        return TIMEOUT;
+      }
     }
   }
 
   // Do regex match in |allText_|, create |matches| and |replacements|. The
   // regex is set on __gCrWeb, so its state is kept between continuous calls on
   // pumpSearch.
-  const regex = gCrWebLegacy.findInPage.regex;
+  const regex = textToFindRegex_;
   if (regex) {
     for (let res; res = regex.exec(allText_);) {
       // The range of current Match in |allText_| is [begin, end).
@@ -475,7 +496,7 @@
     }
     // Process remaining PartialMatches.
     processPartialMatchesInCurrentSection();
-    gCrWebLegacy.findInPage.regex = undefined;
+    textToFindRegex_ = undefined;
   }
 
   // Execute replacements to highlight search results.
@@ -606,7 +627,7 @@
     removeStyle();
     cleanUp();
   }
-  gCrWebLegacy.findInPage.hasInitialized = false;
+  hasInitialized_ = false;
 }
 
 // Mark: Public API
@@ -617,6 +638,27 @@
  */
 const matches: Match[] = [];
 
+/**
+ * Creates the regex needed to find the text.
+ * @param findText Phrase to look for.
+ * @return regex needed to find the text.
+ */
+function createRegex(findText: string): RegExp {
+  const escapedText = findText.replace(REGEX_ESCAPER, '\\$1');
+  const regexString = '(' + escapedText + ')';
+  return new RegExp(regexString, 'ig');
+}
+
+/**
+ * @param text Text to escape.
+ * @return escaped text.
+ */
+function escapeHTML(text: string): string {
+  const unusedDiv = document.createElement('div');
+  unusedDiv.innerText = text;
+  return unusedDiv.innerHTML;
+}
+
 gCrWebLegacy.findInPage = {
   findString,
   matches,
diff --git a/ios/web/find_in_page/resources/find_in_page_utils.ts b/ios/web/find_in_page/resources/find_in_page_utils.ts
deleted file mode 100644
index cd9235a..0000000
--- a/ios/web/find_in_page/resources/find_in_page_utils.ts
+++ /dev/null
@@ -1,29 +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.
-
-/**
- * Regex to escape regex special characters in a string.
- */
-const REGEX_ESCAPER: RegExp = /([.?*+^$[\]\\(){}|-])/g;
-
-/**
- * Creates the regex needed to find the text.
- * @param findText Phrase to look for.
- * @return regex needed to find the text.
- */
-export function createRegex(findText: string): RegExp {
-  const escapedText = findText.replace(REGEX_ESCAPER, '\\$1');
-  const regexString = '(' + escapedText + ')';
-  return new RegExp(regexString, 'ig');
-}
-
-/**
- * @param text Text to escape.
- * @return escaped text.
- */
-export function escapeHTML(text: string): string {
-  const unusedDiv = document.createElement('div');
-  unusedDiv.innerText = text;
-  return unusedDiv.innerHTML;
-}
diff --git a/ios_internal b/ios_internal
index c98c232..badd9e0 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit c98c23282a810917184ef42c8f5c8724474266e9
+Subproject commit badd9e01b2f87ed45b48ca5b26ede8cbafb95ed4
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index d6046711..b9faacd 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -1096,6 +1096,13 @@
              "MediaDrmGetStatusForPolicy",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+// This feature allows for some MediaDrm functions to be executed in a separate
+// process so that crashes do not bring down the browser. Flag is available so
+// that it can be disabled for WebView as separate processes are not allowed.
+BASE_FEATURE(kMediaDrmQueryInSeparateProcess,
+             "MediaDrmQueryInSeparateProcess",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 // When enabled, Playing media sessions will request audio focus from the
 // Android system.
 BASE_FEATURE(kRequestSystemAudioFocus,
@@ -1123,13 +1130,6 @@
              "AllowMediaCodecSoftwareDecoder",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// This feature allows for some MediaDrm functions to be executed in a separate
-// process so that crashes do not bring down the browser. Flag is available so
-// that it can be disabled for WebView as separate processes are not allowed.
-BASE_FEATURE(kAllowMediaCodecCallsInSeparateProcess,
-             "AllowMediaCodecCallsInSeparateProcess",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Allows Chrome to query Android for supported layouts, and forces the use
 // of the layout with the maximum number of channels. This avoids
 // downmixing (and losing channel information) if a media file starts with
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index 383ae64..45222f1 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -396,11 +396,11 @@
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kMediaDrmPreprovisioning);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kMediaDrmPreprovisioningAtStartup);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kMediaDrmGetStatusForPolicy);
+MEDIA_EXPORT BASE_DECLARE_FEATURE(kMediaDrmQueryInSeparateProcess);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kRequestSystemAudioFocus);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kUseAudioLatencyFromHAL);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kUseSecurityLevelWhenCheckingMediaDrmVersion);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kAllowMediaCodecSoftwareDecoder);
-MEDIA_EXPORT BASE_DECLARE_FEATURE(kAllowMediaCodecCallsInSeparateProcess);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kUseAudioManagerMaxChannelLayout);
 #endif  // BUILDFLAG(IS_ANDROID)
 
diff --git a/media/gpu/chromeos/mailbox_video_frame_converter_unittest.cc b/media/gpu/chromeos/mailbox_video_frame_converter_unittest.cc
index b7c6502..dcc76a08 100644
--- a/media/gpu/chromeos/mailbox_video_frame_converter_unittest.cc
+++ b/media/gpu/chromeos/mailbox_video_frame_converter_unittest.cc
@@ -153,13 +153,14 @@
   MOCK_METHOD1(ImportSharedImage,
                scoped_refptr<gpu::ClientSharedImage>(
                    gpu::ExportedSharedImage exported_shared_image));
-  MOCK_METHOD6(CreateSwapChain,
+  MOCK_METHOD7(CreateSwapChain,
                SwapChainSharedImages(viz::SharedImageFormat format,
                                      const gfx::Size& size,
                                      const gfx::ColorSpace& color_space,
                                      GrSurfaceOrigin surface_origin,
                                      SkAlphaType alpha_type,
-                                     gpu::SharedImageUsageSet usage));
+                                     gpu::SharedImageUsageSet usage,
+                                     std::string_view debug_label));
   MOCK_METHOD2(PresentSwapChain,
                void(const gpu::SyncToken& sync_token,
                     const gpu::Mailbox& mailbox));
diff --git a/net/extras/sqlite/sqlite_persistent_cookie_store.cc b/net/extras/sqlite/sqlite_persistent_cookie_store.cc
index b3a2e830..55f44d5 100644
--- a/net/extras/sqlite/sqlite_persistent_cookie_store.cc
+++ b/net/extras/sqlite/sqlite_persistent_cookie_store.cc
@@ -1617,8 +1617,7 @@
               continue;
             }
             add_statement.BindCString(4, "");  // value
-            // BindBlob() immediately makes an internal copy of the data.
-            add_statement.BindBlob(5, encrypted_value);
+            add_statement.BindBlob(5, std::move(encrypted_value));
           } else {
             add_statement.BindString(4, po->cc().Value());
             add_statement.BindBlob(5,
diff --git a/services/data_decoder/ble_scan_parser_impl_unittest.cc b/services/data_decoder/ble_scan_parser_impl_unittest.cc
index bf1d4a7..873cd18 100644
--- a/services/data_decoder/ble_scan_parser_impl_unittest.cc
+++ b/services/data_decoder/ble_scan_parser_impl_unittest.cc
@@ -8,39 +8,76 @@
 
 namespace data_decoder {
 
-TEST(BleScanParserImplTest, ParseBadUuidLengthReturnsEmptyString) {
+template <typename Traits>
+class BleScanParserImplTest : public testing::Test {
+ protected:
+  // These helpers could be static, but at some point in the future, calling a
+  // static method via -> might be a clang-tidy warning. The alternative is
+  // writing out BleScanParserImplTest::ParseUuid, et cetera, and nobody wants
+  // that.
+  device::BluetoothUUID ParseUuid(base::span<const uint8_t> bytes,
+                                  UuidFormat format) {
+    return Traits::ParseUuid(bytes, format);
+  }
+
+  bool ParseServiceUuids(base::span<const uint8_t> bytes,
+                         UuidFormat format,
+                         std::vector<device::BluetoothUUID>& out) {
+    return Traits::ParseServiceUuids(bytes, format, out);
+  }
+
+  mojom::ScanRecordPtr ParseBleScan(base::span<const uint8_t> bytes) {
+    return Traits::ParseBleScan(bytes);
+  }
+};
+
+struct CxxParserTraits {
+  static device::BluetoothUUID ParseUuid(base::span<const uint8_t> bytes,
+                                         UuidFormat format) {
+    return BleScanParserImpl::ParseUuid(bytes, format);
+  }
+
+  static bool ParseServiceUuids(base::span<const uint8_t> bytes,
+                                UuidFormat format,
+                                std::vector<device::BluetoothUUID>& out) {
+    return BleScanParserImpl::ParseServiceUuids(bytes, format, &out);
+  }
+
+  static mojom::ScanRecordPtr ParseBleScan(base::span<const uint8_t> bytes) {
+    return BleScanParserImpl::ParseBleScan(bytes);
+  }
+};
+
+using ParserImpls = ::testing::Types<CxxParserTraits>;
+TYPED_TEST_SUITE(BleScanParserImplTest, ParserImpls);
+
+TYPED_TEST(BleScanParserImplTest, ParseBadUuidLengthReturnsEmptyString) {
   std::vector<uint8_t> bad_uuid(0xab, 5);
-  EXPECT_FALSE(BleScanParserImpl::ParseUuid(bad_uuid, UuidFormat::kFormat16Bit)
-                   .IsValid());
-  EXPECT_FALSE(BleScanParserImpl::ParseUuid(bad_uuid, UuidFormat::kFormat32Bit)
-                   .IsValid());
-  EXPECT_FALSE(BleScanParserImpl::ParseUuid(bad_uuid, UuidFormat::kFormat128Bit)
-                   .IsValid());
+  EXPECT_FALSE(this->ParseUuid(bad_uuid, UuidFormat::kFormat16Bit).IsValid());
+  EXPECT_FALSE(this->ParseUuid(bad_uuid, UuidFormat::kFormat32Bit).IsValid());
+  EXPECT_FALSE(this->ParseUuid(bad_uuid, UuidFormat::kFormat128Bit).IsValid());
 }
 
-TEST(BleScanParserImplTest, Parse16BitUuid) {
+TYPED_TEST(BleScanParserImplTest, Parse16BitUuid) {
   const uint8_t kUuid16[] = {0xab, 0xcd};
   const device::BluetoothUUID kExpected("0000CDAB-0000-1000-8000-00805F9B34FB");
-  EXPECT_EQ(kExpected,
-            BleScanParserImpl::ParseUuid(kUuid16, UuidFormat::kFormat16Bit));
+  EXPECT_EQ(kExpected, this->ParseUuid(kUuid16, UuidFormat::kFormat16Bit));
 }
 
-TEST(BleScanParserImplTest, Parse32BitUuid) {
+TYPED_TEST(BleScanParserImplTest, Parse32BitUuid) {
   const uint8_t kUuid32[] = {0xab, 0xcd, 0xef, 0x01};
   const device::BluetoothUUID kExpected("01EFCDAB-0000-1000-8000-00805F9B34FB");
-  EXPECT_EQ(kExpected,
-            BleScanParserImpl::ParseUuid(kUuid32, UuidFormat::kFormat32Bit));
+  EXPECT_EQ(kExpected, this->ParseUuid(kUuid32, UuidFormat::kFormat32Bit));
 }
 
-TEST(BleScanParserImplTest, Parse128BitUuid) {
+TYPED_TEST(BleScanParserImplTest, Parse128BitUuid) {
   const uint8_t kUuid128[] = {0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89,
                               0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89};
   const device::BluetoothUUID kExpected("89674523-01EF-CDAB-8967-452301EFCDAB");
-  EXPECT_EQ(kExpected,
-            BleScanParserImpl::ParseUuid(kUuid128, UuidFormat::kFormat128Bit));
+  EXPECT_EQ(kExpected, this->ParseUuid(kUuid128, UuidFormat::kFormat128Bit));
 }
 
-TEST(BleScanParserImplTest, Parse16BitServiceUuids) {
+TYPED_TEST(BleScanParserImplTest, Parse16BitServiceUuids) {
   std::vector<device::BluetoothUUID> expected = {
       device::BluetoothUUID("0000CDAB-0000-1000-8000-00805F9B34FB"),
       device::BluetoothUUID("000001EF-0000-1000-8000-00805F9B34FB"),
@@ -56,12 +93,12 @@
                             0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89};
 
   std::vector<device::BluetoothUUID> actual;
-  BleScanParserImpl::ParseServiceUuids(kUuids, UuidFormat::kFormat16Bit,
-                                       &actual);
+  EXPECT_TRUE(
+      this->ParseServiceUuids(kUuids, UuidFormat::kFormat16Bit, actual));
   EXPECT_EQ(expected, actual);
 }
 
-TEST(BleScanParserImplTest, Parse32BitServiceUuids) {
+TYPED_TEST(BleScanParserImplTest, Parse32BitServiceUuids) {
   std::vector<device::BluetoothUUID> expected = {
       device::BluetoothUUID("01EFCDAB-0000-1000-8000-00805F9B34FB"),
       device::BluetoothUUID("89674523-0000-1000-8000-00805F9B34FB"),
@@ -73,12 +110,12 @@
                             0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89};
 
   std::vector<device::BluetoothUUID> actual;
-  BleScanParserImpl::ParseServiceUuids(kUuids, UuidFormat::kFormat32Bit,
-                                       &actual);
+  EXPECT_TRUE(
+      this->ParseServiceUuids(kUuids, UuidFormat::kFormat32Bit, actual));
   EXPECT_EQ(expected, actual);
 }
 
-TEST(BleScanParserImplTest, Parse128BitServiceUuids) {
+TYPED_TEST(BleScanParserImplTest, Parse128BitServiceUuids) {
   std::vector<device::BluetoothUUID> expected = {
       device::BluetoothUUID("89674523-01EF-CDAB-8967-452301EFCDAB"),
       device::BluetoothUUID("89674523-01EF-CDAB-01EF-CDAB89674523"),
@@ -90,12 +127,12 @@
                             0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89};
 
   std::vector<device::BluetoothUUID> actual;
-  BleScanParserImpl::ParseServiceUuids(kUuids, UuidFormat::kFormat128Bit,
-                                       &actual);
+  EXPECT_TRUE(
+      this->ParseServiceUuids(kUuids, UuidFormat::kFormat128Bit, actual));
   EXPECT_EQ(expected, actual);
 }
 
-TEST(BleScanParserImplTest, ParseBleAdvertisingScan) {
+TYPED_TEST(BleScanParserImplTest, ParseBleAdvertisingScan) {
   std::vector<device::BluetoothUUID> expected_service_uuids = {
       device::BluetoothUUID("0000ABCD-0000-1000-8000-00805F9B34FB"),
       device::BluetoothUUID("0000EF01-0000-1000-8000-00805F9B34FB"),
@@ -134,7 +171,7 @@
       // Manufacturer data map 0xd00d => { 0x1a, 0x2b, 0x3c, 0x4d }
       0x07, 0xff, 0x0d, 0xd0, 0x1a, 0x2b, 0x3c, 0x4d};
 
-  mojom::ScanRecordPtr actual = BleScanParserImpl::ParseBleScan(kRawData);
+  mojom::ScanRecordPtr actual = this->ParseBleScan(kRawData);
   ASSERT_TRUE(actual);
   EXPECT_EQ(0x42, actual->advertising_flags);
   EXPECT_EQ(0x1b, actual->tx_power);
diff --git a/services/device/public/cpp/test/fake_sensor_and_provider.cc b/services/device/public/cpp/test/fake_sensor_and_provider.cc
index ba34b97..0090c25 100644
--- a/services/device/public/cpp/test/fake_sensor_and_provider.cc
+++ b/services/device/public/cpp/test/fake_sensor_and_provider.cc
@@ -8,7 +8,6 @@
 #include <memory>
 #include <utility>
 
-#include "base/debug/stack_trace.h"
 #include "base/notreached.h"
 #include "base/time/time.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
diff --git a/services/webnn/coreml/context_impl_coreml.mm b/services/webnn/coreml/context_impl_coreml.mm
index 3343a65..4d70405c 100644
--- a/services/webnn/coreml/context_impl_coreml.mm
+++ b/services/webnn/coreml/context_impl_coreml.mm
@@ -61,6 +61,14 @@
                           "Creation of constant tensors is not supported.")));
     return;
   }
+  // TODO(crbug.com/345352987): implement WebGPU interop tensors for CoreML
+  // backend.
+  if (tensor_info->usage.Has(MLTensorUsageFlags::kWebGpuInterop)) {
+    std::move(callback).Run(base::unexpected(
+        mojom::Error::New(mojom::Error::Code::kNotSupportedError,
+                          "WebGPU Interop is not supported.")));
+    return;
+  }
   std::move(callback).Run(TensorImplCoreml::Create(std::move(receiver), this,
                                                    std::move(tensor_info)));
 }
diff --git a/services/webnn/dml/context_impl_dml.cc b/services/webnn/dml/context_impl_dml.cc
index 0c236cf..76d0283 100644
--- a/services/webnn/dml/context_impl_dml.cc
+++ b/services/webnn/dml/context_impl_dml.cc
@@ -635,6 +635,15 @@
     mojo::PendingAssociatedReceiver<mojom::WebNNTensor> receiver,
     mojom::TensorInfoPtr tensor_info,
     CreateTensorImplCallback callback) {
+  // TODO(crbug.com/345352987): implement WebGPU interop tensors for DirectML
+  // backend.
+  if (tensor_info->usage.Has(MLTensorUsageFlags::kWebGpuInterop)) {
+    std::move(callback).Run(base::unexpected(
+        mojom::Error::New(mojom::Error::Code::kNotSupportedError,
+                          "WebGPU Interop is not supported.")));
+    return;
+  }
+
   if (g_backend_for_testing) {
     g_backend_for_testing->CreateTensorImpl(
         this, std::move(receiver), std::move(tensor_info), std::move(callback));
diff --git a/services/webnn/tflite/context_impl_tflite.cc b/services/webnn/tflite/context_impl_tflite.cc
index eab9d202..d6c11816 100644
--- a/services/webnn/tflite/context_impl_tflite.cc
+++ b/services/webnn/tflite/context_impl_tflite.cc
@@ -57,6 +57,14 @@
                           "Creation of constant tensors is not supported.")));
     return;
   }
+  // TODO(crbug.com/345352987): implement WebGPU interop tensors for TFLite
+  // backend.
+  if (tensor_info->usage.Has(MLTensorUsageFlags::kWebGpuInterop)) {
+    std::move(callback).Run(base::unexpected(
+        mojom::Error::New(mojom::Error::Code::kNotSupportedError,
+                          "WebGPU Interop is not supported.")));
+    return;
+  }
   std::move(callback).Run(TensorImplTflite::Create(std::move(receiver), this,
                                                    std::move(tensor_info)));
 }
diff --git a/sql/database.cc b/sql/database.cc
index 6db6662..ca79678 100644
--- a/sql/database.cc
+++ b/sql/database.cc
@@ -297,6 +297,33 @@
   Close(false);
 }
 
+void Database::StatementRef::Reset(bool clear_bound_variables) {
+  if (clear_bound_variables) {
+    std::ignore = ToSqliteResultCode(sqlite3_clear_bindings(stmt()));
+    bound_blobs_.clear();
+  }
+
+  // ToSqliteResultCode() is called to ensure that sqlite3_reset() doesn't
+  // return a concerning code, such as SQLITE_MISUSE. The processed error code
+  // is ignored because sqlite3_reset() returns an error code if the last
+  // sqlite3_step() failed, and that error was already reported when we ran
+  // sqlite3_step(), via Statement::Run() or Statement::Step().
+  std::ignore = ToSqliteResultCode(sqlite3_reset(stmt()));
+}
+
+base::span<const uint8_t> Database::StatementRef::TakeBlobMemory(
+    int param_index,
+    scoped_refptr<base::RefCountedMemory> blob) {
+  auto inserted = bound_blobs_.emplace(param_index, std::move(blob));
+  CHECK(inserted.second) << "Parameter unexpectedly bound twice: "
+                         << param_index;
+  return *inserted.first->second;
+}
+
+void Database::StatementRef::ClearBlobMemory(int param_index) {
+  bound_blobs_.erase(param_index);
+}
+
 void Database::StatementRef::Close(bool forced) {
   if (stmt_) {
     // Call to InitScopedBlockingCall() cannot go at the beginning of the
@@ -319,6 +346,8 @@
     // error as the most recent sqlite3_step(). The result code is passed
     // through ToSqliteResultCode() to catch issues like SQLITE_MISUSE.
     std::ignore = ToSqliteResultCode(sqlite3_finalize(statement));
+
+    bound_blobs_.clear();
   }
   database_ = nullptr;  // The Database may be getting deleted.
 
@@ -1585,21 +1614,16 @@
     base::cstring_view sql) {
   auto it = statement_cache_.find(id);
   if (it != statement_cache_.end()) {
+    StatementRef& statement = *it->second;
     // Statement is in the cache. It should still be valid. We're the only
     // entity invalidating cached statements, and we remove them from the cache
     // when we do that.
-    DCHECK(it->second->is_valid());
-    DCHECK_EQ(std::string(sqlite3_sql(it->second->stmt())), std::string(sql))
+    DCHECK(statement.is_valid());
+    DCHECK_EQ(base::cstring_view(sqlite3_sql(statement.stmt())), sql)
         << "GetCachedStatement used with same ID but different SQL";
 
     // Reset the statement so it can be reused.
-    //
-    // ToSqliteResultCode() is called to ensure that sqlite3_reset() doesn't
-    // return a concerning code, such as SQLITE_MISUSE. The processed error code
-    // is ignored because sqlite3_reset() returns an error code if the last
-    // sqlite3_step() failed, and that error was already reported when we ran
-    // sqlite3_step(), via Statement::Run() or Statement::Step().
-    std::ignore = ToSqliteResultCode(sqlite3_reset(it->second->stmt()));
+    statement.Reset(/*clear_bound_variables=*/true);
     return it->second;
   }
 
diff --git a/sql/database.h b/sql/database.h
index 2cc49c0d..651ac4d 100644
--- a/sql/database.h
+++ b/sql/database.h
@@ -26,6 +26,7 @@
 #include "base/location.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/ref_counted_memory.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/notreached.h"
@@ -928,6 +929,27 @@
     // this will return nullptr.
     sqlite3_stmt* stmt() const { return stmt_; }
 
+    // Assumes ownership of `blob`.
+    //
+    // To be called BEFORE the data in `blob` will be bound to a SQLite
+    // statement SQLite. SQLite assumes the pointer will remain valid until the
+    // statement is finalized or the parameter is unbound.
+    //
+    // A span pointing to the newly owned memory is returned --- this is the
+    // pointer that should be passed to sqlite3 functions.
+    base::span<const uint8_t> TakeBlobMemory(
+        int index,
+        scoped_refptr<base::RefCountedMemory> blob);
+
+    // Releases memory passed by `TakeBlobMemory()`, if any. The caller should
+    // also tell SQLite to unbind or rebind the parameter (i.e. update the
+    // binding that was previously set with TakeBlobMemory's output).
+    void ClearBlobMemory(int index);
+
+    // Resets the statement and, if `clear_bound_variables` is true, drops
+    // parameter bindings, including dropping `bound_blobs_`.
+    void Reset(bool clear_bound_variables);
+
     // Destroys the compiled statement and sets it to nullptr. The statement
     // will no longer be active. |forced| is used to indicate if
     // orderly-shutdown checks should apply (see Database::RazeAndPoison()).
@@ -948,6 +970,13 @@
 
     ~StatementRef();
 
+    // Holds onto memory that is to be used by the statement. These blobs have
+    // been bound with `SQLITE_STATIC`, see
+    // https://www.sqlite.org/c3ref/bind_blob.html for docs.
+    // Note that value pointer stability is important, and that's granted by
+    // scoped_refptr.
+    base::flat_map<int, scoped_refptr<base::RefCountedMemory>> bound_blobs_;
+
     raw_ptr<Database> database_;
     raw_ptr<sqlite3_stmt> stmt_;
     bool was_valid_;
diff --git a/sql/statement.cc b/sql/statement.cc
index fc81a436..4648ef4 100644
--- a/sql/statement.cc
+++ b/sql/statement.cc
@@ -185,14 +185,7 @@
     // Reports the execution cost for this SQL statement.
     ReportQueryExecutionMetrics();
 
-    if (clear_bound_vars)
-      sqlite3_clear_bindings(ref_->stmt());
-
-    // StepInternal() cannot track success because statements may be reset
-    // before reaching SQLITE_DONE.  Don't call CheckError() because
-    // sqlite3_reset() returns the last step error, which StepInternal() already
-    // checked.
-    sqlite3_reset(ref_->stmt());
+    ref_->Reset(clear_bound_vars);
   }
 
   // Potentially release dirty cache pages if an autocommit statement made
@@ -215,6 +208,13 @@
   return is_valid() && succeeded_;
 }
 
+void Statement::WillBindParameter(int param_index) {
+  DCHECK_GE(param_index, 0);
+  DCHECK_LT(param_index, sqlite3_bind_parameter_count(ref_->stmt()))
+      << "Invalid parameter index";
+  ref_->ClearBlobMemory(param_index);
+}
+
 void Statement::BindNull(int param_index) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -226,9 +226,8 @@
   if (!is_valid())
     return;
 
-  DCHECK_GE(param_index, 0);
-  DCHECK_LT(param_index, sqlite3_bind_parameter_count(ref_->stmt()))
-      << "Invalid parameter index";
+  WillBindParameter(param_index);
+
   int sqlite_result_code = sqlite3_bind_null(ref_->stmt(), param_index + 1);
   DCHECK_EQ(sqlite_result_code, SQLITE_OK);
 }
@@ -250,9 +249,7 @@
   if (!is_valid())
     return;
 
-  DCHECK_GE(param_index, 0);
-  DCHECK_LT(param_index, sqlite3_bind_parameter_count(ref_->stmt()))
-      << "Invalid parameter index";
+  WillBindParameter(param_index);
   int sqlite_result_code = sqlite3_bind_int(ref_->stmt(), param_index + 1, val);
   DCHECK_EQ(sqlite_result_code, SQLITE_OK);
 }
@@ -268,9 +265,7 @@
   if (!is_valid())
     return;
 
-  DCHECK_GE(param_index, 0);
-  DCHECK_LT(param_index, sqlite3_bind_parameter_count(ref_->stmt()))
-      << "Invalid parameter index";
+  WillBindParameter(param_index);
   int sqlite_result_code =
       sqlite3_bind_int64(ref_->stmt(), param_index + 1, val);
   DCHECK_EQ(sqlite_result_code, SQLITE_OK);
@@ -287,9 +282,7 @@
   if (!is_valid())
     return;
 
-  DCHECK_GE(param_index, 0);
-  DCHECK_LT(param_index, sqlite3_bind_parameter_count(ref_->stmt()))
-      << "Invalid parameter index";
+  WillBindParameter(param_index);
   int sqlite_result_code =
       sqlite3_bind_double(ref_->stmt(), param_index + 1, val);
   DCHECK_EQ(sqlite_result_code, SQLITE_OK);
@@ -306,9 +299,7 @@
   if (!is_valid())
     return;
 
-  DCHECK_GE(param_index, 0);
-  DCHECK_LT(param_index, sqlite3_bind_parameter_count(ref_->stmt()))
-      << "Invalid parameter index";
+  WillBindParameter(param_index);
   int64_t int_value = TimeToSqlValue(val);
   int sqlite_result_code =
       sqlite3_bind_int64(ref_->stmt(), param_index + 1, int_value);
@@ -327,9 +318,7 @@
     return;
   }
 
-  DCHECK_GE(param_index, 0);
-  DCHECK_LT(param_index, sqlite3_bind_parameter_count(ref_->stmt()))
-      << "Invalid parameter index";
+  WillBindParameter(param_index);
   int64_t int_value = delta.InMicroseconds();
   int sqlite_result_code =
       sqlite3_bind_int64(ref_->stmt(), param_index + 1, int_value);
@@ -348,9 +337,7 @@
   if (!is_valid())
     return;
 
-  DCHECK_GE(param_index, 0);
-  DCHECK_LT(param_index, sqlite3_bind_parameter_count(ref_->stmt()))
-      << "Invalid parameter index";
+  WillBindParameter(param_index);
 
   // If the string length is more than SQLITE_MAX_LENGTH (or the per-database
   // SQLITE_LIMIT_LENGTH limit), sqlite3_bind_text() fails with SQLITE_TOOBIG.
@@ -375,9 +362,7 @@
   if (!is_valid())
     return;
 
-  DCHECK_GE(param_index, 0);
-  DCHECK_LT(param_index, sqlite3_bind_parameter_count(ref_->stmt()))
-      << "Invalid parameter index";
+  WillBindParameter(param_index);
 
   // std::string_view::data() may return null for empty pieces. In particular,
   // this may happen when the std::string_view is created from the default
@@ -406,7 +391,8 @@
   return BindString(param_index, base::UTF16ToUTF8(value));
 }
 
-void Statement::BindBlob(int param_index, base::span<const uint8_t> value) {
+void Statement::BindBlob(int param_index,
+                         scoped_refptr<base::RefCountedMemory> blob) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
 #if DCHECK_IS_ON()
@@ -414,12 +400,13 @@
   DCHECK(!step_called_) << __func__ << " must not be called after Step()";
 #endif  // DCHECK_IS_ON()
 
-  if (!is_valid())
+  if (!is_valid()) {
     return;
+  }
 
-  DCHECK_GE(param_index, 0);
-  DCHECK_LT(param_index, sqlite3_bind_parameter_count(ref_->stmt()))
-      << "Invalid parameter index";
+  WillBindParameter(param_index);
+  base::span<const uint8_t> value =
+      ref_->TakeBlobMemory(param_index, std::move(blob));
 
   // span::data() may return null for empty spans. In particular, this may
   // happen when the span is created out of a std::vector, because
@@ -441,11 +428,30 @@
   // default (1 billion bytes) in Chrome's SQLite build, so this is an unlilely
   // issue.
 
-  int sqlite_result_code = sqlite3_bind_blob(
-      ref_->stmt(), param_index + 1, data, value.size(), SQLITE_TRANSIENT);
+  int sqlite_result_code = sqlite3_bind_blob(ref_->stmt(), param_index + 1,
+                                             data, value.size(), SQLITE_STATIC);
   DCHECK_EQ(sqlite_result_code, SQLITE_OK);
 }
 
+void Statement::BindBlob(int param_index, std::string blob) {
+  BindBlob(param_index,
+           base::MakeRefCounted<base::RefCountedString>(std::move(blob)));
+}
+
+void Statement::BindBlob(int param_index, std::u16string blob) {
+  BindBlob(param_index,
+           base::MakeRefCounted<base::RefCountedString16>(std::move(blob)));
+}
+
+void Statement::BindBlob(int param_index, std::vector<uint8_t> blob) {
+  BindBlob(param_index,
+           base::MakeRefCounted<base::RefCountedBytes>(std::move(blob)));
+}
+
+void Statement::BindBlob(int param_index, base::span<const uint8_t> blob) {
+  BindBlob(param_index, base::MakeRefCounted<base::RefCountedBytes>(blob));
+}
+
 int Statement::ColumnCount() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
diff --git a/sql/statement.h b/sql/statement.h
index 2513f5a..6ae2c7b 100644
--- a/sql/statement.h
+++ b/sql/statement.h
@@ -141,18 +141,16 @@
   // If you need to store (potentially invalid) UTF-16 strings losslessly,
   // store them as BLOBs instead. `BindBlob()` has an overload for this purpose.
   void BindString16(int param_index, std::u16string_view value);
+
+  // Binds a blob to the statement.
+  void BindBlob(int param_index, scoped_refptr<base::RefCountedMemory> blob);
+
+  // Convenience overloads for `BindBlob()`.
+  void BindBlob(int param_index, std::string blob);
+  void BindBlob(int param_index, std::u16string blob);
+  void BindBlob(int param_index, std::vector<uint8_t> blob);
   void BindBlob(int param_index, base::span<const uint8_t> value);
 
-  // Overload that makes it easy to pass in std::string values.
-  void BindBlob(int param_index, base::span<const char> value) {
-    BindBlob(param_index, base::as_byte_span(value));
-  }
-
-  // Overload that makes it easy to pass in std::u16string values.
-  void BindBlob(int param_index, base::span<const char16_t> value) {
-    BindBlob(param_index, base::as_byte_span(value));
-  }
-
   // Conforms with base::Time serialization recommendations.
   //
   // This is equivalent to the following snippets, which should be replaced.
@@ -294,6 +292,12 @@
   // Retrieve and log the count of VM steps required to execute the query.
   void ReportQueryExecutionMetrics() const;
 
+  // Runs some basic sanity checks and frees memory previously associated with
+  // `param_index`, if any. Should be called when a parameter is about to be
+  // bound regardless of its type.
+  void WillBindParameter(int param_index)
+      VALID_CONTEXT_REQUIRED(sequence_checker_);
+
   // The actual sqlite statement. This may be unique to us, or it may be cached
   // by the Database, which is why it's ref-counted. This pointer is
   // guaranteed non-null.
diff --git a/sql/statement_unittest.cc b/sql/statement_unittest.cc
index b7526337..795a924 100644
--- a/sql/statement_unittest.cc
+++ b/sql/statement_unittest.cc
@@ -12,6 +12,9 @@
 
 #include "base/containers/contains.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "sql/database.h"
@@ -293,6 +296,61 @@
   EXPECT_FALSE(select.Step());
 }
 
+TEST_F(StatementTest, BlobStressTest) {
+  // Create a table that holds a whole lot of blobs. This could tickle
+  // pointer-stability related bugs in the container that stores blob data
+  // before it's being written.
+  const int kMany = 200;
+  std::string create_table_sql(
+      "CREATE TABLE blobs(id INTEGER PRIMARY KEY NOT NULL ");
+  for (int i = 0; i < kMany; ++i) {
+    base::StrAppend(&create_table_sql,
+                    {", a", base::NumberToString(i), " BLOB NOT NULL"});
+  }
+  create_table_sql.append(")");
+
+  ASSERT_TRUE(db_.Execute(create_table_sql));
+
+  std::vector<std::string> param_markers(kMany + 1, "?");
+  const std::string insert_sql =
+      base::StrCat({"INSERT INTO blobs VALUES(",
+                    base::JoinString(param_markers, ", "), ")"});
+  sql::StatementID kInsertStatementId = SQL_FROM_HERE;
+  {
+    Statement insert(db_.GetCachedStatement(kInsertStatementId, insert_sql));
+    // ID row.
+    insert.BindInt64(0, 1);
+    for (int i = 0; i < kMany; ++i) {
+      insert.BindBlob(i + 1, std::string(100, 'a' + i % 26));
+    }
+
+    // Make sure overwriting a blob works as expected.
+    insert.BindBlob(50, std::string("overwrite"));
+    ASSERT_TRUE(insert.Run());
+  }
+
+  // Verify the blobs read out as expected.
+  {
+    Statement select(db_.GetUniqueStatement("SELECT * FROM blobs"));
+    ASSERT_TRUE(select.Step());
+    std::string output50, output51;
+    EXPECT_TRUE(select.ColumnBlobAsString(50, &output50));
+    EXPECT_EQ("overwrite", output50);
+    EXPECT_TRUE(select.ColumnBlobAsString(51, &output51));
+    EXPECT_EQ(std::string(100, 'y'), output51);
+  }
+
+  // Make sure the underlying statement is reset i.e. the old bindings don't
+  // persist across different invocations of `GetCachedStatement`.
+  {
+    Statement insert(db_.GetCachedStatement(kInsertStatementId,
+                                            base::cstring_view(insert_sql)));
+    // ID row.
+    insert.BindInt64(0, 2);
+    ASSERT_FALSE(insert.Run());
+  }
+}
+
 TEST_F(StatementTest, BindString) {
   // `id` makes SQLite's rowid mechanism explicit. We rely on it to retrieve
   // the rows in the same order that they were inserted.
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 653d4faf..30e9197 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -15524,6 +15524,28 @@
             ]
         }
     ],
+    "OmniboxAIModeZPS_Desktop": [
+        {
+            "platforms": [
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_2_AIM_Suggestions",
+                    "params": {
+                        "LocalHistoryNonNormalizedContents": "true"
+                    },
+                    "enable_features": [
+                        "OmniboxMiaZPS"
+                    ]
+                }
+            ]
+        }
+    ],
     "OmniboxAnswerActions": [
         {
             "platforms": [
@@ -23591,6 +23613,21 @@
             ]
         }
     ],
+    "SyncTrustedVaultInfobarImprovements": [
+        {
+            "platforms": [
+                "ios"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "SyncTrustedVaultInfobarImprovements"
+                    ]
+                }
+            ]
+        }
+    ],
     "SysPkJPandVKMv3": [
         {
             "platforms": [
@@ -24712,18 +24749,23 @@
             ],
             "experiments": [
                 {
-                    "name": "EnableDecryption",
+                    "name": "Enabled",
                     "enable_features": [
                         "UseFreedesktopSecretKeyProvider"
-                    ],
-                    "disable_features": [
-                        "UseFreedesktopSecretKeyProviderForEncryption"
                     ]
-                },
+                }
+            ]
+        }
+    ],
+    "UseFreedesktopSecretKeyProviderForEncryption": [
+        {
+            "platforms": [
+                "linux"
+            ],
+            "experiments": [
                 {
-                    "name": "EnableDecryptionAndEncryption",
+                    "name": "Enabled",
                     "enable_features": [
-                        "UseFreedesktopSecretKeyProvider",
                         "UseFreedesktopSecretKeyProviderForEncryption"
                     ]
                 }
diff --git a/third_party/angle b/third_party/angle
index 25fc504..b657918 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 25fc50465713d5c524fc49dda81e80624fd676a5
+Subproject commit b65791809eeeff4e6e243e552e21d174080cdc54
diff --git a/third_party/blink/common/chrome_debug_urls.cc b/third_party/blink/common/chrome_debug_urls.cc
index 876ecd5..9298ce0 100644
--- a/third_party/blink/common/chrome_debug_urls.cc
+++ b/third_party/blink/common/chrome_debug_urls.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/public/common/chrome_debug_urls.h"
 
+#include "base/debug/alias.h"
 #include "base/debug/asan_invalid_access.h"
 #include "base/debug/dump_without_crashing.h"
 #include "base/logging.h"
diff --git a/third_party/blink/public/mojom/on_device_translation/translation_manager.mojom b/third_party/blink/public/mojom/on_device_translation/translation_manager.mojom
index 44cb7b66..455ac51 100644
--- a/third_party/blink/public/mojom/on_device_translation/translation_manager.mojom
+++ b/third_party/blink/public/mojom/on_device_translation/translation_manager.mojom
@@ -38,23 +38,16 @@
   // language pair is not supported.
   kNoNotSupportedLanguage = 5,
 
-  // The translator cannot be created because the Accept-Language check failed.
-  kNoAcceptLanguagesCheckFailed = 6,
-
-  // The translator cannot be created. New models need to be downloaded,
-  // but the number of downloaded language packs will exceedd the limit.
-  kNoExceedsLanguagePackCountLimitation = 7,
-
   // The translator cannot be created, because the translator service crashed.
-  kNoServiceCrashed = 8,
+  kNoServiceCrashed = 6,
 
   // The translator cannot be created, because the use of Translator API is
   // disallowed by `TranslatorAPIAllowed` Enterprise policy
-  kNoDisallowedByPolicy = 9,
+  kNoDisallowedByPolicy = 7,
 
   // The translator cannot be created, because the number of services exceeds
   // the limitation.
-  kNoExceedsServiceCountLimitation = 10,
+  kNoExceedsServiceCountLimitation = 8,
 };
 
 // The error of TranslationManager's CreateTranslator IPC.
@@ -74,30 +67,23 @@
   // translator.
   kFailedToCreateTranslator = 4,
 
-  // The translator cannot be created because the Accept-Language check failed.
-  kAcceptLanguagesCheckFailed = 5,
-
-  // The translator cannot be created. New models need to be downloaded,
-  // but the number of downloaded language packs will exceedd the limit.
-  kExceedsLanguagePackCountLimitation = 6,
-
   // The translator cannot be created, because the translator service crashed.
-  kServiceCrashed = 7,
+  kServiceCrashed = 5,
 
   // The translator cannot be created, because the use of Translator API is
   // disallowed by `TranslatorAPIAllowed` Enterprise policy
-  kDisallowedByPolicy = 8,
+  kDisallowedByPolicy = 6,
 
   // The translator cannot be created, because the number of services exceeds
   // the limitation.
-  kExceedsServiceCountLimitation = 9,
+  kExceedsServiceCountLimitation = 7,
 
   // The translator cannot be created, because the number of pending tasks
   // exceeds the limitation.
-  kExceedsPendingTaskCountLimitation = 10,
+  kExceedsPendingTaskCountLimitation =  8,
 
   // The translator cannot be created because the library version is invalid.
-  kInvalidVersion = 11,
+  kInvalidVersion = 9,
 };
 
 // The result of TranslationManager's CreateTranslator IPC.
diff --git a/third_party/blink/renderer/bindings/core/v8/window_proxy_test.cc b/third_party/blink/renderer/bindings/core/v8/window_proxy_test.cc
index 447cbe1..a197f45 100644
--- a/third_party/blink/renderer/bindings/core/v8/window_proxy_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/window_proxy_test.cc
@@ -4,7 +4,6 @@
 
 #include "third_party/blink/renderer/bindings/core/v8/window_proxy.h"
 
-#include "base/debug/stack_trace.h"
 #include "base/memory/raw_ref.h"
 #include "third_party/blink/public/web/web_script_source.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index ac178db..8de1aa3 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -7163,6 +7163,16 @@
   });
 }
 
+// static
+bool Document::IsValidElementLocalNameNewSpec(const StringView& local_name) {
+  if (local_name.empty()) {
+    return false;
+  }
+  return WTF::VisitCharacters(local_name, [](auto chars) {
+    return !ParseElementLocalNameNewSpec(chars);
+  });
+}
+
 enum QualifiedNameStatus {
   kQNValid,
   kQNMultipleColons,
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index e897a6f9..e78ac85 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -1340,6 +1340,7 @@
 
   // https://github.com/whatwg/dom/pull/1079
   static bool IsValidAttributeLocalNameNewSpec(const StringView&);
+  static bool IsValidElementLocalNameNewSpec(const StringView&);
 
   // The following breaks a qualified name into a prefix and a local name.
   // It also does a validity check, and returns false if the qualified name
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index 2a53778e2..bdb3af6d 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -35,6 +35,7 @@
 #include <vector>
 
 #include "base/command_line.h"
+#include "base/debug/alias.h"
 #include "base/debug/crash_logging.h"
 #include "base/debug/dump_without_crashing.h"
 #include "base/memory/scoped_refptr.h"
diff --git a/third_party/blink/renderer/core/html/custom/custom_element.h b/third_party/blink/renderer/core/html/custom/custom_element.h
index 96208d7b..df8f382 100644
--- a/third_party/blink/renderer/core/html/custom/custom_element.h
+++ b/third_party/blink/renderer/core/html/custom/custom_element.h
@@ -8,11 +8,13 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_typedefs.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/create_element_flags.h"
+#include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/platform/text/character.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/text/ascii_ctype.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/code_point_iterator.h"
 #include "third_party/blink/renderer/platform/wtf/text/utf16.h"
 
 namespace blink {
@@ -50,24 +52,47 @@
       return true;
 
     // This quickly rejects all common built-in element names.
+    // name contains a U+002D (-)
     if (name.find('-', 1) == kNotFound)
       return false;
 
+    // name's 0th code point is an ASCII lower alpha
     if (!IsASCIILower(name[0]))
       return false;
 
-    if (name.Is8Bit()) {
-      auto characters = name.Span8();
-      for (size_t i = 1; i < characters.size(); ++i) {
-        if (!Character::IsPotentialCustomElementName8BitChar(characters[i]))
-          return false;
+    if (RuntimeEnabledFeatures::RelaxDOMValidNamesEnabled()) {
+      // https://github.com/whatwg/html/pull/7991
+      // name is a valid element local name
+      if (!Document::IsValidElementLocalNameNewSpec(name)) {
+        return false;
+      }
+      // name does not contain any ASCII upper alphas
+      if (!WTF::VisitCharacters(name.GetString(), [](auto characters) {
+            for (size_t i = 0; i < characters.size(); i++) {
+              if (IsASCIIUpper(characters[i])) {
+                return false;
+              }
+            }
+            return true;
+          })) {
+        return false;
       }
     } else {
-      auto characters = name.Span16();
-      for (size_t i = 1; i < characters.size();) {
-        UChar32 ch = CodePointAtAndNext(characters, i);
-        if (!Character::IsPotentialCustomElementNameChar(ch))
-          return false;
+      if (name.Is8Bit()) {
+        auto characters = name.Span8();
+        for (size_t i = 1; i < characters.size(); ++i) {
+          if (!Character::IsPotentialCustomElementName8BitChar(characters[i])) {
+            return false;
+          }
+        }
+      } else {
+        auto characters = name.Span16();
+        for (size_t i = 1; i < characters.size();) {
+          UChar32 ch = CodePointAtAndNext(characters, i);
+          if (!Character::IsPotentialCustomElementNameChar(ch)) {
+            return false;
+          }
+        }
       }
     }
 
diff --git a/third_party/blink/renderer/core/html/custom/custom_element_test.cc b/third_party/blink/renderer/core/html/custom/custom_element_test.cc
index 14a6aa88..b106547 100644
--- a/third_party/blink/renderer/core/html/custom/custom_element_test.cc
+++ b/third_party/blink/renderer/core/html/custom/custom_element_test.cc
@@ -83,34 +83,34 @@
 
 TEST(CustomElementTest, TestIsValidNamePotentialCustomElementNameChar) {
   test::TaskEnvironment task_environment;
+  // ranges is a list of ranges of valid characters. The comments show the
+  // invalid values which are in between the specified ranges.
   struct {
     UChar32 from, to;
   } ranges[] = {
-      // "-" | "." need to merge to test -1/+1.
-      {'-', '.'},
-      {'0', '9'},
-      {'_', '_'},
-      {'a', 'z'},
-      {0xB7, 0xB7},
-      {0xC0, 0xD6},
-      {0xD8, 0xF6},
-      // [#xF8-#x2FF] | [#x300-#x37D] need to merge to test -1/+1.
-      {0xF8, 0x37D},
-      {0x37F, 0x1FFF},
-      {0x200C, 0x200D},
-      {0x203F, 0x2040},
-      {0x2070, 0x218F},
-      {0x2C00, 0x2FEF},
-      {0x3001, 0xD7FF},
-      {0xF900, 0xFDCF},
-      {0xFDF0, 0xFFFD},
-      {0x10000, 0xEFFFF},
+      // 0x00 null
+      {0x01, 0x08},
+      // 0x09 tab, 0x0A LF
+      {0x0B, 0x0B},
+      // 0x0C FF, 0x0D CR
+      {0x0E, 0x1F},
+      // 0x20 space
+      {0x21, 0x2E},
+      // 0x2F /
+      {0x30, 0x3D},
+      // 0x3E >
+      {0x3F, 0x40},
+      // 0x41 to 0x5A uppercase alphas
+      {0x5B, 0x10FFFF},
   };
   for (auto range : ranges) {
     TestIsPotentialCustomElementNameChar(range.from - 1, false);
-    for (UChar32 c = range.from; c <= range.to; ++c)
+    for (UChar32 c = range.from; c <= range.to; ++c) {
       TestIsPotentialCustomElementNameChar(c, true);
-    TestIsPotentialCustomElementNameChar(range.to + 1, false);
+    }
+    if (range.to < 0x10FFFF) {
+      TestIsPotentialCustomElementNameChar(range.to + 1, false);
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/html_button_element.cc b/third_party/blink/renderer/core/html/forms/html_button_element.cc
index 1420708..d2bdade97 100644
--- a/third_party/blink/renderer/core/html/forms/html_button_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_button_element.cc
@@ -108,15 +108,26 @@
   return HTMLFormControlElement::IsPresentationAttribute(name);
 }
 
+// static
+std::optional<HTMLButtonElement::Type> HTMLButtonElement::TypeFromString(
+    const AtomicString& string) {
+  if (EqualIgnoringASCIICase(string, "reset")) {
+    return kReset;
+  } else if (EqualIgnoringASCIICase(string, "button")) {
+    return kButton;
+  } else if (EqualIgnoringASCIICase(string, "submit")) {
+    return kSubmit;
+  } else {
+    return std::nullopt;
+  }
+}
+
 void HTMLButtonElement::ParseAttribute(
     const AttributeModificationParams& params) {
   if (params.name == html_names::kTypeAttr) {
-    if (EqualIgnoringASCIICase(params.new_value, "reset")) {
-      type_ = kReset;
-    } else if (EqualIgnoringASCIICase(params.new_value, "button")) {
-      type_ = kButton;
-    } else if (EqualIgnoringASCIICase(params.new_value, "submit")) {
-      type_ = kSubmit;
+    if (std::optional<HTMLButtonElement::Type> type =
+            TypeFromString(params.new_value)) {
+      SetTypeInternal(*type);
     } else {
       if (!params.new_value.IsNull()) {
         if (params.new_value.empty()) {
@@ -132,25 +143,32 @@
         UseCounter::Count(
             GetDocument(),
             WebFeature::kButtonTypeAttrInvalidWithCommandOrCommandfor);
-        type_ = kButton;
+        SetTypeInternal(kButton);
       } else {
-        type_ = kSubmit;
+        SetTypeInternal(kSubmit);
       }
     }
-    UpdateWillValidateCache();
     if (formOwner() && isConnected()) {
       formOwner()->InvalidateDefaultButtonStyle();
     }
   } else if (params.name == html_names::kCommandAttr ||
              params.name == html_names::kCommandforAttr) {
     bool has_type = FastHasAttribute(html_names::kTypeAttr);
-    bool type_is_button = EqualIgnoringASCIICase(
-        FastGetAttribute(html_names::kTypeAttr), "button");
+    auto type = TypeFromString(FastGetAttribute(html_names::kTypeAttr));
+    bool type_is_button = type && *type == kButton;
     if ((!has_type || !type_is_button)) {
       UseCounter::Count(
           GetDocument(),
           WebFeature::kButtonTypeAttrInvalidWithCommandOrCommandfor);
     }
+
+    if (RuntimeEnabledFeatures::HTMLCommandAttributesEnabled() &&
+        !params.new_value.IsNull() && !type) {
+      // https://html.spec.whatwg.org/multipage/form-elements.html#dom-button-type
+      // Type, as reflected in the IDL, must be "button" if there are command
+      // attributes without an explicit valid type attribute set.
+      SetTypeInternal(kButton);
+    }
   } else {
     if (params.name == html_names::kFormactionAttr) {
       LogUpdateAttributeIfIsolatedWorldAndInDocument("button", params);
@@ -159,6 +177,14 @@
   }
 }
 
+void HTMLButtonElement::SetTypeInternal(Type type) {
+  type_ = type;
+  UpdateWillValidateCache();
+  if (formOwner() && isConnected()) {
+    formOwner()->InvalidateDefaultButtonStyle();
+  }
+}
+
 Element* HTMLButtonElement::commandForElement() const {
   if (!RuntimeEnabledFeatures::HTMLCommandAttributesEnabled()) {
     return nullptr;
diff --git a/third_party/blink/renderer/core/html/forms/html_button_element.h b/third_party/blink/renderer/core/html/forms/html_button_element.h
index 076a7d2..c053390 100644
--- a/third_party/blink/renderer/core/html/forms/html_button_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_button_element.h
@@ -113,6 +113,10 @@
 
   int DefaultTabIndex() const override;
 
+  static std::optional<Type> TypeFromString(const AtomicString&);
+
+  void SetTypeInternal(Type type);
+
   Type type_ = kSubmit;
   bool is_activated_submit_ = false;
 };
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.cc b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
index 0eacb85..6c3de41 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.cc
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
@@ -925,18 +925,16 @@
 
 void HTMLDocumentParser::ConstructTreeFromToken(AtomicHTMLToken& atomic_token) {
   DCHECK(!GetDocument()->IsPrefetchOnly());
-
-  // Check whether we've exited the header.
-  if (!task_runner_state_->HaveExitedHeader()) {
-    if (GetDocument()->body()) {
-      task_runner_state_->SetExitedHeader();
-    }
-  }
-
   tree_builder_->ConstructTree(&atomic_token);
   CheckIfBlockingStylesheetAdded();
 }
 
+void HTMLDocumentParser::FirstBodyElementAdded() {
+  if (!task_runner_state_->HaveExitedHeader()) {
+    task_runner_state_->SetExitedHeader();
+  }
+}
+
 bool HTMLDocumentParser::HasInsertionPoint() {
   // FIXME: The wasCreatedByScript() branch here might not be fully correct. Our
   // model of the EOF character differs slightly from the one in the spec
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.h b/third_party/blink/renderer/core/html/parser/html_document_parser.h
index e0d3d8d..136460c 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.h
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.h
@@ -136,6 +136,8 @@
   void NotifyParserPauseByUserTiming() override;
   void NotifyParserResumeByUserTiming() override;
 
+  void FirstBodyElementAdded();
+
  protected:
   void insert(const String&) final;
   void Append(const String&) override;
diff --git a/third_party/blink/renderer/core/html/parser/html_tree_builder.cc b/third_party/blink/renderer/core/html/parser/html_tree_builder.cc
index 0b2ccad..678d25e 100644
--- a/third_party/blink/renderer/core/html/parser/html_tree_builder.cc
+++ b/third_party/blink/renderer/core/html/parser/html_tree_builder.cc
@@ -1310,6 +1310,7 @@
           frameset_ok_ = false;
           tree_.InsertHTMLBodyElement(token);
           SetInsertionMode(kInBodyMode);
+          parser_->FirstBodyElementAdded();
           return;
         case HTMLTag::kFrameset:
           tree_.InsertHTMLElement(token);
diff --git a/third_party/blink/renderer/core/paint/link_highlight_impl.cc b/third_party/blink/renderer/core/paint/link_highlight_impl.cc
index 6e62238..ce8cdce 100644
--- a/third_party/blink/renderer/core/paint/link_highlight_impl.cc
+++ b/third_party/blink/renderer/core/paint/link_highlight_impl.cc
@@ -28,7 +28,6 @@
 #include <memory>
 #include <utility>
 
-#include "base/debug/stack_trace.h"
 #include "base/memory/ptr_util.h"
 #include "cc/animation/animation_id_provider.h"
 #include "cc/animation/keyframe_model.h"
diff --git a/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector.cc
index 2b6a446..cd2e149 100644
--- a/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 #include "third_party/blink/renderer/core/paint/timing/image_paint_timing_detector.h"
 
-#include "base/debug/stack_trace.h"
 #include "base/feature_list.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index d7df627..e767e30 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -27,6 +27,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/debug/alias.h"
 #include "base/memory/values_equivalent.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/numerics/clamped_math.h"
diff --git a/third_party/blink/renderer/core/view_transition/page_swap_event.cc b/third_party/blink/renderer/core/view_transition/page_swap_event.cc
index e903551..bf5a754 100644
--- a/third_party/blink/renderer/core/view_transition/page_swap_event.cc
+++ b/third_party/blink/renderer/core/view_transition/page_swap_event.cc
@@ -15,6 +15,7 @@
 #include "third_party/blink/renderer/core/navigation_api/navigation_api.h"
 #include "third_party/blink/renderer/core/view_transition/dom_view_transition.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/wtf/uuid.h"
 
 namespace blink {
 namespace {
diff --git a/third_party/blink/renderer/modules/ai/availability.cc b/third_party/blink/renderer/modules/ai/availability.cc
index 6d63421..46b7632 100644
--- a/third_party/blink/renderer/modules/ai/availability.cc
+++ b/third_party/blink/renderer/modules/ai/availability.cc
@@ -89,9 +89,6 @@
           execution_context, AIMetrics::AISessionType::kTranslator,
           mojom::blink::ModelAvailabilityCheckResult::
               kUnavailableUnsupportedLanguage);
-    case mojom::blink::CanCreateTranslatorResult::kNoAcceptLanguagesCheckFailed:
-    case mojom::blink::CanCreateTranslatorResult::
-        kNoExceedsLanguagePackCountLimitation:
     case mojom::blink::CanCreateTranslatorResult::kNoServiceCrashed:
     case mojom::blink::CanCreateTranslatorResult::kNoDisallowedByPolicy:
     case mojom::blink::CanCreateTranslatorResult::
diff --git a/third_party/blink/renderer/modules/ai/on_device_translation/create_translator_client.cc b/third_party/blink/renderer/modules/ai/on_device_translation/create_translator_client.cc
index 075d172..95a9c08 100644
--- a/third_party/blink/renderer/modules/ai/on_device_translation/create_translator_client.cc
+++ b/third_party/blink/renderer/modules/ai/on_device_translation/create_translator_client.cc
@@ -21,10 +21,6 @@
 
 const char kExceptionMessageUnableToCreateTranslator[] =
     "Unable to create translator for the given source and target language.";
-const char kLinkToDocument[] =
-    "See "
-    "https://developer.chrome.com/docs/ai/translator-api?#supported-languages "
-    "for more details.";
 
 String ConvertCreateTranslatorErrorToDebugString(CreateTranslatorError error) {
   switch (error) {
@@ -36,14 +32,6 @@
       return "Failed to initialize the translation library.";
     case CreateTranslatorError::kFailedToCreateTranslator:
       return "The translation library failed to create a translator.";
-    case CreateTranslatorError::kAcceptLanguagesCheckFailed:
-      return String(base::StrCat(
-          {"The preferred languages check for Translator API failed. ",
-           kLinkToDocument}));
-    case CreateTranslatorError::kExceedsLanguagePackCountLimitation:
-      return String(base::StrCat(
-          {"The Translator API language pack count exceeded the limitation. ",
-           kLinkToDocument}));
     case CreateTranslatorError::kServiceCrashed:
       return "The translation service crashed.";
     case CreateTranslatorError::kDisallowedByPolicy:
@@ -70,13 +58,6 @@
       NOTREACHED();
     case CanCreateTranslatorResult::kNoNotSupportedLanguage:
       return "The language pair is unsupported.";
-    case CanCreateTranslatorResult::kNoAcceptLanguagesCheckFailed:
-      equivalent_error = CreateTranslatorError::kAcceptLanguagesCheckFailed;
-      break;
-    case CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation:
-      equivalent_error =
-          CreateTranslatorError::kExceedsLanguagePackCountLimitation;
-      break;
     case CanCreateTranslatorResult::kNoServiceCrashed:
       equivalent_error = CreateTranslatorError::kServiceCrashed;
       break;
@@ -101,8 +82,6 @@
       return true;
     case CanCreateTranslatorResult::kReadily:
     case CanCreateTranslatorResult::kNoNotSupportedLanguage:
-    case CanCreateTranslatorResult::kNoAcceptLanguagesCheckFailed:
-    case CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation:
     case CanCreateTranslatorResult::kNoServiceCrashed:
     case CanCreateTranslatorResult::kNoDisallowedByPolicy:
     case CanCreateTranslatorResult::kNoExceedsServiceCountLimitation:
@@ -120,8 +99,6 @@
     case CanCreateTranslatorResult::kAfterDownloadTranslatorCreationRequired:
       return false;
     case CanCreateTranslatorResult::kNoNotSupportedLanguage:
-    case CanCreateTranslatorResult::kNoAcceptLanguagesCheckFailed:
-    case CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation:
     case CanCreateTranslatorResult::kNoServiceCrashed:
     case CanCreateTranslatorResult::kNoDisallowedByPolicy:
     case CanCreateTranslatorResult::kNoExceedsServiceCountLimitation:
@@ -240,8 +217,7 @@
   // they lack the ability to do so.
   CHECK(window != nullptr || context->IsServiceWorkerGlobalScope());
 
-  if (RuntimeEnabledFeatures::TranslationAPIV1Enabled() &&
-      !context->IsServiceWorkerGlobalScope() &&
+  if (!context->IsServiceWorkerGlobalScope() &&
       RequiresUserActivation(result) &&
       !LocalFrame::ConsumeTransientUserActivation(window->GetFrame())) {
     GetResolver()->RejectWithDOMException(
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_request.cc b/third_party/blink/renderer/modules/indexeddb/idb_request.cc
index 38231a4b..5a8eed3 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_request.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_request.cc
@@ -33,7 +33,6 @@
 #include <optional>
 #include <utility>
 
-#include "base/debug/stack_trace.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "third_party/blink/public/platform/web_blob_info.h"
diff --git a/third_party/blink/renderer/modules/ml/ml.cc b/third_party/blink/renderer/modules/ml/ml.cc
index dead991..c2f9a55 100644
--- a/third_party/blink/renderer/modules/ml/ml.cc
+++ b/third_party/blink/renderer/modules/ml/ml.cc
@@ -13,6 +13,7 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_power_preference.h"
 #include "third_party/blink/renderer/modules/ml/ml_context.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_error.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
 #include "third_party/blink/renderer/platform/bindings/exception_code.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
@@ -130,10 +131,53 @@
                                            GPUDevice* gpu_device,
                                            ExceptionState& exception_state) {
   webnn::ScopedTrace scoped_trace("ML::createContext(GPUDevice)");
-  exception_state.ThrowDOMException(
-      DOMExceptionCode::kNotSupportedError,
-      "ML.createContext(GPUDevice) is not supported.");
-  return EmptyPromise();
+  if (!script_state->ContextIsValid()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "Invalid script state");
+    return EmptyPromise();
+  }
+
+  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver<MLContext>>(
+      script_state, exception_state.GetContext());
+  auto promise = resolver->Promise();
+
+  // Ensure `resolver` is rejected if the `CreateWebNNContext()` callback isn't
+  // run due to a WebNN service connection error.
+  pending_resolvers_.insert(resolver);
+
+  EnsureWebNNServiceConnection();
+
+  // TODO(crbug.com/409110243): implement WebNNContextImpl creation from
+  // GPUDevice.
+  webnn_context_provider_->CreateWebNNContext(
+      webnn::mojom::blink::CreateContextOptions::New(
+          ConvertBlinkDeviceTypeToMojo(
+              V8MLDeviceType(V8MLDeviceType::Enum::kGpu)),
+          ConvertBlinkPowerPreferenceToMojo(
+              V8MLPowerPreference(V8MLPowerPreference::Enum::kDefault))),
+      WTF::BindOnce(
+          [](ML* ml, ScriptPromiseResolver<MLContext>* resolver,
+             webnn::ScopedTrace scoped_trace, GPUDevice* gpu_device,
+             webnn::mojom::blink::CreateContextResultPtr result) {
+            ml->pending_resolvers_.erase(resolver);
+
+            if (result->is_error()) {
+              const webnn::mojom::blink::Error& create_context_error =
+                  *result->get_error();
+              resolver->RejectWithDOMException(
+                  WebNNErrorCodeToDOMExceptionCode(create_context_error.code),
+                  create_context_error.message);
+              return;
+            }
+
+            resolver->Resolve(MakeGarbageCollected<MLContext>(
+                resolver->GetExecutionContext(), gpu_device,
+                std::move(result->get_success())));
+          },
+          WrapPersistent(this), WrapPersistent(resolver),
+          std::move(scoped_trace), WrapPersistent(gpu_device)));
+
+  return promise;
 }
 
 void ML::EnsureWebNNServiceConnection() {
diff --git a/third_party/blink/renderer/modules/ml/ml_context.cc b/third_party/blink/renderer/modules/ml/ml_context.cc
index b386bec..89d982e 100644
--- a/third_party/blink/renderer/modules/ml/ml_context.cc
+++ b/third_party/blink/renderer/modules/ml/ml_context.cc
@@ -56,6 +56,7 @@
 #include "third_party/blink/renderer/modules/ml/webnn/ml_graph.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_graph_utils.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_tensor.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
 #include "third_party/blink/renderer/platform/bindings/exception_code.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 
@@ -63,6 +64,9 @@
 
 namespace {
 
+const char kContextWebGPUInteropUnsupportedError[] =
+    "The context does not support WebGPU interop.";
+
 MLDataTypeLimits* SupportedDataTypesToDataTypeLimits(
     const webnn::SupportedDataTypes& supported_data_types) {
   MLDataTypeLimits* data_type_limits = MLDataTypeLimits::Create();
@@ -123,6 +127,24 @@
       WTF::BindOnce(&MLContext::OnLost, WrapWeakPersistent(this)));
 }
 
+MLContext::MLContext(
+    ExecutionContext* execution_context,
+    GPUDevice* gpu_device,
+    webnn::mojom::blink::CreateContextSuccessPtr create_context_success)
+    : device_type_(V8MLDeviceType::Enum::kGpu),
+      power_preference_(V8MLPowerPreference::Enum::kDefault),
+      lost_property_(MakeGarbageCollected<LostProperty>(execution_context)),
+      context_remote_(execution_context),
+      properties_(std::move(create_context_success->context_properties)),
+      webnn_handle_(std::move(create_context_success->context_handle)),
+      gpu_device_(gpu_device) {
+  context_remote_.Bind(
+      std::move(create_context_success->context_remote),
+      execution_context->GetTaskRunner(TaskType::kMachineLearning));
+  context_remote_.set_disconnect_with_reason_handler(
+      WTF::BindOnce(&MLContext::OnLost, WrapWeakPersistent(this)));
+}
+
 MLContext::~MLContext() = default;
 
 V8MLDeviceType MLContext::GetDeviceType() const {
@@ -140,6 +162,7 @@
   visitor->Trace(graphs_);
   visitor->Trace(graph_builders_);
   visitor->Trace(tensors_);
+  visitor->Trace(gpu_device_);
   ScriptWrappable::Trace(visitor);
 }
 
@@ -1005,6 +1028,11 @@
     return EmptyPromise();
   }
 
+  if (descriptor->exportableToGPU() && !gpu_device_) {
+    exception_state.ThrowTypeError(kContextWebGPUInteropUnsupportedError);
+    return EmptyPromise();
+  }
+
   ASSIGN_OR_RETURN(
       webnn::OperandDescriptor validated_descriptor,
       webnn::OperandDescriptor::Create(
@@ -1275,7 +1303,6 @@
 
 ScriptPromise<GPUBuffer> MLContext::exportToGPU(
     ScriptState* script_state,
-    GPUDevice* device,
     MLTensor* tensor,
     ExceptionState& exception_state) {
   webnn::ScopedTrace scoped_trace("MLContext::exportToGPU");
@@ -1299,6 +1326,10 @@
         "The source tensor cannot be exported to WebGPU.");
     return EmptyPromise();
   }
+  if (!gpu_device_) {
+    exception_state.ThrowTypeError(kContextWebGPUInteropUnsupportedError);
+    return EmptyPromise();
+  }
   // TODO(crbug.com/345352987): Implement MLTensor's exportToGPU.
   exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
                                     "MLContext::exportToGPU is not supported.");
diff --git a/third_party/blink/renderer/modules/ml/ml_context.h b/third_party/blink/renderer/modules/ml/ml_context.h
index 875d1a7..761a4d3 100644
--- a/third_party/blink/renderer/modules/ml/ml_context.h
+++ b/third_party/blink/renderer/modules/ml/ml_context.h
@@ -54,6 +54,12 @@
       const V8MLPowerPreference power_preference,
       webnn::mojom::blink::CreateContextSuccessPtr create_context_success);
 
+  // Constructs for MLContext(GPUDevice).
+  MLContext(
+      ExecutionContext* execution_context,
+      GPUDevice* gpu_device,
+      webnn::mojom::blink::CreateContextSuccessPtr create_context_success);
+
   MLContext(const MLContext&) = delete;
   MLContext& operator=(const MLContext&) = delete;
 
@@ -104,7 +110,6 @@
                 ExceptionState& exception_state);
 
   ScriptPromise<GPUBuffer> exportToGPU(ScriptState* script_state,
-                                       GPUDevice* device,
                                        MLTensor* tensor,
                                        ExceptionState& exception_state);
 
@@ -147,6 +152,10 @@
   HeapHashSet<WeakMember<MLGraph>> graphs_;
   HeapHashSet<WeakMember<MLGraphBuilder>> graph_builders_;
   HeapHashSet<WeakMember<MLTensor>> tensors_;
+
+  // The `WebNNContext` was initialized from a WebGPU device which can be
+  // used for interop.
+  WeakMember<GPUDevice> gpu_device_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/ml/ml_context.idl b/third_party/blink/renderer/modules/ml/ml_context.idl
index 0649d86..d808130 100644
--- a/third_party/blink/renderer/modules/ml/ml_context.idl
+++ b/third_party/blink/renderer/modules/ml/ml_context.idl
@@ -340,12 +340,9 @@
     CallWith=ScriptState
   ] MLOpSupportLimits opSupportLimits();
 
-  // TODO(crbug.com/345352987): remove device once MLContext(gpuDevice) is
-  // implemented.
   [
     RuntimeEnabled=MachineLearningNeuralNetwork,
     CallWith=ScriptState,
     RaisesException
-  ] Promise<GPUBuffer> exportToGPU(
-    GPUDevice device, MLTensor tensor);
+  ] Promise<GPUBuffer> exportToGPU(MLTensor tensor);
 };
diff --git a/third_party/blink/renderer/modules/webaudio/BUILD.gn b/third_party/blink/renderer/modules/webaudio/BUILD.gn
index a90cece..98742b7 100644
--- a/third_party/blink/renderer/modules/webaudio/BUILD.gn
+++ b/third_party/blink/renderer/modules/webaudio/BUILD.gn
@@ -46,8 +46,6 @@
     "audio_param_handler.h",
     "audio_param_map.cc",
     "audio_param_map.h",
-    "audio_param_timeline.cc",
-    "audio_param_timeline.h",
     "audio_playout_stats.cc",
     "audio_playout_stats.h",
     "audio_processing_event.cc",
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param_handler.cc b/third_party/blink/renderer/modules/webaudio/audio_param_handler.cc
index 90df9dd..6a1e8e4 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_param_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_param_handler.cc
@@ -9,6 +9,12 @@
 
 #include "third_party/blink/renderer/modules/webaudio/audio_param_handler.h"
 
+#include <algorithm>
+#include <limits>
+#include <memory>
+
+#include "base/containers/span.h"
+#include "base/memory/ptr_util.h"
 #include "build/build_config.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
@@ -17,11 +23,17 @@
 #include "third_party/blink/renderer/modules/webaudio/audio_node_output.h"
 #include "third_party/blink/renderer/platform/audio/audio_utilities.h"
 #include "third_party/blink/renderer/platform/audio/vector_math.h"
+#include "third_party/blink/renderer/platform/bindings/exception_messages.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/strcat.h"
+#include "third_party/fdlibm/ieee754.h"
 
 #if defined(ARCH_CPU_X86_FAMILY)
+#include <emmintrin.h>
 #include <xmmintrin.h>
 #elif defined(CPU_ARM_NEON)
 #include <arm_neon.h>
@@ -31,6 +43,13 @@
 
 namespace {
 
+// For a SetTarget event, we want the event to terminate eventually so that we
+// can stop using the timeline to compute the values.  See
+// `HasSetTargetConverged()` for the algorithm.  `kSetTargetThreshold` is
+// exp(-`kTimeConstantsToConverge`).
+constexpr float kTimeConstantsToConverge = 10.0f;
+constexpr float kSetTargetThreshold = 4.539992976248485e-05f;
+
 // Replace NaN values in `values` with `default_value`.
 void HandleNaNValues(float* values,
                      unsigned number_of_values,
@@ -76,6 +95,61 @@
   }
 }
 
+bool IsNonNegativeAudioParamTime(double time,
+                                 ExceptionState& exception_state,
+                                 String message = "Time") {
+  if (time >= 0) {
+    return true;
+  }
+
+  exception_state.ThrowRangeError(WTF::StrCat(
+      {message,
+       " must be a finite non-negative number: ", String::Number(time)}));
+  return false;
+}
+
+bool IsPositiveAudioParamTime(double time,
+                              ExceptionState& exception_state,
+                              String message) {
+  if (time > 0) {
+    return true;
+  }
+
+  exception_state.ThrowRangeError(WTF::StrCat(
+      {message, " must be a finite positive number: ", String::Number(time)}));
+  return false;
+}
+
+// Test that for a SetTarget event, the current value is close enough
+// to the target value that we can consider the event to have
+// converged to the target.
+bool HasSetTargetConverged(float value,
+                           float target,
+                           double current_time,
+                           double start_time,
+                           double time_constant) {
+  // Converged if enough time constants (`kTimeConstantsToConverge`) have passed
+  // since the start of the event.
+  if (current_time > start_time + kTimeConstantsToConverge * time_constant) {
+    return true;
+  }
+
+  // If `target` is 0, converged if |`value`| is less than
+  // `kSetTargetThreshold`.
+  if (target == 0 && fabs(value) < kSetTargetThreshold) {
+    return true;
+  }
+
+  // If `target` is not zero, converged if relative difference between `value`
+  // and `target` is small.  That is |`target`-`value`|/|`value`| <
+  // `kSetTargetThreshold`.
+  if (target != 0 && fabs(target - value) < kSetTargetThreshold * fabs(value)) {
+    return true;
+  }
+
+  return false;
+}
+
 }  // namespace
 
 AudioParamHandler::AudioParamHandler(BaseAudioContext& context,
@@ -333,4 +407,1974 @@
       GetDeferredTaskHandler().RenderQuantumFrames()));
 }
 
+String AudioParamHandler::AudioParamTimeline::EventToString(
+    const ParamEvent& event) const {
+  // The default arguments for most automation methods is the value and the
+  // time.
+  String args = WTF::StrCat(
+      {String::Number(event.Value()), ", ", String::Number(event.Time(), 16)});
+
+  // Get a nice printable name for the event and update the args if necessary.
+  String s;
+  switch (event.GetType()) {
+    case ParamEvent::Type::kSetValue:
+      s = "setValueAtTime";
+      break;
+    case ParamEvent::Type::kLinearRampToValue:
+      s = "linearRampToValueAtTime";
+      break;
+    case ParamEvent::Type::kExponentialRampToValue:
+      s = "exponentialRampToValue";
+      break;
+    case ParamEvent::Type::kSetTarget:
+      s = "setTargetAtTime";
+      // This has an extra time constant arg
+      args =
+          WTF::StrCat({args, ", ", String::Number(event.TimeConstant(), 16)});
+      break;
+    case ParamEvent::Type::kSetValueCurve:
+      s = "setValueCurveAtTime";
+      // Replace the default arg, using "..." to denote the curve argument.
+      args = WTF::StrCat({"..., ", String::Number(event.Time(), 16), ", ",
+                          String::Number(event.Duration(), 16)});
+      break;
+    case ParamEvent::Type::kCancelValues:
+    case ParamEvent::Type::kSetValueCurveEnd:
+    // Fall through; we should never have to print out the internal
+    // `kCancelValues` or `kSetValueCurveEnd` event.
+    case ParamEvent::Type::kLastType:
+      NOTREACHED();
+  };
+
+  return WTF::StrCat({s, "(", args, ")"});
+}
+
+// Computes the value of a linear ramp event at time t with the given event
+// parameters.
+float AudioParamHandler::AudioParamTimeline::LinearRampAtTime(double t,
+                                                              float value1,
+                                                              double time1,
+                                                              float value2,
+                                                              double time2) {
+  return value1 + (value2 - value1) * (t - time1) / (time2 - time1);
+}
+
+// Computes the value of an exponential ramp event at time t with the given
+// event parameters.
+float AudioParamHandler::AudioParamTimeline::ExponentialRampAtTime(
+    double t,
+    float value1,
+    double time1,
+    float value2,
+    double time2) {
+  DCHECK(!std::isnan(value1) && std::isfinite(value1));
+  DCHECK(!std::isnan(value2) && std::isfinite(value2));
+
+  return (value1 == 0.0f || std::signbit(value1) != std::signbit(value2))
+             ? value1
+             : value1 *
+                   fdlibm::pow(value2 / value1, (t - time1) / (time2 - time1));
+}
+
+// Compute the value of a set target event at time t with the given event
+// parameters.
+float AudioParamHandler::AudioParamTimeline::TargetValueAtTime(
+    double t,
+    float value1,
+    double time1,
+    float value2,
+    float time_constant) {
+  return value2 + (value1 - value2) * fdlibm::exp(-(t - time1) / time_constant);
+}
+
+// Compute the value of a set curve event at time t with the given event
+// parameters.
+float AudioParamHandler::AudioParamTimeline::ValueCurveAtTime(
+    double t,
+    double time1,
+    double duration,
+    const float* curve_data,
+    unsigned curve_length) {
+  double curve_index = (curve_length - 1) / duration * (t - time1);
+  unsigned k = std::min(static_cast<unsigned>(curve_index), curve_length - 1);
+  unsigned k1 = std::min(k + 1, curve_length - 1);
+  float c0 = curve_data[k];
+  float c1 = curve_data[k1];
+  float delta = std::min(curve_index - k, 1.0);
+
+  return c0 + (c1 - c0) * delta;
+}
+
+std::unique_ptr<AudioParamHandler::AudioParamTimeline::ParamEvent>
+AudioParamHandler::AudioParamTimeline::ParamEvent::CreateSetValueEvent(
+    float value,
+    double time) {
+  return base::WrapUnique(
+      new ParamEvent(ParamEvent::Type::kSetValue, value, time));
+}
+
+std::unique_ptr<AudioParamHandler::AudioParamTimeline::ParamEvent>
+AudioParamHandler::AudioParamTimeline::ParamEvent::CreateLinearRampEvent(
+    float value,
+    double time,
+    float initial_value,
+    double call_time) {
+  return base::WrapUnique(new ParamEvent(ParamEvent::Type::kLinearRampToValue,
+                                         value, time, initial_value,
+                                         call_time));
+}
+
+std::unique_ptr<AudioParamHandler::AudioParamTimeline::ParamEvent>
+AudioParamHandler::AudioParamTimeline::ParamEvent::CreateExponentialRampEvent(
+    float value,
+    double time,
+    float initial_value,
+    double call_time) {
+  return base::WrapUnique(
+      new ParamEvent(ParamEvent::Type::kExponentialRampToValue, value, time,
+                     initial_value, call_time));
+}
+
+std::unique_ptr<AudioParamHandler::AudioParamTimeline::ParamEvent>
+AudioParamHandler::AudioParamTimeline::ParamEvent::CreateSetTargetEvent(
+    float value,
+    double time,
+    double time_constant) {
+  // The time line code does not expect a timeConstant of 0. (IT
+  // returns NaN or Infinity due to division by zero.  The caller
+  // should have converted this to a SetValueEvent.
+  DCHECK_NE(time_constant, 0);
+  return base::WrapUnique(
+      new ParamEvent(ParamEvent::Type::kSetTarget, value, time, time_constant));
+}
+
+std::unique_ptr<AudioParamHandler::AudioParamTimeline::ParamEvent>
+AudioParamHandler::AudioParamTimeline::ParamEvent::CreateSetValueCurveEvent(
+    const Vector<float>& curve,
+    double time,
+    double duration) {
+  double curve_points = (curve.size() - 1) / duration;
+  float end_value = curve.data()[curve.size() - 1];
+
+  return base::WrapUnique(new ParamEvent(ParamEvent::Type::kSetValueCurve, time,
+                                         duration, curve, curve_points,
+                                         end_value));
+}
+
+std::unique_ptr<AudioParamHandler::AudioParamTimeline::ParamEvent>
+AudioParamHandler::AudioParamTimeline::ParamEvent::CreateSetValueCurveEndEvent(
+    float value,
+    double time) {
+  return base::WrapUnique(
+      new ParamEvent(ParamEvent::Type::kSetValueCurveEnd, value, time));
+}
+
+std::unique_ptr<AudioParamHandler::AudioParamTimeline::ParamEvent>
+AudioParamHandler::AudioParamTimeline::ParamEvent::CreateCancelValuesEvent(
+    double time,
+    std::unique_ptr<ParamEvent> saved_event) {
+  if (saved_event) {
+    // The savedEvent can only have certain event types.  Verify that.
+    ParamEvent::Type saved_type = saved_event->GetType();
+
+    DCHECK_NE(saved_type, ParamEvent::Type::kLastType);
+    DCHECK(saved_type == ParamEvent::Type::kLinearRampToValue ||
+           saved_type == ParamEvent::Type::kExponentialRampToValue ||
+           saved_type == ParamEvent::Type::kSetValueCurve);
+  }
+
+  return base::WrapUnique(new ParamEvent(ParamEvent::Type::kCancelValues, time,
+                                         std::move(saved_event)));
+}
+
+std::unique_ptr<AudioParamHandler::AudioParamTimeline::ParamEvent>
+AudioParamHandler::AudioParamTimeline::ParamEvent::CreateGeneralEvent(
+    Type type,
+    float value,
+    double time,
+    float initial_value,
+    double call_time,
+    double time_constant,
+    double duration,
+    Vector<float>& curve,
+    double curve_points_per_second,
+    float curve_end_value,
+    std::unique_ptr<ParamEvent> saved_event) {
+  return base::WrapUnique(new ParamEvent(
+      type, value, time, initial_value, call_time, time_constant, duration,
+      curve, curve_points_per_second, curve_end_value, std::move(saved_event)));
+}
+
+AudioParamHandler::AudioParamTimeline::ParamEvent*
+AudioParamHandler::AudioParamTimeline::ParamEvent::SavedEvent() const {
+  DCHECK_EQ(GetType(), ParamEvent::Type::kCancelValues);
+  return saved_event_.get();
+}
+
+bool AudioParamHandler::AudioParamTimeline::ParamEvent::
+    HasDefaultCancelledValue() const {
+  DCHECK_EQ(GetType(), ParamEvent::Type::kCancelValues);
+  return has_default_cancelled_value_;
+}
+
+void AudioParamHandler::AudioParamTimeline::ParamEvent::SetCancelledValue(
+    float value) {
+  DCHECK_EQ(GetType(), ParamEvent::Type::kCancelValues);
+  value_ = value;
+  has_default_cancelled_value_ = true;
+}
+
+// General event
+AudioParamHandler::AudioParamTimeline::ParamEvent::ParamEvent(
+    ParamEvent::Type type,
+    float value,
+    double time,
+    float initial_value,
+    double call_time,
+    double time_constant,
+    double duration,
+    Vector<float>& curve,
+    double curve_points_per_second,
+    float curve_end_value,
+    std::unique_ptr<ParamEvent> saved_event)
+    : type_(type),
+      value_(value),
+      time_(time),
+      initial_value_(initial_value),
+      call_time_(call_time),
+      time_constant_(time_constant),
+      duration_(duration),
+      curve_points_per_second_(curve_points_per_second),
+      curve_end_value_(curve_end_value),
+      saved_event_(std::move(saved_event)),
+      has_default_cancelled_value_(false) {
+  curve_ = curve;
+}
+
+// Create simplest event needing just a value and time, like setValueAtTime
+AudioParamHandler::AudioParamTimeline::ParamEvent::ParamEvent(
+    ParamEvent::Type type,
+    float value,
+    double time)
+    : type_(type),
+      value_(value),
+      time_(time),
+      initial_value_(0),
+      call_time_(0),
+      time_constant_(0),
+      duration_(0),
+      curve_points_per_second_(0),
+      curve_end_value_(0),
+      saved_event_(nullptr),
+      has_default_cancelled_value_(false) {
+  DCHECK(type == ParamEvent::Type::kSetValue ||
+         type == ParamEvent::Type::kSetValueCurveEnd);
+}
+
+// Create a linear or exponential ramp that requires an initial value and
+// time in case
+// there is no actual event that precedes this event.
+AudioParamHandler::AudioParamTimeline::ParamEvent::ParamEvent(
+    ParamEvent::Type type,
+    float value,
+    double time,
+    float initial_value,
+    double call_time)
+    : type_(type),
+      value_(value),
+      time_(time),
+      initial_value_(initial_value),
+      call_time_(call_time),
+      time_constant_(0),
+      duration_(0),
+      curve_points_per_second_(0),
+      curve_end_value_(0),
+      saved_event_(nullptr),
+      has_default_cancelled_value_(false) {
+  DCHECK(type == ParamEvent::Type::kLinearRampToValue ||
+         type == ParamEvent::Type::kExponentialRampToValue);
+}
+
+// Create an event needing a time constant (setTargetAtTime)
+AudioParamHandler::AudioParamTimeline::ParamEvent::ParamEvent(
+    ParamEvent::Type type,
+    float value,
+    double time,
+    double time_constant)
+    : type_(type),
+      value_(value),
+      time_(time),
+      initial_value_(0),
+      call_time_(0),
+      time_constant_(time_constant),
+      duration_(0),
+      curve_points_per_second_(0),
+      curve_end_value_(0),
+      saved_event_(nullptr),
+      has_default_cancelled_value_(false) {
+  DCHECK_EQ(type, ParamEvent::Type::kSetTarget);
+}
+
+// Create a setValueCurve event
+AudioParamHandler::AudioParamTimeline::ParamEvent::ParamEvent(
+    ParamEvent::Type type,
+    double time,
+    double duration,
+    const Vector<float>& curve,
+    double curve_points_per_second,
+    float curve_end_value)
+    : type_(type),
+      value_(0),
+      time_(time),
+      initial_value_(0),
+      call_time_(0),
+      time_constant_(0),
+      duration_(duration),
+      curve_points_per_second_(curve_points_per_second),
+      curve_end_value_(curve_end_value),
+      saved_event_(nullptr),
+      has_default_cancelled_value_(false) {
+  DCHECK_EQ(type, ParamEvent::Type::kSetValueCurve);
+  unsigned curve_length = curve.size();
+  curve_.resize(curve_length);
+  memcpy(curve_.data(), curve.data(), curve_length * sizeof(float));
+}
+
+// Create CancelValues event
+AudioParamHandler::AudioParamTimeline::ParamEvent::ParamEvent(
+    ParamEvent::Type type,
+    double time,
+    std::unique_ptr<ParamEvent> saved_event)
+    : type_(type),
+      value_(0),
+      time_(time),
+      initial_value_(0),
+      call_time_(0),
+      time_constant_(0),
+      duration_(0),
+      curve_points_per_second_(0),
+      curve_end_value_(0),
+      saved_event_(std::move(saved_event)),
+      has_default_cancelled_value_(false) {
+  DCHECK_EQ(type, ParamEvent::Type::kCancelValues);
+}
+
+void AudioParamHandler::AudioParamTimeline::SetValueAtTime(
+    float value,
+    double time,
+    ExceptionState& exception_state) {
+  DCHECK(IsMainThread());
+
+  if (!IsNonNegativeAudioParamTime(time, exception_state)) {
+    return;
+  }
+
+  base::AutoLock locker(events_lock_);
+  InsertEvent(ParamEvent::CreateSetValueEvent(value, time), exception_state);
+}
+
+void AudioParamHandler::AudioParamTimeline::LinearRampToValueAtTime(
+    float value,
+    double time,
+    float initial_value,
+    double call_time,
+    ExceptionState& exception_state) {
+  DCHECK(IsMainThread());
+
+  if (!IsNonNegativeAudioParamTime(time, exception_state)) {
+    return;
+  }
+
+  base::AutoLock locker(events_lock_);
+  InsertEvent(
+      ParamEvent::CreateLinearRampEvent(value, time, initial_value, call_time),
+      exception_state);
+}
+
+void AudioParamHandler::AudioParamTimeline::ExponentialRampToValueAtTime(
+    float value,
+    double time,
+    float initial_value,
+    double call_time,
+    ExceptionState& exception_state) {
+  DCHECK(IsMainThread());
+
+  if (!IsNonNegativeAudioParamTime(time, exception_state)) {
+    return;
+  }
+
+  if (!value) {
+    exception_state.ThrowRangeError(WTF::StrCat(
+        {"The float target value provided (", String::Number(value),
+         ") should not be in the range (",
+         String::Number(-std::numeric_limits<float>::denorm_min()), ", ",
+         String::Number(std::numeric_limits<float>::denorm_min()), ")."}));
+    return;
+  }
+
+  base::AutoLock locker(events_lock_);
+  InsertEvent(ParamEvent::CreateExponentialRampEvent(value, time, initial_value,
+                                                     call_time),
+              exception_state);
+}
+
+void AudioParamHandler::AudioParamTimeline::SetTargetAtTime(
+    float target,
+    double time,
+    double time_constant,
+    ExceptionState& exception_state) {
+  DCHECK(IsMainThread());
+
+  if (!IsNonNegativeAudioParamTime(time, exception_state) ||
+      !IsNonNegativeAudioParamTime(time_constant, exception_state,
+                                   "Time constant")) {
+    return;
+  }
+
+  base::AutoLock locker(events_lock_);
+
+  // If timeConstant = 0, we instantly jump to the target value, so
+  // insert a SetValueEvent instead of SetTargetEvent.
+  if (time_constant == 0) {
+    InsertEvent(ParamEvent::CreateSetValueEvent(target, time), exception_state);
+  } else {
+    InsertEvent(ParamEvent::CreateSetTargetEvent(target, time, time_constant),
+                exception_state);
+  }
+}
+
+void AudioParamHandler::AudioParamTimeline::SetValueCurveAtTime(
+    const Vector<float>& curve,
+    double time,
+    double duration,
+    ExceptionState& exception_state) {
+  DCHECK(IsMainThread());
+
+  if (!IsNonNegativeAudioParamTime(time, exception_state) ||
+      !IsPositiveAudioParamTime(duration, exception_state, "Duration")) {
+    return;
+  }
+
+  if (curve.size() < 2) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kInvalidStateError,
+        ExceptionMessages::IndexExceedsMinimumBound("curve length",
+                                                    curve.size(), 2u));
+    return;
+  }
+
+  base::AutoLock locker(events_lock_);
+  InsertEvent(ParamEvent::CreateSetValueCurveEvent(curve, time, duration),
+              exception_state);
+
+  // Insert a setValueAtTime event too to establish an event so that all
+  // following events will process from the end of the curve instead of the
+  // beginning.
+  InsertEvent(ParamEvent::CreateSetValueCurveEndEvent(
+                  curve.data()[curve.size() - 1], time + duration),
+              exception_state);
+}
+
+void AudioParamHandler::AudioParamTimeline::InsertEvent(
+    std::unique_ptr<ParamEvent> event,
+    ExceptionState& exception_state) {
+  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("webaudio.audionode"),
+               "AudioParamHandler::AudioParamTimeline::InsertEvent");
+
+  DCHECK(IsMainThread());
+
+  // Sanity check the event. Be super careful we're not getting infected with
+  // NaN or Inf. These should have been handled by the caller.
+  DCHECK_LT(event->GetType(), ParamEvent::Type::kLastType);
+  DCHECK(std::isfinite(event->Value()));
+  DCHECK(std::isfinite(event->Time()));
+  DCHECK(std::isfinite(event->TimeConstant()));
+  DCHECK(std::isfinite(event->Duration()));
+  DCHECK_GE(event->Duration(), 0);
+
+  double insert_time = event->Time();
+
+  if (!events_.size() &&
+      (event->GetType() == ParamEvent::Type::kLinearRampToValue ||
+       event->GetType() == ParamEvent::Type::kExponentialRampToValue)) {
+    // There are no events preceding these ramps.  Insert a new
+    // setValueAtTime event to set the starting point for these
+    // events.  Use a time of 0 to make sure it precedes all other
+    // events.  This will get fixed when when handle new events.
+    events_.insert(
+        0,
+        AudioParamHandler::AudioParamTimeline::ParamEvent::CreateSetValueEvent(
+            event->InitialValue(), 0));
+    new_events_.insert(events_[0].get());
+  }
+
+  if (events_.empty()) {
+    events_.insert(0, std::move(event));
+    new_events_.insert(events_[0].get());
+    return;
+  }
+
+  // Most of the time, we must insert after the last event. If the time of the
+  // last event is greater than the insert_time, use binary search to find the
+  // insertion point.
+  wtf_size_t insertion_idx = events_.size();
+  DCHECK_GT(insertion_idx, wtf_size_t{0});
+  wtf_size_t ub = insertion_idx - 1;  // upper bound of events that can overlap.
+  if (events_.back()->Time() > insert_time) {
+    auto it = std::upper_bound(
+        events_.begin(), events_.end(), insert_time,
+        [](const double value, const std::unique_ptr<ParamEvent>& entry) {
+          return value < entry->Time();
+        });
+    insertion_idx = static_cast<wtf_size_t>(std::distance(events_.begin(), it));
+    DCHECK_LT(insertion_idx, events_.size());
+    ub = insertion_idx;
+  }
+  DCHECK_LT(ub, static_cast<wtf_size_t>(std::numeric_limits<int>::max()));
+
+  if (event->GetType() == ParamEvent::Type::kSetValueCurve) {
+    double end_time = event->Time() + event->Duration();
+    for (int i = ub; i >= 0; i--) {
+      ParamEvent::Type test_type = events_[i]->GetType();
+      // Events of type `kSetValueCurveEnd` or `kCancelValues` never conflict.
+      if (test_type == ParamEvent::Type::kSetValueCurveEnd ||
+          test_type == ParamEvent::Type::kCancelValues) {
+        continue;
+      }
+      if (test_type == ParamEvent::Type::kSetValueCurve) {
+        // A SetValueCurve overlapping an existing SetValueCurve requires
+        // special care.
+        double test_end_time = events_[i]->Time() + events_[i]->Duration();
+        // Events are overlapped if the new event starts before the old event
+        // ends and the old event starts before the new event ends.
+        bool overlap =
+            event->Time() < test_end_time && events_[i]->Time() < end_time;
+        if (overlap) {
+          // If the start time of the event overlaps the start/end of an
+          // existing event or if the existing event end overlaps the
+          // start/end of the event, it's an error.
+          exception_state.ThrowDOMException(
+              DOMExceptionCode::kNotSupportedError,
+              WTF::StrCat({EventToString(*event), " overlaps ",
+                           EventToString(*events_[i])}));
+          return;
+        }
+      } else {
+        // Here we handle existing events of types other than
+        // `kSetValueCurveEnd`, `kCancelValues` and `kSetValueCurve`.
+        // Throw an error if an existing event starts in the middle of this
+        // SetValueCurve event.
+        if (events_[i]->Time() > event->Time() &&
+            events_[i]->Time() < end_time) {
+          exception_state.ThrowDOMException(
+              DOMExceptionCode::kNotSupportedError,
+              WTF::StrCat({EventToString(*event), " overlaps ",
+                           EventToString(*events_[i])}));
+          return;
+        }
+      }
+      if (events_[i]->Time() < insert_time) {
+        // We found an existing event, E, of type other than `kSetValueCurveEnd`
+        // or `kCancelValues` that starts before the new event of type
+        // `kSetValueCurve` that we want to insert. No earlier existing event
+        // can overlap with the new event. An overlapping `kSetValueCurve` would
+        // have overlapped with E too, so one of them would not be inserted.
+        // Other event types overlap with the new `kSetValueCurve` event only if
+        // they start in the middle of the new event, which is not the case.
+        break;
+      }
+    }
+  } else {
+    // Not a `SetValueCurve` new event. Make sure this new event doesn't overlap
+    // any existing `SetValueCurve` event.
+    for (int i = ub; i >= 0; i--) {
+      ParamEvent::Type test_type = events_[i]->GetType();
+      // Events of type `kSetValueCurveEnd` or `kCancelValues` never conflict.
+      if (test_type == ParamEvent::Type::kSetValueCurveEnd ||
+          test_type == ParamEvent::Type::kCancelValues) {
+        continue;
+      }
+      if (test_type == ParamEvent::Type::kSetValueCurve) {
+        double end_time = events_[i]->Time() + events_[i]->Duration();
+        if (event->GetType() != ParamEvent::Type::kSetValueCurveEnd &&
+            event->Time() >= events_[i]->Time() && event->Time() < end_time) {
+          exception_state.ThrowDOMException(
+              DOMExceptionCode::kNotSupportedError,
+              WTF::StrCat({EventToString(*event), " overlaps ",
+                           EventToString(*events_[i])}));
+          return;
+        }
+      }
+      if (events_[i]->Time() < insert_time) {
+        // We found an existing event, E, of type other than `kSetValueCurveEnd`
+        // or `kCancelValues` that starts before the new event that we want to
+        // insert. No earlier event of type `kSetValueCurve` can overlap with
+        // the new event, because it would have overlapped with E too.
+        break;
+      }
+    }
+  }
+
+  events_.insert(insertion_idx, std::move(event));
+  new_events_.insert(events_[insertion_idx].get());
+}
+
+bool AudioParamHandler::AudioParamTimeline::HasValues(
+    size_t current_frame,
+    double sample_rate,
+    unsigned render_quantum_frames) const {
+  base::AutoTryLock try_locker(events_lock_);
+
+  if (try_locker.is_acquired()) {
+    unsigned n_events = events_.size();
+
+    // Clearly, if there are no scheduled events, we have no timeline values.
+    if (n_events == 0) {
+      return false;
+    }
+
+    // Handle the case where the first event (of certain types) is in the
+    // future.  Then, no sample-accurate processing is needed because the event
+    // hasn't started.
+    if (events_[0]->Time() >
+        (current_frame + render_quantum_frames) / sample_rate) {
+      switch (events_[0]->GetType()) {
+        case ParamEvent::Type::kSetTarget:
+        case ParamEvent::Type::kSetValue:
+        case ParamEvent::Type::kSetValueCurve:
+          // If the first event is one of these types, and the event starts
+          // after the end of the current render quantum, we don't need to do
+          // the slow sample-accurate path.
+          return false;
+        default:
+          // Handle other event types below.
+          break;
+      }
+    }
+
+    // If there are at least 2 events in the timeline, assume there are timeline
+    // values.  This could be optimized to be more careful, but checking is
+    // complicated and keeping this consistent with `ValuesForFrameRangeImpl()`
+    // will be hard, so it's probably best to let the general timeline handle
+    // this until the events are in the past.
+    if (n_events >= 2) {
+      return true;
+    }
+
+    // We have exactly one event in the timeline.
+    switch (events_[0]->GetType()) {
+      case ParamEvent::Type::kSetTarget:
+        // Need automation if the event starts somewhere before the
+        // end of the current render quantum.
+        return events_[0]->Time() <=
+               (current_frame + render_quantum_frames) / sample_rate;
+      case ParamEvent::Type::kSetValue:
+      case ParamEvent::Type::kLinearRampToValue:
+      case ParamEvent::Type::kExponentialRampToValue:
+      case ParamEvent::Type::kCancelValues:
+      case ParamEvent::Type::kSetValueCurveEnd:
+        // If these events are in the past, we don't need any automation; the
+        // value is a constant.
+        return !(events_[0]->Time() < current_frame / sample_rate);
+      case ParamEvent::Type::kSetValueCurve: {
+        double curve_end_time = events_[0]->Time() + events_[0]->Duration();
+        double current_time = current_frame / sample_rate;
+
+        return (events_[0]->Time() <= current_time) &&
+               (current_time < curve_end_time);
+      }
+      case ParamEvent::Type::kLastType:
+        NOTREACHED();
+    }
+  }
+
+  // Can't get the lock so that means the main thread is trying to insert an
+  // event.  Just return true then.  If the main thread releases the lock before
+  // valueForContextTime or valuesForFrameRange runs, then the there will be an
+  // event on the timeline, so everything is fine.  If the lock is held so that
+  // neither valueForContextTime nor valuesForFrameRange can run, this is ok
+  // too, because they have tryLocks to produce a default value.  The event will
+  // then get processed in the next rendering quantum.
+  //
+  // Don't want to return false here because that would confuse the processing
+  // of the timeline if previously we returned true and now suddenly return
+  // false, only to return true on the next rendering quantum.  Currently, once
+  // a timeline has been introduced it is always true forever because m_events
+  // never shrinks.
+  return true;
+}
+
+void AudioParamHandler::AudioParamTimeline::CancelScheduledValues(
+    double cancel_time,
+    ExceptionState& exception_state) {
+  DCHECK(IsMainThread());
+
+  if (!IsNonNegativeAudioParamTime(cancel_time, exception_state)) {
+    return;
+  }
+
+  base::AutoLock locker(events_lock_);
+
+  // Remove all events starting at startTime.
+  for (wtf_size_t i = 0; i < events_.size(); ++i) {
+    // Removal all events whose event time (start) is greater than or
+    // equal to the cancel time.  And also handle the special case
+    // where the cancel time lies in the middle of a setValueCurve
+    // event.
+    //
+    // This critically depends on the fact that no event can be
+    // scheduled in the middle of the curve or at the same start time.
+    // Then removing the setValueCurve doesn't remove any events that
+    // shouldn't have been.
+    double start_time = events_[i]->Time();
+
+    if (start_time >= cancel_time ||
+        ((events_[i]->GetType() == ParamEvent::Type::kSetValueCurve) &&
+         start_time <= cancel_time &&
+         (start_time + events_[i]->Duration() > cancel_time))) {
+      RemoveCancelledEvents(i);
+      break;
+    }
+  }
+}
+
+void AudioParamHandler::AudioParamTimeline::CancelAndHoldAtTime(
+    double cancel_time,
+    ExceptionState& exception_state) {
+  DCHECK(IsMainThread());
+
+  if (!IsNonNegativeAudioParamTime(cancel_time, exception_state)) {
+    return;
+  }
+
+  base::AutoLock locker(events_lock_);
+
+  wtf_size_t i;
+  // Find the first event at or just past `cancel_time`.
+  for (i = 0; i < events_.size(); ++i) {
+    if (events_[i]->Time() > cancel_time) {
+      break;
+    }
+  }
+
+  // The event that is being cancelled.  This is the event just past
+  // `cancel_time`, if any.
+  wtf_size_t cancelled_event_index = i;
+
+  // If the event just before `cancel_time` is a SetTarget or SetValueCurve
+  // event, we need to handle that event specially instead of the event after.
+  if (i > 0 &&
+      ((events_[i - 1]->GetType() == ParamEvent::Type::kSetTarget) ||
+       (events_[i - 1]->GetType() == ParamEvent::Type::kSetValueCurve))) {
+    cancelled_event_index = i - 1;
+  } else if (i >= events_.size()) {
+    // If there were no events occurring after `cancel_time` (and the
+    // previous event is not SetTarget or SetValueCurve, we're done.
+    return;
+  }
+
+  // cancelledEvent is the event that is being cancelled.
+  ParamEvent* cancelled_event = events_[cancelled_event_index].get();
+  ParamEvent::Type event_type = cancelled_event->GetType();
+
+  // New event to be inserted, if any, and a SetValueEvent if needed.
+  std::unique_ptr<ParamEvent> new_event;
+  std::unique_ptr<ParamEvent> new_set_value_event;
+
+  switch (event_type) {
+    case ParamEvent::Type::kLinearRampToValue:
+    case ParamEvent::Type::kExponentialRampToValue: {
+      // For these events we need to remember the parameters of the event
+      // for a CancelValues event so that we can properly cancel the event
+      // and hold the value.
+      std::unique_ptr<ParamEvent> saved_event = ParamEvent::CreateGeneralEvent(
+          event_type, cancelled_event->Value(), cancelled_event->Time(),
+          cancelled_event->InitialValue(), cancelled_event->CallTime(),
+          cancelled_event->TimeConstant(), cancelled_event->Duration(),
+          cancelled_event->Curve(), cancelled_event->CurvePointsPerSecond(),
+          cancelled_event->CurveEndValue(), nullptr);
+
+      new_event = ParamEvent::CreateCancelValuesEvent(cancel_time,
+                                                      std::move(saved_event));
+    } break;
+    case ParamEvent::Type::kSetTarget: {
+      if (cancelled_event->Time() < cancel_time) {
+        // Don't want to remove the SetTarget event if it started before the
+        // cancel time, so bump the index.  But we do want to insert a
+        // cancelEvent so that we stop this automation and hold the value when
+        // we get there.
+        ++cancelled_event_index;
+
+        new_event = ParamEvent::CreateCancelValuesEvent(cancel_time, nullptr);
+      }
+    } break;
+    case ParamEvent::Type::kSetValueCurve: {
+      // If the setValueCurve event started strictly before the cancel time,
+      // there might be something to do....
+      if (cancelled_event->Time() < cancel_time) {
+        if (cancel_time >
+            cancelled_event->Time() + cancelled_event->Duration()) {
+          // If the cancellation time is past the end of the curve there's
+          // nothing to do except remove the following events.
+          ++cancelled_event_index;
+        } else {
+          // Cancellation time is in the middle of the curve.  Therefore,
+          // create a new SetValueCurve event with the appropriate new
+          // parameters to cancel this event properly.  Since it's illegal
+          // to insert any event within a SetValueCurve event, we can
+          // compute the new end value now instead of doing when running
+          // the timeline.
+          double new_duration = cancel_time - cancelled_event->Time();
+          float end_value = ValueCurveAtTime(
+              cancel_time, cancelled_event->Time(), cancelled_event->Duration(),
+              cancelled_event->Curve().data(), cancelled_event->Curve().size());
+
+          // Replace the existing SetValueCurve with this new one that is
+          // identical except for the duration.
+          new_event = ParamEvent::CreateGeneralEvent(
+              event_type, cancelled_event->Value(), cancelled_event->Time(),
+              cancelled_event->InitialValue(), cancelled_event->CallTime(),
+              cancelled_event->TimeConstant(), new_duration,
+              cancelled_event->Curve(), cancelled_event->CurvePointsPerSecond(),
+              end_value, nullptr);
+
+          new_set_value_event = ParamEvent::CreateSetValueEvent(
+              end_value, cancelled_event->Time() + new_duration);
+        }
+      }
+    } break;
+    case ParamEvent::Type::kSetValue:
+    case ParamEvent::Type::kSetValueCurveEnd:
+    case ParamEvent::Type::kCancelValues:
+      // Nothing needs to be done for a SetValue or CancelValues event.
+      break;
+    case ParamEvent::Type::kLastType:
+      NOTREACHED();
+  }
+
+  // Now remove all the following events from the timeline.
+  if (cancelled_event_index < events_.size()) {
+    RemoveCancelledEvents(cancelled_event_index);
+  }
+
+  // Insert the new event, if any.
+  if (new_event) {
+    InsertEvent(std::move(new_event), exception_state);
+    if (new_set_value_event) {
+      InsertEvent(std::move(new_set_value_event), exception_state);
+    }
+  }
+}
+
+std::tuple<bool, float>
+AudioParamHandler::AudioParamTimeline::ValueForContextTime(
+    AudioDestinationHandler& audio_destination,
+    float default_value,
+    float min_value,
+    float max_value,
+    unsigned render_quantum_frames) {
+  {
+    base::AutoTryLock try_locker(events_lock_);
+    if (!try_locker.is_acquired() || !events_.size() ||
+        audio_destination.CurrentTime() < events_[0]->Time()) {
+      return std::make_tuple(false, default_value);
+    }
+  }
+
+  // Ask for just a single value.
+  float value;
+  double sample_rate = audio_destination.SampleRate();
+  size_t start_frame = audio_destination.CurrentSampleFrame();
+  // One parameter change per render quantum.
+  double control_rate = sample_rate / render_quantum_frames;
+  value = ValuesForFrameRange(start_frame, start_frame + 1, default_value,
+                              &value, 1, sample_rate, control_rate, min_value,
+                              max_value, render_quantum_frames);
+
+  return std::make_tuple(true, value);
+}
+
+float AudioParamHandler::AudioParamTimeline::ValuesForFrameRange(
+    size_t start_frame,
+    size_t end_frame,
+    float default_value,
+    float* values,
+    unsigned number_of_values,
+    double sample_rate,
+    double control_rate,
+    float min_value,
+    float max_value,
+    unsigned render_quantum_frames) {
+  // We can't contend the lock in the realtime audio thread.
+  base::AutoTryLock try_locker(events_lock_);
+  if (!try_locker.is_acquired()) {
+    if (values) {
+      for (unsigned i = 0; i < number_of_values; ++i) {
+        values[i] = default_value;
+      }
+    }
+    return default_value;
+  }
+
+  float last_value = ValuesForFrameRangeImpl(
+      start_frame, end_frame, default_value, values, number_of_values,
+      sample_rate, control_rate, render_quantum_frames);
+
+  // Clamp the values now to the nominal range
+  vector_math::Vclip(values, 1, &min_value, &max_value, values, 1,
+                     number_of_values);
+
+  return last_value;
+}
+
+float AudioParamHandler::AudioParamTimeline::ValuesForFrameRangeImpl(
+    size_t start_frame,
+    size_t end_frame,
+    float default_value,
+    float* values,
+    unsigned number_of_values,
+    double sample_rate,
+    double control_rate,
+    unsigned render_quantum_frames) {
+  DCHECK(values);
+  DCHECK_GE(number_of_values, 1u);
+
+  // Return default value if there are no events matching the desired time
+  // range.
+  if (!events_.size() || (end_frame / sample_rate <= events_[0]->Time())) {
+    FillWithDefault(values, default_value, number_of_values, 0);
+
+    return default_value;
+  }
+
+  int number_of_events = events_.size();
+
+  // MUST clamp event before `events_` is possibly mutated because
+  // `new_events_` has raw pointers to objects in `events_`.  Clamping
+  // will clear out all of these pointers before `events_` is
+  // potentially modified.
+  //
+  // TODO(rtoy): Consider making `events_` be scoped_refptr instead of
+  // unique_ptr.
+  if (new_events_.size() > 0) {
+    ClampNewEventsToCurrentTime(start_frame / sample_rate);
+  }
+
+  if (number_of_events > 0) {
+    double current_time = start_frame / sample_rate;
+
+    if (HandleAllEventsInThePast(current_time, sample_rate, default_value,
+                                 number_of_values, values,
+                                 render_quantum_frames)) {
+      return default_value;
+    }
+  }
+
+  // Maintain a running time (frame) and index for writing the values buffer.
+  // If first event is after startFrame then fill initial part of values buffer
+  // with defaultValue until we reach the first event time.
+  auto [current_frame, write_index] =
+      HandleFirstEvent(values, default_value, number_of_values, start_frame,
+                       end_frame, sample_rate, start_frame, 0);
+
+  float value = default_value;
+
+  // Go through each event and render the value buffer where the times overlap,
+  // stopping when we've rendered all the requested values.
+  int last_skipped_event_index = 0;
+  for (int i = 0; i < number_of_events && write_index < number_of_values; ++i) {
+    ParamEvent* event = events_[i].get();
+    ParamEvent* next_event =
+        i < number_of_events - 1 ? events_[i + 1].get() : nullptr;
+
+    // Wait until we get a more recent event.
+    if (!IsEventCurrent(event, next_event, current_frame, sample_rate)) {
+      // This is not the special SetValue event case, and nextEvent is
+      // in the past. We can skip processing of this event since it's
+      // in past. We keep track of this event in lastSkippedEventIndex
+      // to note what events we've skipped.
+      last_skipped_event_index = i;
+      continue;
+    }
+
+    // If there's no next event, set nextEventType to LastType to indicate that.
+    ProcessSetTargetFollowedByRamp(
+        i, event,
+        next_event ? static_cast<ParamEvent::Type>(next_event->GetType())
+                   : ParamEvent::Type::kLastType,
+        current_frame, sample_rate, control_rate, value);
+
+    float value1 = event->Value();
+    double time1 = event->Time();
+
+    // Check to see if an event was cancelled.
+    auto [value2, time2, next_event_type] = HandleCancelValues(
+        event, next_event, next_event ? next_event->Value() : value1,
+        next_event ? next_event->Time() : end_frame / sample_rate + 1);
+
+    DCHECK(!std::isnan(value1));
+    DCHECK(!std::isnan(value2));
+    DCHECK_GE(time2, time1);
+
+    // `fill_to_end_frame` is the exclusive upper bound of the last frame to be
+    // computed for this event.  It's either the last desired frame
+    // (`end_frame`) or derived from the end time of the next event
+    // (`time2`). We compute ceil(`time2`*`sample_rate`) because
+    // `fill_to_end_frame` is the exclusive upper bound.  Consider the case
+    // where `start_frame` = 128 and `time2` = 128.1 (assuming `sample_rate` =
+    // 1).  Since `time2` is greater than 128, we want to output a value for
+    // frame 128.  This requires that `fill_to_end_frame` be at least 129.  This
+    // is achieved by ceil(`time2`).
+    //
+    // However, `time2` can be very large, so compute this carefully in the case
+    // where `time2` exceeds the size of a size_t.
+
+    size_t fill_to_end_frame = end_frame;
+    if (end_frame > time2 * sample_rate) {
+      fill_to_end_frame = static_cast<size_t>(ceil(time2 * sample_rate));
+    }
+
+    DCHECK_GE(fill_to_end_frame, start_frame);
+    unsigned fill_to_frame =
+        static_cast<unsigned>(fill_to_end_frame - start_frame);
+    fill_to_frame = std::min(fill_to_frame, number_of_values);
+
+    const AutomationState current_state = {
+        number_of_values,
+        start_frame,
+        end_frame,
+        sample_rate,
+        control_rate,
+        fill_to_frame,
+        fill_to_end_frame,
+        value1,
+        time1,
+        value2,
+        time2,
+        event,
+        i,
+    };
+
+    // First handle linear and exponential ramps which require looking ahead to
+    // the next event.
+    if (next_event_type == ParamEvent::Type::kLinearRampToValue) {
+      std::tie(current_frame, value, write_index) = ProcessLinearRamp(
+          current_state, values, current_frame, value, write_index);
+    } else if (next_event_type == ParamEvent::Type::kExponentialRampToValue) {
+      std::tie(current_frame, value, write_index) = ProcessExponentialRamp(
+          current_state, values, current_frame, value, write_index);
+    } else {
+      // Handle event types not requiring looking ahead to the next event.
+      switch (event->GetType()) {
+        case ParamEvent::Type::kSetValue:
+        case ParamEvent::Type::kSetValueCurveEnd:
+        case ParamEvent::Type::kLinearRampToValue: {
+          current_frame = fill_to_end_frame;
+
+          // Simply stay at a constant value.
+          value = event->Value();
+          write_index =
+              FillWithDefault(values, value, fill_to_frame, write_index);
+          break;
+        }
+
+        case ParamEvent::Type::kCancelValues: {
+          std::tie(current_frame, value, write_index) = ProcessCancelValues(
+              current_state, values, current_frame, value, write_index);
+          break;
+        }
+
+        case ParamEvent::Type::kExponentialRampToValue: {
+          current_frame = fill_to_end_frame;
+
+          // If we're here, we've reached the end of the ramp.  For
+          // the values after the end of the ramp, we just want to
+          // continue with the ramp end value.
+          value = event->Value();
+          write_index =
+              FillWithDefault(values, value, fill_to_frame, write_index);
+
+          break;
+        }
+
+        case ParamEvent::Type::kSetTarget: {
+          std::tie(current_frame, value, write_index) = ProcessSetTarget(
+              current_state, values, current_frame, value, write_index);
+          break;
+        }
+
+        case ParamEvent::Type::kSetValueCurve: {
+          std::tie(current_frame, value, write_index) = ProcessSetValueCurve(
+              current_state, values, current_frame, value, write_index);
+          break;
+        }
+        case ParamEvent::Type::kLastType:
+          NOTREACHED();
+      }
+    }
+  }
+
+  // If we skipped over any events (because they are in the past), we can
+  // remove them so we don't have to check them ever again.  (This MUST be
+  // running with the m_events lock so we can safely modify the m_events
+  // array.)
+  if (last_skipped_event_index > 0) {
+    // `new_events_` should be empty here so we don't have to
+    // do any updates due to this mutation of `events_`.
+    DCHECK_EQ(new_events_.size(), 0u);
+    RemoveOldEvents(last_skipped_event_index - 1);
+  }
+
+  // If there's any time left after processing the last event then just
+  // propagate the last value to the end of the values buffer.
+  write_index = FillWithDefault(values, value, number_of_values, write_index);
+
+  // This value is used to set the `.value` attribute of the AudioParam.  it
+  // should be the last computed value.
+  return values[number_of_values - 1];
+}
+
+std::tuple<size_t, unsigned>
+AudioParamHandler::AudioParamTimeline::HandleFirstEvent(
+    float* values,
+    float default_value,
+    unsigned number_of_values,
+    size_t start_frame,
+    size_t end_frame,
+    double sample_rate,
+    size_t current_frame,
+    unsigned write_index) {
+  double first_event_time = events_[0]->Time();
+  if (first_event_time > start_frame / sample_rate) {
+    // `fill_to_frame` is an exclusive upper bound, so use ceil() to compute the
+    // bound from the `first_event_time`.
+    size_t fill_to_end_frame = end_frame;
+    double first_event_frame = ceil(first_event_time * sample_rate);
+    if (end_frame > first_event_frame) {
+      fill_to_end_frame = first_event_frame;
+    }
+    DCHECK_GE(fill_to_end_frame, start_frame);
+
+    unsigned fill_to_frame =
+        static_cast<unsigned>(fill_to_end_frame - start_frame);
+    fill_to_frame = std::min(fill_to_frame, number_of_values);
+    write_index =
+        FillWithDefault(values, default_value, fill_to_frame, write_index);
+
+    current_frame += fill_to_frame;
+  }
+
+  return std::make_tuple(current_frame, write_index);
+}
+
+bool AudioParamHandler::AudioParamTimeline::IsEventCurrent(
+    const ParamEvent* event,
+    const ParamEvent* next_event,
+    size_t current_frame,
+    double sample_rate) const {
+  // WARNING: due to round-off it might happen that `next_event->Time()` is just
+  // larger than `current_frame`/`sample_rate`.  This means that we will end up
+  // running the `event` again.  The code below had better be prepared for this
+  // case!  What should happen is the fillToFrame should be 0 so that while the
+  // event is actually run again, nothing actually gets computed, and we move on
+  // to the next event.
+  //
+  // An example of this case is `SetValueCurveAtTime()`.  The time at which
+  // `SetValueCurveAtTime()` ends (and the `SetValueAtTime()` begins) might be
+  // just past `current_time`/`sample_rate`.  Then `SetValueCurveAtTime()` will
+  // be processed again before advancing to `SetValueAtTime()`.  The number of
+  // frames to be processed should be zero in this case.
+  if (next_event && next_event->Time() < current_frame / sample_rate) {
+    // But if the current event is a SetValue event and the event time is
+    // between currentFrame - 1 and currentFrame (in time). we don't want to
+    // skip it.  If we do skip it, the SetValue event is completely skipped
+    // and not applied, which is wrong.  Other events don't have this problem.
+    // (Because currentFrame is unsigned, we do the time check in this funny,
+    // but equivalent way.)
+    double event_frame = event->Time() * sample_rate;
+
+    // Condition is currentFrame - 1 < eventFrame <= currentFrame, but
+    // currentFrame is unsigned and could be 0, so use
+    // currentFrame < eventFrame + 1 instead.
+    if (!(((event->GetType() == ParamEvent::Type::kSetValue ||
+            event->GetType() == ParamEvent::Type::kSetValueCurveEnd) &&
+           (event_frame <= current_frame) &&
+           (current_frame < event_frame + 1)))) {
+      // This is not the special SetValue event case, and nextEvent is
+      // in the past. We can skip processing of this event since it's
+      // in past.
+      return false;
+    }
+  }
+  return true;
+}
+
+void AudioParamHandler::AudioParamTimeline::ClampNewEventsToCurrentTime(
+    double current_time) {
+  bool clamped_some_event_time = false;
+
+  for (auto* event : new_events_) {
+    if (event->Time() < current_time) {
+      event->SetTime(current_time);
+      clamped_some_event_time = true;
+    }
+  }
+
+  if (clamped_some_event_time) {
+    // If we clamped some event time to current time, we need to sort
+    // the event list in time order again, but it must be stable!
+    std::stable_sort(events_.begin(), events_.end(), ParamEvent::EventPrecedes);
+  }
+
+  new_events_.clear();
+}
+
+bool AudioParamHandler::AudioParamTimeline::HandleAllEventsInThePast(
+    double current_time,
+    double sample_rate,
+    float& default_value,
+    unsigned number_of_values,
+    float* values,
+    unsigned render_quantum_frames) {
+  // Optimize the case where the last event is in the past.
+  ParamEvent* last_event = events_[events_.size() - 1].get();
+  ParamEvent::Type last_event_type = last_event->GetType();
+  double last_event_time = last_event->Time();
+
+  // If the last event is in the past and the event has ended, then we can
+  // just propagate the same value.  Except for SetTarget which lasts
+  // "forever".  SetValueCurve also has an explicit SetValue at the end of
+  // the curve, so we don't need to worry that SetValueCurve time is a
+  // start time, not an end time.
+  if (last_event_time + 1.5 * render_quantum_frames / sample_rate <
+      current_time) {
+    // If the last event is SetTarget, make sure we've converged and, that
+    // we're at least 5 time constants past the start of the event.  If not, we
+    // have to continue processing it.
+    if (last_event_type == ParamEvent::Type::kSetTarget) {
+      if (HasSetTargetConverged(default_value, last_event->Value(),
+                                current_time, last_event_time,
+                                last_event->TimeConstant())) {
+        // We've converged. Slam the default value with the target value.
+        default_value = last_event->Value();
+      } else {
+        // Not converged, so give up; we can't remove this event yet.
+        return false;
+      }
+    }
+
+    // `events_` is being mutated.  `new_events_` better be empty because there
+    // are raw pointers there.
+    DCHECK_EQ(new_events_.size(), 0U);
+    // The event has finished, so just copy the default value out.
+    // Since all events are now also in the past, we can just remove all
+    // timeline events too because `default_value` has the expected
+    // value.
+    FillWithDefault(values, default_value, number_of_values, 0);
+    RemoveOldEvents(events_.size());
+
+    return true;
+  }
+
+  return false;
+}
+
+void AudioParamHandler::AudioParamTimeline::ProcessSetTargetFollowedByRamp(
+    int event_index,
+    ParamEvent*& event,
+    ParamEvent::Type next_event_type,
+    size_t current_frame,
+    double sample_rate,
+    double control_rate,
+    float& value) {
+  // If the current event is SetTarget and the next event is a
+  // LinearRampToValue or ExponentialRampToValue, special handling is needed.
+  // In this case, the linear and exponential ramp should start at wherever
+  // the SetTarget processing has reached.
+  if (event->GetType() == ParamEvent::Type::kSetTarget &&
+      (next_event_type == ParamEvent::Type::kLinearRampToValue ||
+       next_event_type == ParamEvent::Type::kExponentialRampToValue)) {
+    // Replace the SetTarget with a SetValue to set the starting time and
+    // value for the ramp using the current frame.  We need to update `value`
+    // appropriately depending on whether the ramp has started or not.
+    //
+    // If SetTarget starts somewhere between currentFrame - 1 and
+    // currentFrame, we directly compute the value it would have at
+    // currentFrame.  If not, we update the value from the value from
+    // currentFrame - 1.
+    //
+    // Can't use the condition currentFrame - 1 <= t0 * sampleRate <=
+    // currentFrame because currentFrame is unsigned and could be 0.  Instead,
+    // compute the condition this way,
+    // where f = currentFrame and Fs = sampleRate:
+    //
+    //    f - 1 <= t0 * Fs <= f
+    //    2 * f - 2 <= 2 * Fs * t0 <= 2 * f
+    //    -2 <= 2 * Fs * t0 - 2 * f <= 0
+    //    -1 <= 2 * Fs * t0 - 2 * f + 1 <= 1
+    //     abs(2 * Fs * t0 - 2 * f + 1) <= 1
+    if (fabs(2 * sample_rate * event->Time() - 2 * current_frame + 1) <= 1) {
+      // SetTarget is starting somewhere between currentFrame - 1 and
+      // currentFrame. Compute the value the SetTarget would have at the
+      // currentFrame.
+      value = event->Value() +
+              (value - event->Value()) *
+                  fdlibm::exp(-(current_frame / sample_rate - event->Time()) /
+                              event->TimeConstant());
+    } else {
+      // SetTarget has already started.  Update `value` one frame because it's
+      // the value from the previous frame.
+      float discrete_time_constant =
+          static_cast<float>(audio_utilities::DiscreteTimeConstantForSampleRate(
+              event->TimeConstant(), control_rate));
+      value += (event->Value() - value) * discrete_time_constant;
+    }
+
+    // Insert a SetValueEvent to mark the starting value and time.
+    // Clear the clamp check because this doesn't need it.
+    events_[event_index] =
+        ParamEvent::CreateSetValueEvent(value, current_frame / sample_rate);
+
+    // Update our pointer to the current event because we just changed it.
+    event = events_[event_index].get();
+  }
+}
+
+std::tuple<float,
+           double,
+           AudioParamHandler::AudioParamTimeline::ParamEvent::Type>
+AudioParamHandler::AudioParamTimeline::HandleCancelValues(
+    const ParamEvent* current_event,
+    ParamEvent* next_event,
+    float value2,
+    double time2) {
+  DCHECK(current_event);
+
+  ParamEvent::Type next_event_type =
+      next_event ? next_event->GetType() : ParamEvent::Type::kLastType;
+
+  if (next_event && next_event->GetType() == ParamEvent::Type::kCancelValues &&
+      next_event->SavedEvent()) {
+    float value1 = current_event->Value();
+    double time1 = current_event->Time();
+
+    switch (current_event->GetType()) {
+      case ParamEvent::Type::kCancelValues:
+      case ParamEvent::Type::kLinearRampToValue:
+      case ParamEvent::Type::kExponentialRampToValue:
+      case ParamEvent::Type::kSetValueCurveEnd:
+      case ParamEvent::Type::kSetValue: {
+        // These three events potentially establish a starting value for
+        // the following event, so we need to examine the cancelled
+        // event to see what to do.
+        const ParamEvent* saved_event = next_event->SavedEvent();
+
+        // Update the end time and type to pretend that we're running
+        // this saved event type.
+        time2 = next_event->Time();
+        next_event_type = saved_event->GetType();
+
+        if (next_event->HasDefaultCancelledValue()) {
+          // We've already established a value for the cancelled
+          // event, so just return it.
+          value2 = next_event->Value();
+        } else {
+          // If the next event would have been a LinearRamp or
+          // ExponentialRamp, we need to compute a new end value for
+          // the event so that the curve works continues as if it were
+          // not cancelled.
+          switch (saved_event->GetType()) {
+            case ParamEvent::Type::kLinearRampToValue:
+              value2 =
+                  LinearRampAtTime(next_event->Time(), value1, time1,
+                                   saved_event->Value(), saved_event->Time());
+              break;
+            case ParamEvent::Type::kExponentialRampToValue:
+              value2 = ExponentialRampAtTime(next_event->Time(), value1, time1,
+                                             saved_event->Value(),
+                                             saved_event->Time());
+              DCHECK(!std::isnan(value1));
+              break;
+            case ParamEvent::Type::kSetValueCurve:
+            case ParamEvent::Type::kSetValueCurveEnd:
+            case ParamEvent::Type::kSetValue:
+            case ParamEvent::Type::kSetTarget:
+            case ParamEvent::Type::kCancelValues:
+              // These cannot be possible types for the saved event
+              // because they can't be created.
+              // createCancelValuesEvent doesn't allow them (SetValue,
+              // SetTarget, CancelValues) or cancelScheduledValues()
+              // doesn't create such an event (SetValueCurve).
+              NOTREACHED();
+            case ParamEvent::Type::kLastType:
+              // Illegal event type.
+              NOTREACHED();
+          }
+
+          // Cache the new value so we don't keep computing it over and over.
+          next_event->SetCancelledValue(value2);
+        }
+      } break;
+      case ParamEvent::Type::kSetValueCurve:
+        // Everything needed for this was handled when cancelling was
+        // done.
+        break;
+      case ParamEvent::Type::kSetTarget:
+        // Nothing special needs to be done for SetTarget
+        // followed by CancelValues.
+        break;
+      case ParamEvent::Type::kLastType:
+        NOTREACHED();
+    }
+  }
+
+  return std::make_tuple(value2, time2, next_event_type);
+}
+
+std::tuple<size_t, float, unsigned>
+AudioParamHandler::AudioParamTimeline::ProcessLinearRamp(
+    const AutomationState& current_state,
+    float* values,
+    size_t current_frame,
+    float value,
+    unsigned write_index) {
+#if defined(ARCH_CPU_X86_FAMILY)
+  auto number_of_values = current_state.number_of_values;
+#endif
+  auto fill_to_frame = current_state.fill_to_frame;
+  auto time1 = current_state.time1;
+  auto time2 = current_state.time2;
+  auto value1 = current_state.value1;
+  auto value2 = current_state.value2;
+  auto sample_rate = current_state.sample_rate;
+
+  double delta_time = time2 - time1;
+  DCHECK_GE(delta_time, 0);
+  // Since delta_time is a double, 1/delta_time can easily overflow a float.
+  // Thus, if delta_time is close enough to zero (less than float min), treat it
+  // as zero.
+  float k =
+      delta_time <= std::numeric_limits<float>::min() ? 0 : 1 / delta_time;
+  const float value_delta = value2 - value1;
+#if defined(ARCH_CPU_X86_FAMILY)
+  if (fill_to_frame > write_index) {
+    // Minimize in-loop operations. Calculate starting value and increment.
+    // Next step: value += inc.
+    //  value = value1 +
+    //      (currentFrame/sampleRate - time1) * k * (value2 - value1);
+    //  inc = 4 / sampleRate * k * (value2 - value1);
+    // Resolve recursion by expanding constants to achieve a 4-step loop
+    // unrolling.
+    //  value = value1 +
+    //    ((currentFrame/sampleRate - time1) + i * sampleFrameTimeIncr) * k
+    //    * (value2 -value1), i in 0..3
+    __m128 v_value =
+        _mm_mul_ps(_mm_set_ps1(1 / sample_rate), _mm_set_ps(3, 2, 1, 0));
+    v_value =
+        _mm_add_ps(v_value, _mm_set_ps1(current_frame / sample_rate - time1));
+    v_value = _mm_mul_ps(v_value, _mm_set_ps1(k * value_delta));
+    v_value = _mm_add_ps(v_value, _mm_set_ps1(value1));
+    __m128 v_inc = _mm_set_ps1(4 / sample_rate * k * value_delta);
+
+    // Truncate loop steps to multiple of 4.
+    unsigned fill_to_frame_trunc =
+        write_index + ((fill_to_frame - write_index) / 4) * 4;
+    // Compute final time.
+    DCHECK_LE(fill_to_frame_trunc, number_of_values);
+    current_frame += fill_to_frame_trunc - write_index;
+
+    // Process 4 loop steps.
+    for (; write_index < fill_to_frame_trunc; write_index += 4) {
+      _mm_storeu_ps(values + write_index, v_value);
+      v_value = _mm_add_ps(v_value, v_inc);
+    }
+  }
+  // Update `value` with the last value computed so that the
+  // `.value` attribute of the AudioParam gets the correct linear
+  // ramp value, in case the following loop doesn't execute.
+  if (write_index >= 1) {
+    value = values[write_index - 1];
+  }
+#endif
+  // Serially process remaining values.
+  for (; write_index < fill_to_frame; ++write_index) {
+    float x = (current_frame / sample_rate - time1) * k;
+    // value = (1 - x) * value1 + x * value2;
+    value = value1 + x * value_delta;
+    values[write_index] = value;
+    ++current_frame;
+  }
+
+  return std::make_tuple(current_frame, value, write_index);
+}
+
+std::tuple<size_t, float, unsigned>
+AudioParamHandler::AudioParamTimeline::ProcessExponentialRamp(
+    const AutomationState& current_state,
+    float* values,
+    size_t current_frame,
+    float value,
+    unsigned write_index) {
+  auto fill_to_frame = current_state.fill_to_frame;
+  auto time1 = current_state.time1;
+  auto time2 = current_state.time2;
+  auto value1 = current_state.value1;
+  auto value2 = current_state.value2;
+  auto sample_rate = current_state.sample_rate;
+
+  if (value1 * value2 <= 0 || time1 >= time2) {
+    // It's an error 1) if `value1` and `value2` have opposite signs or if one
+    // of them is zero, or 2) if `time1` is greater than or equal to `time2`.
+    // Handle this by propagating the previous value.
+    value = value1;
+
+    for (; write_index < fill_to_frame; ++write_index) {
+      values[write_index] = value;
+    }
+  } else {
+    double delta_time = time2 - time1;
+    double num_sample_frames = delta_time * sample_rate;
+    // The value goes exponentially from value1 to value2 in a duration of
+    // deltaTime seconds according to
+    //
+    //  v(t) = v1*(v2/v1)^((t-t1)/(t2-t1))
+    //
+    // Let c be currentFrame and F be the sampleRate.  Then we want to
+    // sample v(t) at times t = (c + k)/F for k = 0, 1, ...:
+    //
+    //   v((c+k)/F) = v1*(v2/v1)^(((c/F+k/F)-t1)/(t2-t1))
+    //              = v1*(v2/v1)^((c/F-t1)/(t2-t1))
+    //                  *(v2/v1)^((k/F)/(t2-t1))
+    //              = v1*(v2/v1)^((c/F-t1)/(t2-t1))
+    //                  *[(v2/v1)^(1/(F*(t2-t1)))]^k
+    //
+    // Thus, this can be written as
+    //
+    //   v((c+k)/F) = V*m^k
+    //
+    // where
+    //   V = v1*(v2/v1)^((c/F-t1)/(t2-t1))
+    //   m = (v2/v1)^(1/(F*(t2-t1)))
+
+    // Compute the per-sample multiplier.
+    double multiplier = fdlibm::pow(value2 / value1, 1.0 / num_sample_frames);
+    // Set the starting value of the exponential ramp.  Do not attempt
+    // to optimize pow to powf.  See crbug.com/771306.
+    value = value1 *
+            fdlibm::pow(value2 / static_cast<double>(value1),
+                        (current_frame / sample_rate - time1) / delta_time);
+    for (double accumulator = value; write_index < fill_to_frame;
+         ++write_index) {
+      value = accumulator;
+      values[write_index] = value;
+      accumulator *= multiplier;
+      ++current_frame;
+    }
+
+    // Due to roundoff it's possible that value exceeds value2.  Clip value
+    // to value2 if we are within 1/2 frame of time2.
+    if (current_frame > time2 * sample_rate - 0.5) {
+      value = value2;
+    }
+  }
+
+  return std::make_tuple(current_frame, value, write_index);
+}
+
+std::tuple<size_t, float, unsigned>
+AudioParamHandler::AudioParamTimeline::ProcessSetTarget(
+    const AutomationState& current_state,
+    float* values,
+    size_t current_frame,
+    float value,
+    unsigned write_index) {
+#if defined(ARCH_CPU_X86_FAMILY)
+  auto number_of_values = current_state.number_of_values;
+#endif
+  auto fill_to_frame = current_state.fill_to_frame;
+  auto time1 = current_state.time1;
+  auto value1 = current_state.value1;
+  auto sample_rate = current_state.sample_rate;
+  auto control_rate = current_state.control_rate;
+  auto fill_to_end_frame = current_state.fill_to_end_frame;
+  auto* event = current_state.event.get();
+
+  // Exponential approach to target value with given time constant.
+  //
+  //   v(t) = v2 + (v1 - v2)*exp(-(t-t1/tau))
+  //
+  float target = value1;
+  float time_constant = event->TimeConstant();
+  float discrete_time_constant =
+      static_cast<float>(audio_utilities::DiscreteTimeConstantForSampleRate(
+          time_constant, control_rate));
+
+  // Set the starting value correctly.  This is only needed when the
+  // current time is "equal" to the start time of this event.  This is
+  // to get the sampling correct if the start time of this automation
+  // isn't on a frame boundary.  Otherwise, we can just continue from
+  // where we left off from the previous rendering quantum.
+  {
+    double ramp_start_frame = time1 * sample_rate;
+    // Condition is c - 1 < r <= c where c = currentFrame and r =
+    // rampStartFrame.  Compute it this way because currentFrame is
+    // unsigned and could be 0.
+    if (ramp_start_frame <= current_frame &&
+        current_frame < ramp_start_frame + 1) {
+      value = target + (value - target) *
+                           fdlibm::exp(-(current_frame / sample_rate - time1) /
+                                       time_constant);
+    } else {
+      // Otherwise, need to compute a new value because `value` is the
+      // last computed value of SetTarget.  Time has progressed by one
+      // frame, so we need to update the value for the new frame.
+      value += (target - value) * discrete_time_constant;
+    }
+  }
+
+  // If the value is close enough to the target, just fill in the data
+  // with the target value.
+  if (HasSetTargetConverged(value, target, current_frame / sample_rate, time1,
+                            time_constant)) {
+    current_frame += fill_to_frame - write_index;
+    for (; write_index < fill_to_frame; ++write_index) {
+      values[write_index] = target;
+    }
+  } else {
+#if defined(ARCH_CPU_X86_FAMILY)
+    if (fill_to_frame > write_index) {
+      // Resolve recursion by expanding constants to achieve a 4-step
+      // loop unrolling.
+      //
+      // v1 = v0 + (t - v0) * c
+      // v2 = v1 + (t - v1) * c
+      // v2 = v0 + (t - v0) * c + (t - (v0 + (t - v0) * c)) * c
+      // v2 = v0 + (t - v0) * c + (t - v0) * c - (t - v0) * c * c
+      // v2 = v0 + (t - v0) * c * (2 - c)
+      // Thus c0 = c, c1 = c*(2-c). The same logic applies to c2 and c3.
+      const float c0 = discrete_time_constant;
+      const float c1 = c0 * (2 - c0);
+      const float c2 = c0 * ((c0 - 3) * c0 + 3);
+      const float c3 = c0 * (c0 * ((4 - c0) * c0 - 6) + 4);
+
+      float delta;
+      __m128 v_c = _mm_set_ps(c2, c1, c0, 0);
+      __m128 v_delta, v_value, v_result;
+
+      // Process 4 loop steps.
+      unsigned fill_to_frame_trunc =
+          write_index + ((fill_to_frame - write_index) / 4) * 4;
+      DCHECK_LE(fill_to_frame_trunc, number_of_values);
+
+      for (; write_index < fill_to_frame_trunc; write_index += 4) {
+        delta = target - value;
+        v_delta = _mm_set_ps1(delta);
+        v_value = _mm_set_ps1(value);
+
+        v_result = _mm_add_ps(v_value, _mm_mul_ps(v_delta, v_c));
+        _mm_storeu_ps(values + write_index, v_result);
+
+        // Update value for next iteration.
+        value += delta * c3;
+      }
+    }
+#endif
+    // Serially process remaining values
+    for (; write_index < fill_to_frame; ++write_index) {
+      values[write_index] = value;
+      value += (target - value) * discrete_time_constant;
+    }
+    // The previous loops may have updated `value` one extra time.
+    // Reset it to the last computed value.
+    if (write_index >= 1) {
+      value = values[write_index - 1];
+    }
+    current_frame = fill_to_end_frame;
+  }
+
+  return std::make_tuple(current_frame, value, write_index);
+}
+
+std::tuple<size_t, float, unsigned>
+AudioParamHandler::AudioParamTimeline::ProcessSetValueCurve(
+    const AutomationState& current_state,
+    float* values,
+    size_t current_frame,
+    float value,
+    unsigned write_index) {
+  auto number_of_values = current_state.number_of_values;
+  auto fill_to_frame = current_state.fill_to_frame;
+  auto time1 = current_state.time1;
+  auto sample_rate = current_state.sample_rate;
+  auto start_frame = current_state.start_frame;
+  auto end_frame = current_state.end_frame;
+  auto fill_to_end_frame = current_state.fill_to_end_frame;
+  auto* event = current_state.event.get();
+
+  const Vector<float> curve = event->Curve();
+  const float* curve_data = curve.data();
+  unsigned number_of_curve_points = curve.size();
+
+  float curve_end_value = event->CurveEndValue();
+
+  // Curve events have duration, so don't just use next event time.
+  double duration = event->Duration();
+  // How much to step the curve index for each frame.  This is basically
+  // the term (N - 1)/Td in the specification.
+  double curve_points_per_frame = event->CurvePointsPerSecond() / sample_rate;
+
+  if (!number_of_curve_points || duration <= 0 || sample_rate <= 0) {
+    // Error condition - simply propagate previous value.
+    current_frame = fill_to_end_frame;
+    for (; write_index < fill_to_frame; ++write_index) {
+      values[write_index] = value;
+    }
+    return std::make_tuple(current_frame, value, write_index);
+  }
+
+  // Save old values and recalculate information based on the curve's
+  // duration instead of the next event time.
+  size_t next_event_fill_to_frame = fill_to_frame;
+
+  // fillToEndFrame = min(endFrame,
+  //                      ceil(sampleRate * (time1 + duration))),
+  // but compute this carefully in case sampleRate*(time1 + duration) is
+  // huge.  fillToEndFrame is an exclusive upper bound of the last frame
+  // to be computed, so ceil is used.
+  {
+    double curve_end_frame = ceil(sample_rate * (time1 + duration));
+    if (end_frame > curve_end_frame) {
+      fill_to_end_frame = static_cast<size_t>(curve_end_frame);
+    } else {
+      fill_to_end_frame = end_frame;
+    }
+  }
+
+  // `fill_to_frame` can be less than `start_frame` when the end of the
+  // setValueCurve automation has been reached, but the next automation
+  // has not yet started. In this case, `fill_to_frame` is clipped to
+  // `time1`+`duration` above, but `start_frame` will keep increasing
+  // (because the current time is increasing).
+  fill_to_frame = (fill_to_end_frame < start_frame)
+                      ? 0
+                      : static_cast<unsigned>(fill_to_end_frame - start_frame);
+  fill_to_frame = std::min(fill_to_frame, number_of_values);
+
+  // Index into the curve data using a floating-point value.
+  // We're scaling the number of curve points by the duration (see
+  // curvePointsPerFrame).
+  double curve_virtual_index = 0;
+  if (time1 < current_frame / sample_rate) {
+    // Index somewhere in the middle of the curve data.
+    // Don't use timeToSampleFrame() since we want the exact
+    // floating-point frame.
+    double frame_offset = current_frame - time1 * sample_rate;
+    curve_virtual_index = curve_points_per_frame * frame_offset;
+  }
+
+  // Set the default value in case fillToFrame is 0.
+  value = curve_end_value;
+
+  // Render the stretched curve data using linear interpolation.
+  // Oversampled curve data can be provided if sharp discontinuities are
+  // desired.
+  unsigned k = 0;
+#if defined(ARCH_CPU_X86_FAMILY)
+  if (fill_to_frame > write_index) {
+    const __m128 v_curve_virtual_index = _mm_set_ps1(curve_virtual_index);
+    const __m128 v_curve_points_per_frame = _mm_set_ps1(curve_points_per_frame);
+    const __m128 v_number_of_curve_points_m1 =
+        _mm_set_ps1(number_of_curve_points - 1);
+    const __m128 v_n1 = _mm_set_ps1(1.0f);
+    const __m128 v_n4 = _mm_set_ps1(4.0f);
+
+    __m128 v_k = _mm_set_ps(3, 2, 1, 0);
+    int a_curve_index0[4];
+    int a_curve_index1[4];
+
+    // Truncate loop steps to multiple of 4
+    unsigned truncated_steps = ((fill_to_frame - write_index) / 4) * 4;
+    unsigned fill_to_frame_trunc = write_index + truncated_steps;
+    DCHECK_LE(fill_to_frame_trunc, number_of_values);
+
+    for (; write_index < fill_to_frame_trunc; write_index += 4) {
+      // Compute current index this way to minimize round-off that would
+      // have occurred by incrementing the index by curvePointsPerFrame.
+      __m128 v_current_virtual_index = _mm_add_ps(
+          v_curve_virtual_index, _mm_mul_ps(v_k, v_curve_points_per_frame));
+      v_k = _mm_add_ps(v_k, v_n4);
+
+      // Clamp index to the last element of the array.
+      __m128i v_curve_index0 = _mm_cvttps_epi32(
+          _mm_min_ps(v_current_virtual_index, v_number_of_curve_points_m1));
+      __m128i v_curve_index1 =
+          _mm_cvttps_epi32(_mm_min_ps(_mm_add_ps(v_current_virtual_index, v_n1),
+                                      v_number_of_curve_points_m1));
+
+      // Linearly interpolate between the two nearest curve points.
+      // `delta` is clamped to 1 because `current_virtual_index` can exceed
+      // `curve_index0` by more than one.  This can happen when we reached
+      // the end of the curve but still need values to fill out the
+      // current rendering quantum.
+      _mm_storeu_si128(reinterpret_cast<__m128i*>(a_curve_index0),
+                       v_curve_index0);
+      _mm_storeu_si128(reinterpret_cast<__m128i*>(a_curve_index1),
+                       v_curve_index1);
+      __m128 v_c0 = _mm_set_ps(
+          curve_data[a_curve_index0[3]], curve_data[a_curve_index0[2]],
+          curve_data[a_curve_index0[1]], curve_data[a_curve_index0[0]]);
+      __m128 v_c1 = _mm_set_ps(
+          curve_data[a_curve_index1[3]], curve_data[a_curve_index1[2]],
+          curve_data[a_curve_index1[1]], curve_data[a_curve_index1[0]]);
+      __m128 v_delta = _mm_min_ps(
+          _mm_sub_ps(v_current_virtual_index, _mm_cvtepi32_ps(v_curve_index0)),
+          v_n1);
+
+      __m128 v_value =
+          _mm_add_ps(v_c0, _mm_mul_ps(_mm_sub_ps(v_c1, v_c0), v_delta));
+
+      _mm_storeu_ps(values + write_index, v_value);
+    }
+    // Pass along k to the serial loop.
+    k = truncated_steps;
+  }
+  if (write_index >= 1) {
+    value = values[write_index - 1];
+  }
+#endif
+  for (; write_index < fill_to_frame; ++write_index, ++k) {
+    // Compute current index this way to minimize round-off that would
+    // have occurred by incrementing the index by curvePointsPerFrame.
+    double current_virtual_index =
+        curve_virtual_index + k * curve_points_per_frame;
+    unsigned curve_index0;
+
+    // Clamp index to the last element of the array.
+    if (current_virtual_index < number_of_curve_points) {
+      curve_index0 = static_cast<unsigned>(current_virtual_index);
+    } else {
+      curve_index0 = number_of_curve_points - 1;
+    }
+
+    unsigned curve_index1 =
+        std::min(curve_index0 + 1, number_of_curve_points - 1);
+
+    // Linearly interpolate between the two nearest curve points.  `delta` is
+    // clamped to 1 because `current_virtual_index` can exceed `curve_index0` by
+    // more than one.  This can happen when we reached the end of the curve but
+    // still need values to fill out the current rendering quantum.
+    DCHECK_LT(curve_index0, number_of_curve_points);
+    DCHECK_LT(curve_index1, number_of_curve_points);
+    float c0 = curve_data[curve_index0];
+    float c1 = curve_data[curve_index1];
+    double delta = std::min(current_virtual_index - curve_index0, 1.0);
+
+    value = c0 + (c1 - c0) * delta;
+
+    values[write_index] = value;
+  }
+
+  // If there's any time left after the duration of this event and the
+  // start of the next, then just propagate the last value of the
+  // `curve_data`. Don't modify `value` unless there is time left.
+  if (write_index < next_event_fill_to_frame) {
+    value = curve_end_value;
+    for (; write_index < next_event_fill_to_frame; ++write_index) {
+      values[write_index] = value;
+    }
+  }
+
+  // Re-adjust current time
+  current_frame += next_event_fill_to_frame;
+
+  return std::make_tuple(current_frame, value, write_index);
+}
+
+std::tuple<size_t, float, unsigned>
+AudioParamHandler::AudioParamTimeline::ProcessCancelValues(
+    const AutomationState& current_state,
+    float* values,
+    size_t current_frame,
+    float value,
+    unsigned write_index) {
+  auto fill_to_frame = current_state.fill_to_frame;
+  auto time1 = current_state.time1;
+  auto sample_rate = current_state.sample_rate;
+  auto control_rate = current_state.control_rate;
+  auto fill_to_end_frame = current_state.fill_to_end_frame;
+  auto* event = current_state.event.get();
+  auto event_index = current_state.event_index;
+
+  // If the previous event was a SetTarget or ExponentialRamp
+  // event, the current value is one sample behind.  Update
+  // the sample value by one sample, but only at the start of
+  // this CancelValues event.
+  if (event->HasDefaultCancelledValue()) {
+    value = event->Value();
+  } else {
+    double cancel_frame = time1 * sample_rate;
+    if (event_index >= 1 && cancel_frame <= current_frame &&
+        current_frame < cancel_frame + 1) {
+      ParamEvent::Type last_event_type = events_[event_index - 1]->GetType();
+      if (last_event_type == ParamEvent::Type::kSetTarget) {
+        float target = events_[event_index - 1]->Value();
+        float time_constant = events_[event_index - 1]->TimeConstant();
+        float discrete_time_constant = static_cast<float>(
+            audio_utilities::DiscreteTimeConstantForSampleRate(time_constant,
+                                                               control_rate));
+        value += (target - value) * discrete_time_constant;
+      }
+    }
+  }
+
+  // Simply stay at the current value.
+  for (; write_index < fill_to_frame; ++write_index) {
+    values[write_index] = value;
+  }
+
+  current_frame = fill_to_end_frame;
+
+  return std::make_tuple(current_frame, value, write_index);
+}
+
+uint32_t AudioParamHandler::AudioParamTimeline::FillWithDefault(
+    float* values,
+    float default_value,
+    uint32_t end_frame,
+    uint32_t write_index) {
+  uint32_t index = write_index;
+
+  for (; index < end_frame; ++index) {
+    values[index] = default_value;
+  }
+
+  return index;
+}
+
+void AudioParamHandler::AudioParamTimeline::RemoveCancelledEvents(
+    wtf_size_t first_event_to_remove) {
+  // For all the events that are being removed, also remove that event
+  // from `new_events_`.
+  if (new_events_.size() > 0) {
+    for (wtf_size_t k = first_event_to_remove; k < events_.size(); ++k) {
+      new_events_.erase(events_[k].get());
+    }
+  }
+
+  // Now we can remove the cancelled events from the list.
+  events_.EraseAt(first_event_to_remove,
+                  events_.size() - first_event_to_remove);
+}
+
+void AudioParamHandler::AudioParamTimeline::RemoveOldEvents(
+    wtf_size_t event_count) {
+  wtf_size_t n_events = events_.size();
+  DCHECK(event_count <= n_events);
+
+  // Always leave at least one event in the event list!
+  if (n_events > 1) {
+    events_.EraseAt(0, std::min(event_count, n_events - 1));
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param_handler.h b/third_party/blink/renderer/modules/webaudio/audio_param_handler.h
index 6c6e0fd..85841bc 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_param_handler.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_param_handler.h
@@ -8,17 +8,23 @@
 #include <sys/types.h>
 
 #include <atomic>
+#include <tuple>
 
+#include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/synchronization/lock.h"
 #include "third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
-#include "third_party/blink/renderer/modules/webaudio/audio_param_timeline.h"
+#include "third_party/blink/renderer/modules/webaudio/audio_destination_node.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_summing_junction.h"
 #include "third_party/blink/renderer/modules/webaudio/base_audio_context.h"
 #include "third_party/blink/renderer/modules/webaudio/inspector_helper_mixin.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
 
@@ -117,6 +123,7 @@
   // AudioSummingJunction
   void DidUpdate() override {}
 
+  // Timeline
   void SetValueAtTime(float value,
                       double time,
                       ExceptionState& exception_state) {
@@ -210,6 +217,450 @@
   base::Lock& RateLock() const { return rate_lock_; }
 
  private:
+  class AudioParamTimeline {
+    DISALLOW_NEW();
+
+   public:
+    AudioParamTimeline() = default;
+
+    void SetValueAtTime(float value, double time, ExceptionState&);
+    void LinearRampToValueAtTime(float value,
+                                 double time,
+                                 float initial_value,
+                                 double call_time,
+                                 ExceptionState&);
+    void ExponentialRampToValueAtTime(float value,
+                                      double time,
+                                      float initial_value,
+                                      double call_time,
+                                      ExceptionState&);
+    void SetTargetAtTime(float target,
+                         double time,
+                         double time_constant,
+                         ExceptionState&);
+    void SetValueCurveAtTime(const Vector<float>& curve,
+                             double time,
+                             double duration,
+                             ExceptionState&);
+    void CancelScheduledValues(double start_time, ExceptionState&);
+    void CancelAndHoldAtTime(double cancel_time, ExceptionState&);
+
+    // Compute the value from this AudioParamHandler at the current context
+    // frame. Returns two values:
+    //
+    //   bool has_value - to indicate if the value could be computed from the
+    //                    timeline
+    //   float value    - the timeline value if `has_value` is true; otherwise
+    //                    `default_value` is returned.
+    std::tuple<bool, float> ValueForContextTime(AudioDestinationHandler&,
+                                                float default_value,
+                                                float min_value,
+                                                float max_value,
+                                                unsigned render_quantum_frames);
+
+    // Given the time range in frames, calculates parameter values into the
+    // values buffer and returns the last parameter value calculated for
+    // "values" or the defaultValue if none were calculated.  controlRate is the
+    // rate (number per second) at which parameter values will be calculated. It
+    // should equal sampleRate for sample-accurate parameter changes, and
+    // otherwise will usually match the render quantum size such that the
+    // parameter value changes once per render quantum.
+    float ValuesForFrameRange(size_t start_frame,
+                              size_t end_frame,
+                              float default_value,
+                              float* values,
+                              unsigned number_of_values,
+                              double sample_rate,
+                              double control_rate,
+                              float min_value,
+                              float max_value,
+                              unsigned render_quantum_frames);
+
+    // Returns true if the AudioParam timeline needs to run in this
+    // rendering quantum.  This means some automation is already running
+    // or is scheduled to run in the current rendering quantuym.
+    bool HasValues(size_t current_frame,
+                   double sample_rate,
+                   unsigned render_quantum_frames) const;
+
+   private:
+    class ParamEvent {
+     public:
+      enum class Type {
+        kSetValue,
+        kLinearRampToValue,
+        kExponentialRampToValue,
+        kSetTarget,
+        kSetValueCurve,
+        // For cancelValuesAndHold
+        kCancelValues,
+        // Special marker for the end of a `kSetValueCurve` event.
+        kSetValueCurveEnd,
+        kLastType
+      };
+
+      static std::unique_ptr<ParamEvent> CreateLinearRampEvent(
+          float value,
+          double time,
+          float initial_value,
+          double call_time);
+      static std::unique_ptr<ParamEvent> CreateExponentialRampEvent(
+          float value,
+          double time,
+          float initial_value,
+          double call_time);
+      static std::unique_ptr<ParamEvent> CreateSetValueEvent(float value,
+                                                             double time);
+      static std::unique_ptr<ParamEvent>
+      CreateSetTargetEvent(float value, double time, double time_constant);
+      static std::unique_ptr<ParamEvent> CreateSetValueCurveEvent(
+          const Vector<float>& curve,
+          double time,
+          double duration);
+      static std::unique_ptr<ParamEvent> CreateSetValueCurveEndEvent(
+          float value,
+          double time);
+      static std::unique_ptr<ParamEvent> CreateCancelValuesEvent(
+          double time,
+          std::unique_ptr<ParamEvent> saved_event);
+      // Needed for creating a saved event where we want to supply all
+      // the possible parameters because we're mostly copying an
+      // existing event.
+      static std::unique_ptr<ParamEvent> CreateGeneralEvent(
+          Type,
+          float value,
+          double time,
+          float initial_value,
+          double call_time,
+          double time_constant,
+          double duration,
+          Vector<float>& curve,
+          double curve_points_per_second,
+          float curve_end_value,
+          std::unique_ptr<ParamEvent> saved_event);
+
+      static bool EventPrecedes(const std::unique_ptr<ParamEvent>& a,
+                                const std::unique_ptr<ParamEvent>& b) {
+        return a->Time() < b->Time();
+      }
+
+      Type GetType() const { return type_; }
+      float Value() const { return value_; }
+      double Time() const { return time_; }
+      void SetTime(double new_time) { time_ = new_time; }
+      double TimeConstant() const { return time_constant_; }
+      double Duration() const { return duration_; }
+      const Vector<float>& Curve() const { return curve_; }
+      Vector<float>& Curve() { return curve_; }
+      float InitialValue() const { return initial_value_; }
+      double CallTime() const { return call_time_; }
+
+      double CurvePointsPerSecond() const { return curve_points_per_second_; }
+      float CurveEndValue() const { return curve_end_value_; }
+
+      // For CancelValues events. Not valid for any other event.
+      ParamEvent* SavedEvent() const;
+      bool HasDefaultCancelledValue() const;
+      void SetCancelledValue(float);
+
+     private:
+      // General event
+      ParamEvent(Type type,
+                 float value,
+                 double time,
+                 float initial_value,
+                 double call_time,
+                 double time_constant,
+                 double duration,
+                 Vector<float>& curve,
+                 double curve_points_per_second,
+                 float curve_end_value,
+                 std::unique_ptr<ParamEvent> saved_event);
+
+      // Create simplest event needing just a value and time, like
+      // setValueAtTime.
+      ParamEvent(Type, float value, double time);
+
+      // Create a linear or exponential ramp that requires an initial
+      // value and time in case there is no actual event that precedes
+      // this event.
+      ParamEvent(Type,
+                 float value,
+                 double time,
+                 float initial_value,
+                 double call_time);
+
+      // Create an event needing a time constant (setTargetAtTime)
+      ParamEvent(Type, float value, double time, double time_constant);
+
+      // Create a setValueCurve event
+      ParamEvent(Type,
+                 double time,
+                 double duration,
+                 const Vector<float>& curve,
+                 double curve_points_per_second,
+                 float curve_end_value);
+
+      // Create CancelValues event
+      ParamEvent(Type, double time, std::unique_ptr<ParamEvent> saved_event);
+
+      Type type_;
+
+      // The value for the event.  The interpretation of this depends on
+      // the event type. Not used for SetValueCurve. For CancelValues,
+      // it is the end value to use when cancelling a LinearRampToValue
+      // or ExponentialRampToValue event.
+      float value_;
+
+      // The time for the event. The interpretation of this depends on
+      // the event type.
+      double time_;
+
+      // Initial value and time to use for linear and exponential ramps that
+      // don't have a preceding event.
+      float initial_value_;
+      double call_time_;
+
+      // Only used for SetTarget events
+      double time_constant_;
+
+      // The following items are only used for SetValueCurve events.
+      //
+      // The duration of the curve.
+      double duration_;
+      // The array of curve points.
+      Vector<float> curve_;
+      // The number of curve points per second. it is used to compute
+      // the curve index step when running the automation.
+      double curve_points_per_second_;
+      // The default value to use at the end of the curve.  Normally
+      // it's the last entry in m_curve, but cancelling a SetValueCurve
+      // will set this to a new value.
+      float curve_end_value_;
+
+      // For CancelValues. If CancelValues is in the middle of an event, this
+      // holds the event that is being cancelled, so that processing can
+      // continue as if the event still existed up until we reach the actual
+      // scheduled cancel time.
+      std::unique_ptr<ParamEvent> saved_event_;
+
+      // True if a default value has been assigned to the CancelValues event.
+      bool has_default_cancelled_value_;
+    };
+
+    // State of the timeline for the current event.
+    struct AutomationState {
+      // Parameters for the current automation request.  Number of
+      // values to be computed for the automation request
+      const unsigned number_of_values;
+      // Start and end frames for this automation request
+      const size_t start_frame;
+      const size_t end_frame;
+
+      // Sample rate and control rate for this request
+      const double sample_rate;
+      const double control_rate;
+
+      // Parameters needed for processing the current event.
+      const unsigned fill_to_frame;
+      const size_t fill_to_end_frame;
+
+      // Value and time for the current event
+      const float value1;
+      const double time1;
+
+      // Value and time for the next event, if any.
+      const float value2;
+      const double time2;
+
+      // The current event, and its index in the event vector.
+      raw_ptr<const ParamEvent> event;
+      const int event_index;
+    };
+
+    void InsertEvent(std::unique_ptr<ParamEvent>, ExceptionState&)
+        EXCLUSIVE_LOCKS_REQUIRED(events_lock_);
+    float ValuesForFrameRangeImpl(size_t start_frame,
+                                  size_t end_frame,
+                                  float default_value,
+                                  float* values,
+                                  unsigned number_of_values,
+                                  double sample_rate,
+                                  double control_rate,
+                                  unsigned render_quantum_frames)
+        EXCLUSIVE_LOCKS_REQUIRED(events_lock_);
+
+    // Produce a nice string describing the event in human-readable form.
+    String EventToString(const ParamEvent&) const;
+
+    // Automation functions that compute the value of the specified
+    // automation at the specified time.
+    float LinearRampAtTime(double t,
+                           float value1,
+                           double time1,
+                           float value2,
+                           double time2);
+    float ExponentialRampAtTime(double t,
+                                float value1,
+                                double time1,
+                                float value2,
+                                double time2);
+    float TargetValueAtTime(double t,
+                            float value1,
+                            double time1,
+                            float value2,
+                            float time_constant);
+    float ValueCurveAtTime(double t,
+                           double time1,
+                           double duration,
+                           const float* curve_data,
+                           unsigned curve_length);
+
+    // Handles the special case where the first event in the timeline
+    // starts after `start_frame`.  These initial values are filled using
+    // `default_value`.  The updated `current_frame` and `write_index` is
+    // returned.
+    std::tuple<size_t, unsigned> HandleFirstEvent(float* values,
+                                                  float default_value,
+                                                  unsigned number_of_values,
+                                                  size_t start_frame,
+                                                  size_t end_frame,
+                                                  double sample_rate,
+                                                  size_t current_frame,
+                                                  unsigned write_index)
+        EXCLUSIVE_LOCKS_REQUIRED(events_lock_);
+
+    // Return true if `current_event` starts after `current_frame`, but
+    // also takes into account the `next_event` if any.
+    bool IsEventCurrent(const ParamEvent* current_event,
+                        const ParamEvent* next_event,
+                        size_t current_frame,
+                        double sample_rate) const;
+
+    // Clamp times to current time, if needed for any new events.  Note,
+    // this method can mutate `events_`, so do call this only in safe
+    // places.
+    void ClampNewEventsToCurrentTime(double current_time)
+        EXCLUSIVE_LOCKS_REQUIRED(events_lock_);
+
+    // Handle the case where the last event in the timeline is in the
+    // past.  Returns false if any event is not in the past. Otherwise,
+    // return true and also fill in `values` with `default_value`.
+    // `default_value` may be updated with a new value.
+    bool HandleAllEventsInThePast(double current_time,
+                                  double sample_rate,
+                                  float& default_value,
+                                  unsigned number_of_values,
+                                  float* values,
+                                  unsigned render_quantum_frames)
+        EXCLUSIVE_LOCKS_REQUIRED(events_lock_);
+
+    // Handle processing of CancelValue event. If cancellation happens, value2,
+    // time2, and nextEventType will be updated with the new value due to
+    // cancellation.  Note that `next_event` or its member can be null.
+    std::tuple<float, double, ParamEvent::Type> HandleCancelValues(
+        const ParamEvent* current_event,
+        ParamEvent* next_event,
+        float value2,
+        double time2) EXCLUSIVE_LOCKS_REQUIRED(events_lock_);
+
+    // Process a SetTarget event and the next event is a
+    // LinearRampToValue or ExponentialRampToValue event.  This requires
+    // special handling because the ramp should start at whatever value
+    // the SetTarget event has reached at this time, instead of using
+    // the value of the SetTarget event.
+    void ProcessSetTargetFollowedByRamp(int event_index,
+                                        ParamEvent*& current_event,
+                                        ParamEvent::Type next_event_type,
+                                        size_t current_frame,
+                                        double sample_rate,
+                                        double control_rate,
+                                        float& value)
+        EXCLUSIVE_LOCKS_REQUIRED(events_lock_);
+
+    // Handle processing of LinearRampEvent, writing the appropriate
+    // values to `values`.  Returns the updated `current_frame`, last
+    // computed `value`, and the updated `write_index`.
+    std::tuple<size_t, float, unsigned> ProcessLinearRamp(
+        const AutomationState& current_state,
+        float* values,
+        size_t current_frame,
+        float value,
+        unsigned write_index);
+
+    // Handle processing of ExponentialRampEvent, writing the appropriate
+    // values to `values`.  Returns the updated `current_frame`, last
+    // computed `value`, and the updated `write_index`.
+    std::tuple<size_t, float, unsigned> ProcessExponentialRamp(
+        const AutomationState& current_state,
+        float* values,
+        size_t current_frame,
+        float value,
+        unsigned write_index);
+
+    // Handle processing of SetTargetEvent, writing the appropriate
+    // values to `values`.  Returns the updated `current_frame`, last
+    // computed `value`, and the updated `write_index`.
+    std::tuple<size_t, float, unsigned> ProcessSetTarget(
+        const AutomationState& current_state,
+        float* values,
+        size_t current_frame,
+        float value,
+        unsigned write_index);
+
+    // Handle processing of SetValueCurveEvent, writing the appropriate
+    // values to `values`.  Returns the updated `current_frame`, last
+    // computed `value`, and the updated `write_index`.
+    std::tuple<size_t, float, unsigned> ProcessSetValueCurve(
+        const AutomationState& current_state,
+        float* values,
+        size_t current_frame,
+        float value,
+        unsigned write_index);
+
+    // Handle processing of CancelValuesEvent, writing the appropriate
+    // values to `values`.  Returns the updated `current_frame`, last
+    // computed `value`, and the updated `write_index`.
+    std::tuple<size_t, float, unsigned> ProcessCancelValues(
+        const AutomationState& current_state,
+        float* values,
+        size_t current_frame,
+        float value,
+        unsigned write_index) EXCLUSIVE_LOCKS_REQUIRED(events_lock_);
+
+    // Fill the output vector `values` with the value `default_value`,
+    // starting at `write_index` and continuing up to `end_frame`
+    // (exclusive).  `write_index` is updated with the new index.
+    uint32_t FillWithDefault(float* values,
+                             float default_value,
+                             uint32_t end_frame,
+                             uint32_t write_index);
+
+    // When cancelling events, remove the items from `events_` starting
+    // at the given index.  Update `new_events_` too.
+    void RemoveCancelledEvents(wtf_size_t first_event_to_remove)
+        EXCLUSIVE_LOCKS_REQUIRED(events_lock_);
+
+    // Remove old events, but always leave at least one event in the timeline.
+    // This is needed in case a new event is added (like linearRamp) that would
+    // use a previous event to compute the automation.
+    void RemoveOldEvents(wtf_size_t n_events)
+        EXCLUSIVE_LOCKS_REQUIRED(events_lock_);
+
+    // Vector of all automation events for the AudioParam.
+    Vector<std::unique_ptr<ParamEvent>> events_ GUARDED_BY(events_lock_);
+
+    // Vector of raw pointers to the actual ParamEvent that was
+    // inserted.  As new events are added, `new_events_` is updated with
+    // the new event.  When the timline is processed, these events are
+    // clamped to current time by `ClampNewEventsToCurrentTime`. Access
+    // must be locked via `events_lock_`.  Must be maintained together
+    // with `events_`.
+    HashSet<ParamEvent*> new_events_ GUARDED_BY(events_lock_);
+
+    mutable base::Lock events_lock_;
+  };
+
   AudioParamHandler(BaseAudioContext&,
                     AudioParamType,
                     double default_value,
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param_timeline.cc b/third_party/blink/renderer/modules/webaudio/audio_param_timeline.cc
deleted file mode 100644
index 6440113f..0000000
--- a/third_party/blink/renderer/modules/webaudio/audio_param_timeline.cc
+++ /dev/null
@@ -1,2052 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1.  Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- * 2.  Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in the
- *     documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
-#include "third_party/blink/renderer/modules/webaudio/audio_param_timeline.h"
-
-#include <algorithm>
-#include <limits>
-#include <memory>
-
-#include "base/memory/ptr_util.h"
-#include "build/build_config.h"
-#include "third_party/blink/renderer/core/inspector/console_message.h"
-#include "third_party/blink/renderer/platform/audio/audio_utilities.h"
-#include "third_party/blink/renderer/platform/audio/vector_math.h"
-#include "third_party/blink/renderer/platform/bindings/exception_messages.h"
-#include "third_party/blink/renderer/platform/bindings/exception_state.h"
-#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
-#include "third_party/blink/renderer/platform/wtf/math_extras.h"
-#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
-#include "third_party/blink/renderer/platform/wtf/text/strcat.h"
-#include "third_party/fdlibm/ieee754.h"
-
-#if defined(ARCH_CPU_X86_FAMILY)
-#include <emmintrin.h>
-#endif
-
-namespace blink {
-
-namespace {
-
-// For a SetTarget event, we want the event to terminate eventually so that we
-// can stop using the timeline to compute the values.  See
-// `HasSetTargetConverged()` for the algorithm.  `kSetTargetThreshold` is
-// exp(-`kTimeConstantsToConverge`).
-constexpr float kTimeConstantsToConverge = 10.0f;
-constexpr float kSetTargetThreshold = 4.539992976248485e-05f;
-
-bool IsNonNegativeAudioParamTime(double time,
-                                 ExceptionState& exception_state,
-                                 String message = "Time") {
-  if (time >= 0) {
-    return true;
-  }
-
-  exception_state.ThrowRangeError(WTF::StrCat(
-      {message,
-       " must be a finite non-negative number: ", String::Number(time)}));
-  return false;
-}
-
-bool IsPositiveAudioParamTime(double time,
-                              ExceptionState& exception_state,
-                              String message) {
-  if (time > 0) {
-    return true;
-  }
-
-  exception_state.ThrowRangeError(WTF::StrCat(
-      {message, " must be a finite positive number: ", String::Number(time)}));
-  return false;
-}
-
-// Test that for a SetTarget event, the current value is close enough
-// to the target value that we can consider the event to have
-// converged to the target.
-bool HasSetTargetConverged(float value,
-                           float target,
-                           double current_time,
-                           double start_time,
-                           double time_constant) {
-  // Converged if enough time constants (`kTimeConstantsToConverge`) have passed
-  // since the start of the event.
-  if (current_time > start_time + kTimeConstantsToConverge * time_constant) {
-    return true;
-  }
-
-  // If `target` is 0, converged if |`value`| is less than
-  // `kSetTargetThreshold`.
-  if (target == 0 && fabs(value) < kSetTargetThreshold) {
-    return true;
-  }
-
-  // If `target` is not zero, converged if relative difference between `value`
-  // and `target` is small.  That is |`target`-`value`|/|`value`| <
-  // `kSetTargetThreshold`.
-  if (target != 0 && fabs(target - value) < kSetTargetThreshold * fabs(value)) {
-    return true;
-  }
-
-  return false;
-}
-
-}  // namespace
-
-String AudioParamTimeline::EventToString(const ParamEvent& event) const {
-  // The default arguments for most automation methods is the value and the
-  // time.
-  String args = WTF::StrCat(
-      {String::Number(event.Value()), ", ", String::Number(event.Time(), 16)});
-
-  // Get a nice printable name for the event and update the args if necessary.
-  String s;
-  switch (event.GetType()) {
-    case ParamEvent::Type::kSetValue:
-      s = "setValueAtTime";
-      break;
-    case ParamEvent::Type::kLinearRampToValue:
-      s = "linearRampToValueAtTime";
-      break;
-    case ParamEvent::Type::kExponentialRampToValue:
-      s = "exponentialRampToValue";
-      break;
-    case ParamEvent::Type::kSetTarget:
-      s = "setTargetAtTime";
-      // This has an extra time constant arg
-      args =
-          WTF::StrCat({args, ", ", String::Number(event.TimeConstant(), 16)});
-      break;
-    case ParamEvent::Type::kSetValueCurve:
-      s = "setValueCurveAtTime";
-      // Replace the default arg, using "..." to denote the curve argument.
-      args = WTF::StrCat({"..., ", String::Number(event.Time(), 16), ", ",
-                          String::Number(event.Duration(), 16)});
-      break;
-    case ParamEvent::Type::kCancelValues:
-    case ParamEvent::Type::kSetValueCurveEnd:
-    // Fall through; we should never have to print out the internal
-    // `kCancelValues` or `kSetValueCurveEnd` event.
-    case ParamEvent::Type::kLastType:
-      NOTREACHED();
-  };
-
-  return WTF::StrCat({s, "(", args, ")"});
-}
-
-// Computes the value of a linear ramp event at time t with the given event
-// parameters.
-float AudioParamTimeline::LinearRampAtTime(double t,
-                                           float value1,
-                                           double time1,
-                                           float value2,
-                                           double time2) {
-  return value1 + (value2 - value1) * (t - time1) / (time2 - time1);
-}
-
-// Computes the value of an exponential ramp event at time t with the given
-// event parameters.
-float AudioParamTimeline::ExponentialRampAtTime(double t,
-                                                float value1,
-                                                double time1,
-                                                float value2,
-                                                double time2) {
-  DCHECK(!std::isnan(value1) && std::isfinite(value1));
-  DCHECK(!std::isnan(value2) && std::isfinite(value2));
-
-  return (value1 == 0.0f || std::signbit(value1) != std::signbit(value2))
-      ? value1
-      : value1 * fdlibm::pow(value2 / value1, (t - time1) / (time2 - time1));
-}
-
-// Compute the value of a set target event at time t with the given event
-// parameters.
-float AudioParamTimeline::TargetValueAtTime(double t,
-                                            float value1,
-                                            double time1,
-                                            float value2,
-                                            float time_constant) {
-  return value2 + (value1 - value2) * fdlibm::exp(-(t - time1) / time_constant);
-}
-
-// Compute the value of a set curve event at time t with the given event
-// parameters.
-float AudioParamTimeline::ValueCurveAtTime(double t,
-                                           double time1,
-                                           double duration,
-                                           const float* curve_data,
-                                           unsigned curve_length) {
-  double curve_index = (curve_length - 1) / duration * (t - time1);
-  unsigned k = std::min(static_cast<unsigned>(curve_index), curve_length - 1);
-  unsigned k1 = std::min(k + 1, curve_length - 1);
-  float c0 = curve_data[k];
-  float c1 = curve_data[k1];
-  float delta = std::min(curve_index - k, 1.0);
-
-  return c0 + (c1 - c0) * delta;
-}
-
-std::unique_ptr<AudioParamTimeline::ParamEvent>
-AudioParamTimeline::ParamEvent::CreateSetValueEvent(float value, double time) {
-  return base::WrapUnique(
-      new ParamEvent(ParamEvent::Type::kSetValue, value, time));
-}
-
-std::unique_ptr<AudioParamTimeline::ParamEvent>
-AudioParamTimeline::ParamEvent::CreateLinearRampEvent(float value,
-                                                      double time,
-                                                      float initial_value,
-                                                      double call_time) {
-  return base::WrapUnique(new ParamEvent(ParamEvent::Type::kLinearRampToValue,
-                                         value, time, initial_value,
-                                         call_time));
-}
-
-std::unique_ptr<AudioParamTimeline::ParamEvent>
-AudioParamTimeline::ParamEvent::CreateExponentialRampEvent(float value,
-                                                           double time,
-                                                           float initial_value,
-                                                           double call_time) {
-  return base::WrapUnique(
-      new ParamEvent(ParamEvent::Type::kExponentialRampToValue, value, time,
-                     initial_value, call_time));
-}
-
-std::unique_ptr<AudioParamTimeline::ParamEvent>
-AudioParamTimeline::ParamEvent::CreateSetTargetEvent(float value,
-                                                     double time,
-                                                     double time_constant) {
-  // The time line code does not expect a timeConstant of 0. (IT
-  // returns NaN or Infinity due to division by zero.  The caller
-  // should have converted this to a SetValueEvent.
-  DCHECK_NE(time_constant, 0);
-  return base::WrapUnique(
-      new ParamEvent(ParamEvent::Type::kSetTarget, value, time, time_constant));
-}
-
-std::unique_ptr<AudioParamTimeline::ParamEvent>
-AudioParamTimeline::ParamEvent::CreateSetValueCurveEvent(
-    const Vector<float>& curve,
-    double time,
-    double duration) {
-  double curve_points = (curve.size() - 1) / duration;
-  float end_value = curve.data()[curve.size() - 1];
-
-  return base::WrapUnique(new ParamEvent(ParamEvent::Type::kSetValueCurve, time,
-                                         duration, curve, curve_points,
-                                         end_value));
-}
-
-std::unique_ptr<AudioParamTimeline::ParamEvent>
-AudioParamTimeline::ParamEvent::CreateSetValueCurveEndEvent(float value,
-                                                            double time) {
-  return base::WrapUnique(
-      new ParamEvent(ParamEvent::Type::kSetValueCurveEnd, value, time));
-}
-
-std::unique_ptr<AudioParamTimeline::ParamEvent>
-AudioParamTimeline::ParamEvent::CreateCancelValuesEvent(
-    double time,
-    std::unique_ptr<ParamEvent> saved_event) {
-  if (saved_event) {
-    // The savedEvent can only have certain event types.  Verify that.
-    ParamEvent::Type saved_type = saved_event->GetType();
-
-    DCHECK_NE(saved_type, ParamEvent::Type::kLastType);
-    DCHECK(saved_type == ParamEvent::Type::kLinearRampToValue ||
-           saved_type == ParamEvent::Type::kExponentialRampToValue ||
-           saved_type == ParamEvent::Type::kSetValueCurve);
-  }
-
-  return base::WrapUnique(new ParamEvent(ParamEvent::Type::kCancelValues, time,
-                                         std::move(saved_event)));
-}
-
-std::unique_ptr<AudioParamTimeline::ParamEvent>
-AudioParamTimeline::ParamEvent::CreateGeneralEvent(
-    Type type,
-    float value,
-    double time,
-    float initial_value,
-    double call_time,
-    double time_constant,
-    double duration,
-    Vector<float>& curve,
-    double curve_points_per_second,
-    float curve_end_value,
-    std::unique_ptr<ParamEvent> saved_event) {
-  return base::WrapUnique(new ParamEvent(
-      type, value, time, initial_value, call_time, time_constant, duration,
-      curve, curve_points_per_second, curve_end_value, std::move(saved_event)));
-}
-
-AudioParamTimeline::ParamEvent* AudioParamTimeline::ParamEvent::SavedEvent()
-    const {
-  DCHECK_EQ(GetType(), ParamEvent::Type::kCancelValues);
-  return saved_event_.get();
-}
-
-bool AudioParamTimeline::ParamEvent::HasDefaultCancelledValue() const {
-  DCHECK_EQ(GetType(), ParamEvent::Type::kCancelValues);
-  return has_default_cancelled_value_;
-}
-
-void AudioParamTimeline::ParamEvent::SetCancelledValue(float value) {
-  DCHECK_EQ(GetType(), ParamEvent::Type::kCancelValues);
-  value_ = value;
-  has_default_cancelled_value_ = true;
-}
-
-// General event
-AudioParamTimeline::ParamEvent::ParamEvent(
-    ParamEvent::Type type,
-    float value,
-    double time,
-    float initial_value,
-    double call_time,
-    double time_constant,
-    double duration,
-    Vector<float>& curve,
-    double curve_points_per_second,
-    float curve_end_value,
-    std::unique_ptr<ParamEvent> saved_event)
-    : type_(type),
-      value_(value),
-      time_(time),
-      initial_value_(initial_value),
-      call_time_(call_time),
-      time_constant_(time_constant),
-      duration_(duration),
-      curve_points_per_second_(curve_points_per_second),
-      curve_end_value_(curve_end_value),
-      saved_event_(std::move(saved_event)),
-      has_default_cancelled_value_(false) {
-  curve_ = curve;
-}
-
-// Create simplest event needing just a value and time, like setValueAtTime
-AudioParamTimeline::ParamEvent::ParamEvent(ParamEvent::Type type,
-                                           float value,
-                                           double time)
-    : type_(type),
-      value_(value),
-      time_(time),
-      initial_value_(0),
-      call_time_(0),
-      time_constant_(0),
-      duration_(0),
-      curve_points_per_second_(0),
-      curve_end_value_(0),
-      saved_event_(nullptr),
-      has_default_cancelled_value_(false) {
-  DCHECK(type == ParamEvent::Type::kSetValue ||
-         type == ParamEvent::Type::kSetValueCurveEnd);
-}
-
-// Create a linear or exponential ramp that requires an initial value and
-// time in case
-// there is no actual event that precedes this event.
-AudioParamTimeline::ParamEvent::ParamEvent(ParamEvent::Type type,
-                                           float value,
-                                           double time,
-                                           float initial_value,
-                                           double call_time)
-    : type_(type),
-      value_(value),
-      time_(time),
-      initial_value_(initial_value),
-      call_time_(call_time),
-      time_constant_(0),
-      duration_(0),
-      curve_points_per_second_(0),
-      curve_end_value_(0),
-      saved_event_(nullptr),
-      has_default_cancelled_value_(false) {
-  DCHECK(type == ParamEvent::Type::kLinearRampToValue ||
-         type == ParamEvent::Type::kExponentialRampToValue);
-}
-
-// Create an event needing a time constant (setTargetAtTime)
-AudioParamTimeline::ParamEvent::ParamEvent(ParamEvent::Type type,
-                                           float value,
-                                           double time,
-                                           double time_constant)
-    : type_(type),
-      value_(value),
-      time_(time),
-      initial_value_(0),
-      call_time_(0),
-      time_constant_(time_constant),
-      duration_(0),
-      curve_points_per_second_(0),
-      curve_end_value_(0),
-      saved_event_(nullptr),
-      has_default_cancelled_value_(false) {
-  DCHECK_EQ(type, ParamEvent::Type::kSetTarget);
-}
-
-// Create a setValueCurve event
-AudioParamTimeline::ParamEvent::ParamEvent(ParamEvent::Type type,
-                                           double time,
-                                           double duration,
-                                           const Vector<float>& curve,
-                                           double curve_points_per_second,
-                                           float curve_end_value)
-    : type_(type),
-      value_(0),
-      time_(time),
-      initial_value_(0),
-      call_time_(0),
-      time_constant_(0),
-      duration_(duration),
-      curve_points_per_second_(curve_points_per_second),
-      curve_end_value_(curve_end_value),
-      saved_event_(nullptr),
-      has_default_cancelled_value_(false) {
-  DCHECK_EQ(type, ParamEvent::Type::kSetValueCurve);
-  unsigned curve_length = curve.size();
-  curve_.resize(curve_length);
-  memcpy(curve_.data(), curve.data(), curve_length * sizeof(float));
-}
-
-// Create CancelValues event
-AudioParamTimeline::ParamEvent::ParamEvent(
-    ParamEvent::Type type,
-    double time,
-    std::unique_ptr<ParamEvent> saved_event)
-    : type_(type),
-      value_(0),
-      time_(time),
-      initial_value_(0),
-      call_time_(0),
-      time_constant_(0),
-      duration_(0),
-      curve_points_per_second_(0),
-      curve_end_value_(0),
-      saved_event_(std::move(saved_event)),
-      has_default_cancelled_value_(false) {
-  DCHECK_EQ(type, ParamEvent::Type::kCancelValues);
-}
-
-void AudioParamTimeline::SetValueAtTime(float value,
-                                        double time,
-                                        ExceptionState& exception_state) {
-  DCHECK(IsMainThread());
-
-  if (!IsNonNegativeAudioParamTime(time, exception_state)) {
-    return;
-  }
-
-  base::AutoLock locker(events_lock_);
-  InsertEvent(ParamEvent::CreateSetValueEvent(value, time), exception_state);
-}
-
-void AudioParamTimeline::LinearRampToValueAtTime(
-    float value,
-    double time,
-    float initial_value,
-    double call_time,
-    ExceptionState& exception_state) {
-  DCHECK(IsMainThread());
-
-  if (!IsNonNegativeAudioParamTime(time, exception_state)) {
-    return;
-  }
-
-  base::AutoLock locker(events_lock_);
-  InsertEvent(
-      ParamEvent::CreateLinearRampEvent(value, time, initial_value, call_time),
-      exception_state);
-}
-
-void AudioParamTimeline::ExponentialRampToValueAtTime(
-    float value,
-    double time,
-    float initial_value,
-    double call_time,
-    ExceptionState& exception_state) {
-  DCHECK(IsMainThread());
-
-  if (!IsNonNegativeAudioParamTime(time, exception_state)) {
-    return;
-  }
-
-  if (!value) {
-    exception_state.ThrowRangeError(WTF::StrCat(
-        {"The float target value provided (", String::Number(value),
-         ") should not be in the range (",
-         String::Number(-std::numeric_limits<float>::denorm_min()), ", ",
-         String::Number(std::numeric_limits<float>::denorm_min()), ")."}));
-    return;
-  }
-
-  base::AutoLock locker(events_lock_);
-  InsertEvent(ParamEvent::CreateExponentialRampEvent(value, time, initial_value,
-                                                     call_time),
-              exception_state);
-}
-
-void AudioParamTimeline::SetTargetAtTime(float target,
-                                         double time,
-                                         double time_constant,
-                                         ExceptionState& exception_state) {
-  DCHECK(IsMainThread());
-
-  if (!IsNonNegativeAudioParamTime(time, exception_state) ||
-      !IsNonNegativeAudioParamTime(time_constant, exception_state,
-                                   "Time constant")) {
-    return;
-  }
-
-  base::AutoLock locker(events_lock_);
-
-  // If timeConstant = 0, we instantly jump to the target value, so
-  // insert a SetValueEvent instead of SetTargetEvent.
-  if (time_constant == 0) {
-    InsertEvent(ParamEvent::CreateSetValueEvent(target, time), exception_state);
-  } else {
-    InsertEvent(ParamEvent::CreateSetTargetEvent(target, time, time_constant),
-                exception_state);
-  }
-}
-
-void AudioParamTimeline::SetValueCurveAtTime(const Vector<float>& curve,
-                                             double time,
-                                             double duration,
-                                             ExceptionState& exception_state) {
-  DCHECK(IsMainThread());
-
-  if (!IsNonNegativeAudioParamTime(time, exception_state) ||
-      !IsPositiveAudioParamTime(duration, exception_state, "Duration")) {
-    return;
-  }
-
-  if (curve.size() < 2) {
-    exception_state.ThrowDOMException(
-        DOMExceptionCode::kInvalidStateError,
-        ExceptionMessages::IndexExceedsMinimumBound("curve length",
-                                                    curve.size(), 2u));
-    return;
-  }
-
-  base::AutoLock locker(events_lock_);
-  InsertEvent(ParamEvent::CreateSetValueCurveEvent(curve, time, duration),
-              exception_state);
-
-  // Insert a setValueAtTime event too to establish an event so that all
-  // following events will process from the end of the curve instead of the
-  // beginning.
-  InsertEvent(ParamEvent::CreateSetValueCurveEndEvent(
-                  curve.data()[curve.size() - 1], time + duration),
-              exception_state);
-}
-
-void AudioParamTimeline::InsertEvent(std::unique_ptr<ParamEvent> event,
-                                     ExceptionState& exception_state) {
-  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("webaudio.audionode"),
-               "AudioParamTimeline::InsertEvent");
-
-  DCHECK(IsMainThread());
-
-  // Sanity check the event. Be super careful we're not getting infected with
-  // NaN or Inf. These should have been handled by the caller.
-  DCHECK_LT(event->GetType(), ParamEvent::Type::kLastType);
-  DCHECK(std::isfinite(event->Value()));
-  DCHECK(std::isfinite(event->Time()));
-  DCHECK(std::isfinite(event->TimeConstant()));
-  DCHECK(std::isfinite(event->Duration()));
-  DCHECK_GE(event->Duration(), 0);
-
-  double insert_time = event->Time();
-
-  if (!events_.size() &&
-      (event->GetType() == ParamEvent::Type::kLinearRampToValue ||
-       event->GetType() == ParamEvent::Type::kExponentialRampToValue)) {
-    // There are no events preceding these ramps.  Insert a new
-    // setValueAtTime event to set the starting point for these
-    // events.  Use a time of 0 to make sure it precedes all other
-    // events.  This will get fixed when when handle new events.
-    events_.insert(0, AudioParamTimeline::ParamEvent::CreateSetValueEvent(
-                          event->InitialValue(), 0));
-    new_events_.insert(events_[0].get());
-  }
-
-  if (events_.empty()) {
-    events_.insert(0, std::move(event));
-    new_events_.insert(events_[0].get());
-    return;
-  }
-
-  // Most of the time, we must insert after the last event. If the time of the
-  // last event is greater than the insert_time, use binary search to find the
-  // insertion point.
-  wtf_size_t insertion_idx = events_.size();
-  DCHECK_GT(insertion_idx, wtf_size_t{0});
-  wtf_size_t ub = insertion_idx - 1;  // upper bound of events that can overlap.
-  if (events_.back()->Time() > insert_time) {
-    auto it = std::upper_bound(
-        events_.begin(), events_.end(), insert_time,
-        [](const double value, const std::unique_ptr<ParamEvent>& entry) {
-          return value < entry->Time();
-        });
-    insertion_idx = static_cast<wtf_size_t>(std::distance(events_.begin(), it));
-    DCHECK_LT(insertion_idx, events_.size());
-    ub = insertion_idx;
-  }
-  DCHECK_LT(ub, static_cast<wtf_size_t>(std::numeric_limits<int>::max()));
-
-  if (event->GetType() == ParamEvent::Type::kSetValueCurve) {
-    double end_time = event->Time() + event->Duration();
-    for (int i = ub; i >= 0; i--) {
-      ParamEvent::Type test_type = events_[i]->GetType();
-      // Events of type `kSetValueCurveEnd` or `kCancelValues` never conflict.
-      if (test_type == ParamEvent::Type::kSetValueCurveEnd ||
-          test_type == ParamEvent::Type::kCancelValues) {
-        continue;
-      }
-      if (test_type == ParamEvent::Type::kSetValueCurve) {
-        // A SetValueCurve overlapping an existing SetValueCurve requires
-        // special care.
-        double test_end_time = events_[i]->Time() + events_[i]->Duration();
-        // Events are overlapped if the new event starts before the old event
-        // ends and the old event starts before the new event ends.
-        bool overlap =
-            event->Time() < test_end_time && events_[i]->Time() < end_time;
-        if (overlap) {
-          // If the start time of the event overlaps the start/end of an
-          // existing event or if the existing event end overlaps the
-          // start/end of the event, it's an error.
-          exception_state.ThrowDOMException(
-              DOMExceptionCode::kNotSupportedError,
-              WTF::StrCat({EventToString(*event), " overlaps ",
-                           EventToString(*events_[i])}));
-          return;
-        }
-      } else {
-        // Here we handle existing events of types other than
-        // `kSetValueCurveEnd`, `kCancelValues` and `kSetValueCurve`.
-        // Throw an error if an existing event starts in the middle of this
-        // SetValueCurve event.
-        if (events_[i]->Time() > event->Time() &&
-            events_[i]->Time() < end_time) {
-          exception_state.ThrowDOMException(
-              DOMExceptionCode::kNotSupportedError,
-              WTF::StrCat({EventToString(*event), " overlaps ",
-                           EventToString(*events_[i])}));
-          return;
-        }
-      }
-      if (events_[i]->Time() < insert_time) {
-        // We found an existing event, E, of type other than `kSetValueCurveEnd`
-        // or `kCancelValues` that starts before the new event of type
-        // `kSetValueCurve` that we want to insert. No earlier existing event
-        // can overlap with the new event. An overlapping `kSetValueCurve` would
-        // have overlapped with E too, so one of them would not be inserted.
-        // Other event types overlap with the new `kSetValueCurve` event only if
-        // they start in the middle of the new event, which is not the case.
-        break;
-      }
-    }
-  } else {
-    // Not a `SetValueCurve` new event. Make sure this new event doesn't overlap
-    // any existing `SetValueCurve` event.
-    for (int i = ub; i >= 0; i--) {
-      ParamEvent::Type test_type = events_[i]->GetType();
-      // Events of type `kSetValueCurveEnd` or `kCancelValues` never conflict.
-      if (test_type == ParamEvent::Type::kSetValueCurveEnd ||
-          test_type == ParamEvent::Type::kCancelValues) {
-        continue;
-      }
-      if (test_type == ParamEvent::Type::kSetValueCurve) {
-        double end_time = events_[i]->Time() + events_[i]->Duration();
-        if (event->GetType() != ParamEvent::Type::kSetValueCurveEnd &&
-            event->Time() >= events_[i]->Time() && event->Time() < end_time) {
-          exception_state.ThrowDOMException(
-              DOMExceptionCode::kNotSupportedError,
-              WTF::StrCat({EventToString(*event), " overlaps ",
-                           EventToString(*events_[i])}));
-          return;
-        }
-      }
-      if (events_[i]->Time() < insert_time) {
-        // We found an existing event, E, of type other than `kSetValueCurveEnd`
-        // or `kCancelValues` that starts before the new event that we want to
-        // insert. No earlier event of type `kSetValueCurve` can overlap with
-        // the new event, because it would have overlapped with E too.
-        break;
-      }
-    }
-  }
-
-  events_.insert(insertion_idx, std::move(event));
-  new_events_.insert(events_[insertion_idx].get());
-}
-
-bool AudioParamTimeline::HasValues(size_t current_frame,
-                                   double sample_rate,
-                                   unsigned render_quantum_frames) const {
-  base::AutoTryLock try_locker(events_lock_);
-
-  if (try_locker.is_acquired()) {
-    unsigned n_events = events_.size();
-
-    // Clearly, if there are no scheduled events, we have no timeline values.
-    if (n_events == 0) {
-      return false;
-    }
-
-    // Handle the case where the first event (of certain types) is in the
-    // future.  Then, no sample-accurate processing is needed because the event
-    // hasn't started.
-    if (events_[0]->Time() >
-        (current_frame + render_quantum_frames) / sample_rate) {
-      switch (events_[0]->GetType()) {
-        case ParamEvent::Type::kSetTarget:
-        case ParamEvent::Type::kSetValue:
-        case ParamEvent::Type::kSetValueCurve:
-          // If the first event is one of these types, and the event starts
-          // after the end of the current render quantum, we don't need to do
-          // the slow sample-accurate path.
-          return false;
-        default:
-          // Handle other event types below.
-          break;
-      }
-    }
-
-    // If there are at least 2 events in the timeline, assume there are timeline
-    // values.  This could be optimized to be more careful, but checking is
-    // complicated and keeping this consistent with `ValuesForFrameRangeImpl()`
-    // will be hard, so it's probably best to let the general timeline handle
-    // this until the events are in the past.
-    if (n_events >= 2) {
-      return true;
-    }
-
-    // We have exactly one event in the timeline.
-    switch (events_[0]->GetType()) {
-      case ParamEvent::Type::kSetTarget:
-        // Need automation if the event starts somewhere before the
-        // end of the current render quantum.
-        return events_[0]->Time() <=
-               (current_frame + render_quantum_frames) / sample_rate;
-      case ParamEvent::Type::kSetValue:
-      case ParamEvent::Type::kLinearRampToValue:
-      case ParamEvent::Type::kExponentialRampToValue:
-      case ParamEvent::Type::kCancelValues:
-      case ParamEvent::Type::kSetValueCurveEnd:
-        // If these events are in the past, we don't need any automation; the
-        // value is a constant.
-        return !(events_[0]->Time() < current_frame / sample_rate);
-      case ParamEvent::Type::kSetValueCurve: {
-        double curve_end_time = events_[0]->Time() + events_[0]->Duration();
-        double current_time = current_frame / sample_rate;
-
-        return (events_[0]->Time() <= current_time) &&
-               (current_time < curve_end_time);
-      }
-      case ParamEvent::Type::kLastType:
-        NOTREACHED();
-    }
-  }
-
-  // Can't get the lock so that means the main thread is trying to insert an
-  // event.  Just return true then.  If the main thread releases the lock before
-  // valueForContextTime or valuesForFrameRange runs, then the there will be an
-  // event on the timeline, so everything is fine.  If the lock is held so that
-  // neither valueForContextTime nor valuesForFrameRange can run, this is ok
-  // too, because they have tryLocks to produce a default value.  The event will
-  // then get processed in the next rendering quantum.
-  //
-  // Don't want to return false here because that would confuse the processing
-  // of the timeline if previously we returned true and now suddenly return
-  // false, only to return true on the next rendering quantum.  Currently, once
-  // a timeline has been introduced it is always true forever because m_events
-  // never shrinks.
-  return true;
-}
-
-void AudioParamTimeline::CancelScheduledValues(
-    double cancel_time,
-    ExceptionState& exception_state) {
-  DCHECK(IsMainThread());
-
-  if (!IsNonNegativeAudioParamTime(cancel_time, exception_state)) {
-    return;
-  }
-
-  base::AutoLock locker(events_lock_);
-
-  // Remove all events starting at startTime.
-  for (wtf_size_t i = 0; i < events_.size(); ++i) {
-    // Removal all events whose event time (start) is greater than or
-    // equal to the cancel time.  And also handle the special case
-    // where the cancel time lies in the middle of a setValueCurve
-    // event.
-    //
-    // This critically depends on the fact that no event can be
-    // scheduled in the middle of the curve or at the same start time.
-    // Then removing the setValueCurve doesn't remove any events that
-    // shouldn't have been.
-    double start_time = events_[i]->Time();
-
-    if (start_time >= cancel_time ||
-        ((events_[i]->GetType() == ParamEvent::Type::kSetValueCurve) &&
-         start_time <= cancel_time &&
-         (start_time + events_[i]->Duration() > cancel_time))) {
-      RemoveCancelledEvents(i);
-      break;
-    }
-  }
-}
-
-void AudioParamTimeline::CancelAndHoldAtTime(double cancel_time,
-                                             ExceptionState& exception_state) {
-  DCHECK(IsMainThread());
-
-  if (!IsNonNegativeAudioParamTime(cancel_time, exception_state)) {
-    return;
-  }
-
-  base::AutoLock locker(events_lock_);
-
-  wtf_size_t i;
-  // Find the first event at or just past `cancel_time`.
-  for (i = 0; i < events_.size(); ++i) {
-    if (events_[i]->Time() > cancel_time) {
-      break;
-    }
-  }
-
-  // The event that is being cancelled.  This is the event just past
-  // `cancel_time`, if any.
-  wtf_size_t cancelled_event_index = i;
-
-  // If the event just before `cancel_time` is a SetTarget or SetValueCurve
-  // event, we need to handle that event specially instead of the event after.
-  if (i > 0 &&
-      ((events_[i - 1]->GetType() == ParamEvent::Type::kSetTarget) ||
-       (events_[i - 1]->GetType() == ParamEvent::Type::kSetValueCurve))) {
-    cancelled_event_index = i - 1;
-  } else if (i >= events_.size()) {
-    // If there were no events occurring after `cancel_time` (and the
-    // previous event is not SetTarget or SetValueCurve, we're done.
-    return;
-  }
-
-  // cancelledEvent is the event that is being cancelled.
-  ParamEvent* cancelled_event = events_[cancelled_event_index].get();
-  ParamEvent::Type event_type = cancelled_event->GetType();
-
-  // New event to be inserted, if any, and a SetValueEvent if needed.
-  std::unique_ptr<ParamEvent> new_event;
-  std::unique_ptr<ParamEvent> new_set_value_event;
-
-  switch (event_type) {
-    case ParamEvent::Type::kLinearRampToValue:
-    case ParamEvent::Type::kExponentialRampToValue: {
-      // For these events we need to remember the parameters of the event
-      // for a CancelValues event so that we can properly cancel the event
-      // and hold the value.
-      std::unique_ptr<ParamEvent> saved_event = ParamEvent::CreateGeneralEvent(
-          event_type, cancelled_event->Value(), cancelled_event->Time(),
-          cancelled_event->InitialValue(), cancelled_event->CallTime(),
-          cancelled_event->TimeConstant(), cancelled_event->Duration(),
-          cancelled_event->Curve(), cancelled_event->CurvePointsPerSecond(),
-          cancelled_event->CurveEndValue(), nullptr);
-
-      new_event = ParamEvent::CreateCancelValuesEvent(cancel_time,
-                                                      std::move(saved_event));
-    } break;
-    case ParamEvent::Type::kSetTarget: {
-      if (cancelled_event->Time() < cancel_time) {
-        // Don't want to remove the SetTarget event if it started before the
-        // cancel time, so bump the index.  But we do want to insert a
-        // cancelEvent so that we stop this automation and hold the value when
-        // we get there.
-        ++cancelled_event_index;
-
-        new_event = ParamEvent::CreateCancelValuesEvent(cancel_time, nullptr);
-      }
-    } break;
-    case ParamEvent::Type::kSetValueCurve: {
-      // If the setValueCurve event started strictly before the cancel time,
-      // there might be something to do....
-      if (cancelled_event->Time() < cancel_time) {
-        if (cancel_time >
-            cancelled_event->Time() + cancelled_event->Duration()) {
-          // If the cancellation time is past the end of the curve there's
-          // nothing to do except remove the following events.
-          ++cancelled_event_index;
-        } else {
-          // Cancellation time is in the middle of the curve.  Therefore,
-          // create a new SetValueCurve event with the appropriate new
-          // parameters to cancel this event properly.  Since it's illegal
-          // to insert any event within a SetValueCurve event, we can
-          // compute the new end value now instead of doing when running
-          // the timeline.
-          double new_duration = cancel_time - cancelled_event->Time();
-          float end_value = ValueCurveAtTime(
-              cancel_time, cancelled_event->Time(), cancelled_event->Duration(),
-              cancelled_event->Curve().data(), cancelled_event->Curve().size());
-
-          // Replace the existing SetValueCurve with this new one that is
-          // identical except for the duration.
-          new_event = ParamEvent::CreateGeneralEvent(
-              event_type, cancelled_event->Value(), cancelled_event->Time(),
-              cancelled_event->InitialValue(), cancelled_event->CallTime(),
-              cancelled_event->TimeConstant(), new_duration,
-              cancelled_event->Curve(), cancelled_event->CurvePointsPerSecond(),
-              end_value, nullptr);
-
-          new_set_value_event = ParamEvent::CreateSetValueEvent(
-              end_value, cancelled_event->Time() + new_duration);
-        }
-      }
-    } break;
-    case ParamEvent::Type::kSetValue:
-    case ParamEvent::Type::kSetValueCurveEnd:
-    case ParamEvent::Type::kCancelValues:
-      // Nothing needs to be done for a SetValue or CancelValues event.
-      break;
-    case ParamEvent::Type::kLastType:
-      NOTREACHED();
-  }
-
-  // Now remove all the following events from the timeline.
-  if (cancelled_event_index < events_.size()) {
-    RemoveCancelledEvents(cancelled_event_index);
-  }
-
-  // Insert the new event, if any.
-  if (new_event) {
-    InsertEvent(std::move(new_event), exception_state);
-    if (new_set_value_event) {
-      InsertEvent(std::move(new_set_value_event), exception_state);
-    }
-  }
-}
-
-std::tuple<bool, float> AudioParamTimeline::ValueForContextTime(
-    AudioDestinationHandler& audio_destination,
-    float default_value,
-    float min_value,
-    float max_value,
-    unsigned render_quantum_frames) {
-  {
-    base::AutoTryLock try_locker(events_lock_);
-    if (!try_locker.is_acquired() || !events_.size() ||
-        audio_destination.CurrentTime() < events_[0]->Time()) {
-      return std::make_tuple(false, default_value);
-    }
-  }
-
-  // Ask for just a single value.
-  float value;
-  double sample_rate = audio_destination.SampleRate();
-  size_t start_frame = audio_destination.CurrentSampleFrame();
-  // One parameter change per render quantum.
-  double control_rate = sample_rate / render_quantum_frames;
-  value = ValuesForFrameRange(start_frame, start_frame + 1, default_value,
-                              &value, 1, sample_rate, control_rate, min_value,
-                              max_value, render_quantum_frames);
-
-  return std::make_tuple(true, value);
-}
-
-float AudioParamTimeline::ValuesForFrameRange(size_t start_frame,
-                                              size_t end_frame,
-                                              float default_value,
-                                              float* values,
-                                              unsigned number_of_values,
-                                              double sample_rate,
-                                              double control_rate,
-                                              float min_value,
-                                              float max_value,
-                                              unsigned render_quantum_frames) {
-  // We can't contend the lock in the realtime audio thread.
-  base::AutoTryLock try_locker(events_lock_);
-  if (!try_locker.is_acquired()) {
-    if (values) {
-      for (unsigned i = 0; i < number_of_values; ++i) {
-        values[i] = default_value;
-      }
-    }
-    return default_value;
-  }
-
-  float last_value = ValuesForFrameRangeImpl(
-      start_frame, end_frame, default_value, values, number_of_values,
-      sample_rate, control_rate, render_quantum_frames);
-
-  // Clamp the values now to the nominal range
-  vector_math::Vclip(values, 1, &min_value, &max_value, values, 1,
-                     number_of_values);
-
-  return last_value;
-}
-
-float AudioParamTimeline::ValuesForFrameRangeImpl(
-    size_t start_frame,
-    size_t end_frame,
-    float default_value,
-    float* values,
-    unsigned number_of_values,
-    double sample_rate,
-    double control_rate,
-    unsigned render_quantum_frames) {
-  DCHECK(values);
-  DCHECK_GE(number_of_values, 1u);
-
-  // Return default value if there are no events matching the desired time
-  // range.
-  if (!events_.size() || (end_frame / sample_rate <= events_[0]->Time())) {
-    FillWithDefault(values, default_value, number_of_values, 0);
-
-    return default_value;
-  }
-
-  int number_of_events = events_.size();
-
-  // MUST clamp event before `events_` is possibly mutated because
-  // `new_events_` has raw pointers to objects in `events_`.  Clamping
-  // will clear out all of these pointers before `events_` is
-  // potentially modified.
-  //
-  // TODO(rtoy): Consider making `events_` be scoped_refptr instead of
-  // unique_ptr.
-  if (new_events_.size() > 0) {
-    ClampNewEventsToCurrentTime(start_frame / sample_rate);
-  }
-
-  if (number_of_events > 0) {
-    double current_time = start_frame / sample_rate;
-
-    if (HandleAllEventsInThePast(current_time, sample_rate, default_value,
-                                 number_of_values, values,
-                                 render_quantum_frames)) {
-      return default_value;
-    }
-  }
-
-  // Maintain a running time (frame) and index for writing the values buffer.
-  // If first event is after startFrame then fill initial part of values buffer
-  // with defaultValue until we reach the first event time.
-  auto [current_frame, write_index] =
-      HandleFirstEvent(values, default_value, number_of_values, start_frame,
-                       end_frame, sample_rate, start_frame, 0);
-
-  float value = default_value;
-
-  // Go through each event and render the value buffer where the times overlap,
-  // stopping when we've rendered all the requested values.
-  int last_skipped_event_index = 0;
-  for (int i = 0; i < number_of_events && write_index < number_of_values; ++i) {
-    ParamEvent* event = events_[i].get();
-    ParamEvent* next_event =
-        i < number_of_events - 1 ? events_[i + 1].get() : nullptr;
-
-    // Wait until we get a more recent event.
-    if (!IsEventCurrent(event, next_event, current_frame, sample_rate)) {
-      // This is not the special SetValue event case, and nextEvent is
-      // in the past. We can skip processing of this event since it's
-      // in past. We keep track of this event in lastSkippedEventIndex
-      // to note what events we've skipped.
-      last_skipped_event_index = i;
-      continue;
-    }
-
-    // If there's no next event, set nextEventType to LastType to indicate that.
-    ProcessSetTargetFollowedByRamp(
-        i, event,
-        next_event ? static_cast<ParamEvent::Type>(next_event->GetType())
-                   : ParamEvent::Type::kLastType,
-        current_frame, sample_rate, control_rate, value);
-
-    float value1 = event->Value();
-    double time1 = event->Time();
-
-    // Check to see if an event was cancelled.
-    auto [value2, time2, next_event_type] = HandleCancelValues(
-        event, next_event, next_event ? next_event->Value() : value1,
-        next_event ? next_event->Time() : end_frame / sample_rate + 1);
-
-    DCHECK(!std::isnan(value1));
-    DCHECK(!std::isnan(value2));
-    DCHECK_GE(time2, time1);
-
-    // `fill_to_end_frame` is the exclusive upper bound of the last frame to be
-    // computed for this event.  It's either the last desired frame
-    // (`end_frame`) or derived from the end time of the next event
-    // (`time2`). We compute ceil(`time2`*`sample_rate`) because
-    // `fill_to_end_frame` is the exclusive upper bound.  Consider the case
-    // where `start_frame` = 128 and `time2` = 128.1 (assuming `sample_rate` =
-    // 1).  Since `time2` is greater than 128, we want to output a value for
-    // frame 128.  This requires that `fill_to_end_frame` be at least 129.  This
-    // is achieved by ceil(`time2`).
-    //
-    // However, `time2` can be very large, so compute this carefully in the case
-    // where `time2` exceeds the size of a size_t.
-
-    size_t fill_to_end_frame = end_frame;
-    if (end_frame > time2 * sample_rate) {
-      fill_to_end_frame = static_cast<size_t>(ceil(time2 * sample_rate));
-    }
-
-    DCHECK_GE(fill_to_end_frame, start_frame);
-    unsigned fill_to_frame =
-        static_cast<unsigned>(fill_to_end_frame - start_frame);
-    fill_to_frame = std::min(fill_to_frame, number_of_values);
-
-    const AutomationState current_state = {
-        number_of_values,
-        start_frame,
-        end_frame,
-        sample_rate,
-        control_rate,
-        fill_to_frame,
-        fill_to_end_frame,
-        value1,
-        time1,
-        value2,
-        time2,
-        event,
-        i,
-    };
-
-    // First handle linear and exponential ramps which require looking ahead to
-    // the next event.
-    if (next_event_type == ParamEvent::Type::kLinearRampToValue) {
-      std::tie(current_frame, value, write_index) = ProcessLinearRamp(
-          current_state, values, current_frame, value, write_index);
-    } else if (next_event_type == ParamEvent::Type::kExponentialRampToValue) {
-      std::tie(current_frame, value, write_index) = ProcessExponentialRamp(
-          current_state, values, current_frame, value, write_index);
-    } else {
-      // Handle event types not requiring looking ahead to the next event.
-      switch (event->GetType()) {
-        case ParamEvent::Type::kSetValue:
-        case ParamEvent::Type::kSetValueCurveEnd:
-        case ParamEvent::Type::kLinearRampToValue: {
-          current_frame = fill_to_end_frame;
-
-          // Simply stay at a constant value.
-          value = event->Value();
-          write_index =
-              FillWithDefault(values, value, fill_to_frame, write_index);
-          break;
-        }
-
-        case ParamEvent::Type::kCancelValues: {
-          std::tie(current_frame, value, write_index) = ProcessCancelValues(
-              current_state, values, current_frame, value, write_index);
-          break;
-        }
-
-        case ParamEvent::Type::kExponentialRampToValue: {
-          current_frame = fill_to_end_frame;
-
-          // If we're here, we've reached the end of the ramp.  For
-          // the values after the end of the ramp, we just want to
-          // continue with the ramp end value.
-          value = event->Value();
-          write_index =
-              FillWithDefault(values, value, fill_to_frame, write_index);
-
-          break;
-        }
-
-        case ParamEvent::Type::kSetTarget: {
-          std::tie(current_frame, value, write_index) = ProcessSetTarget(
-              current_state, values, current_frame, value, write_index);
-          break;
-        }
-
-        case ParamEvent::Type::kSetValueCurve: {
-          std::tie(current_frame, value, write_index) = ProcessSetValueCurve(
-              current_state, values, current_frame, value, write_index);
-          break;
-        }
-        case ParamEvent::Type::kLastType:
-          NOTREACHED();
-      }
-    }
-  }
-
-  // If we skipped over any events (because they are in the past), we can
-  // remove them so we don't have to check them ever again.  (This MUST be
-  // running with the m_events lock so we can safely modify the m_events
-  // array.)
-  if (last_skipped_event_index > 0) {
-    // `new_events_` should be empty here so we don't have to
-    // do any updates due to this mutation of `events_`.
-    DCHECK_EQ(new_events_.size(), 0u);
-    RemoveOldEvents(last_skipped_event_index - 1);
-  }
-
-  // If there's any time left after processing the last event then just
-  // propagate the last value to the end of the values buffer.
-  write_index = FillWithDefault(values, value, number_of_values, write_index);
-
-  // This value is used to set the `.value` attribute of the AudioParam.  it
-  // should be the last computed value.
-  return values[number_of_values - 1];
-}
-
-std::tuple<size_t, unsigned> AudioParamTimeline::HandleFirstEvent(
-    float* values,
-    float default_value,
-    unsigned number_of_values,
-    size_t start_frame,
-    size_t end_frame,
-    double sample_rate,
-    size_t current_frame,
-    unsigned write_index) {
-  double first_event_time = events_[0]->Time();
-  if (first_event_time > start_frame / sample_rate) {
-    // `fill_to_frame` is an exclusive upper bound, so use ceil() to compute the
-    // bound from the `first_event_time`.
-    size_t fill_to_end_frame = end_frame;
-    double first_event_frame = ceil(first_event_time * sample_rate);
-    if (end_frame > first_event_frame) {
-      fill_to_end_frame = first_event_frame;
-    }
-    DCHECK_GE(fill_to_end_frame, start_frame);
-
-    unsigned fill_to_frame =
-        static_cast<unsigned>(fill_to_end_frame - start_frame);
-    fill_to_frame = std::min(fill_to_frame, number_of_values);
-    write_index =
-        FillWithDefault(values, default_value, fill_to_frame, write_index);
-
-    current_frame += fill_to_frame;
-  }
-
-  return std::make_tuple(current_frame, write_index);
-}
-
-bool AudioParamTimeline::IsEventCurrent(const ParamEvent* event,
-                                        const ParamEvent* next_event,
-                                        size_t current_frame,
-                                        double sample_rate) const {
-  // WARNING: due to round-off it might happen that `next_event->Time()` is just
-  // larger than `current_frame`/`sample_rate`.  This means that we will end up
-  // running the `event` again.  The code below had better be prepared for this
-  // case!  What should happen is the fillToFrame should be 0 so that while the
-  // event is actually run again, nothing actually gets computed, and we move on
-  // to the next event.
-  //
-  // An example of this case is `SetValueCurveAtTime()`.  The time at which
-  // `SetValueCurveAtTime()` ends (and the `SetValueAtTime()` begins) might be
-  // just past `current_time`/`sample_rate`.  Then `SetValueCurveAtTime()` will
-  // be processed again before advancing to `SetValueAtTime()`.  The number of
-  // frames to be processed should be zero in this case.
-  if (next_event && next_event->Time() < current_frame / sample_rate) {
-    // But if the current event is a SetValue event and the event time is
-    // between currentFrame - 1 and currentFrame (in time). we don't want to
-    // skip it.  If we do skip it, the SetValue event is completely skipped
-    // and not applied, which is wrong.  Other events don't have this problem.
-    // (Because currentFrame is unsigned, we do the time check in this funny,
-    // but equivalent way.)
-    double event_frame = event->Time() * sample_rate;
-
-    // Condition is currentFrame - 1 < eventFrame <= currentFrame, but
-    // currentFrame is unsigned and could be 0, so use
-    // currentFrame < eventFrame + 1 instead.
-    if (!(((event->GetType() == ParamEvent::Type::kSetValue ||
-            event->GetType() == ParamEvent::Type::kSetValueCurveEnd) &&
-           (event_frame <= current_frame) &&
-           (current_frame < event_frame + 1)))) {
-      // This is not the special SetValue event case, and nextEvent is
-      // in the past. We can skip processing of this event since it's
-      // in past.
-      return false;
-    }
-  }
-  return true;
-}
-
-void AudioParamTimeline::ClampNewEventsToCurrentTime(double current_time) {
-  bool clamped_some_event_time = false;
-
-  for (auto* event : new_events_) {
-    if (event->Time() < current_time) {
-      event->SetTime(current_time);
-      clamped_some_event_time = true;
-    }
-  }
-
-  if (clamped_some_event_time) {
-    // If we clamped some event time to current time, we need to sort
-    // the event list in time order again, but it must be stable!
-    std::stable_sort(events_.begin(), events_.end(), ParamEvent::EventPrecedes);
-  }
-
-  new_events_.clear();
-}
-
-bool AudioParamTimeline::HandleAllEventsInThePast(
-    double current_time,
-    double sample_rate,
-    float& default_value,
-    unsigned number_of_values,
-    float* values,
-    unsigned render_quantum_frames) {
-  // Optimize the case where the last event is in the past.
-  ParamEvent* last_event = events_[events_.size() - 1].get();
-  ParamEvent::Type last_event_type = last_event->GetType();
-  double last_event_time = last_event->Time();
-
-  // If the last event is in the past and the event has ended, then we can
-  // just propagate the same value.  Except for SetTarget which lasts
-  // "forever".  SetValueCurve also has an explicit SetValue at the end of
-  // the curve, so we don't need to worry that SetValueCurve time is a
-  // start time, not an end time.
-  if (last_event_time + 1.5 * render_quantum_frames / sample_rate <
-      current_time) {
-    // If the last event is SetTarget, make sure we've converged and, that
-    // we're at least 5 time constants past the start of the event.  If not, we
-    // have to continue processing it.
-    if (last_event_type == ParamEvent::Type::kSetTarget) {
-      if (HasSetTargetConverged(default_value, last_event->Value(),
-                                current_time, last_event_time,
-                                last_event->TimeConstant())) {
-        // We've converged. Slam the default value with the target value.
-        default_value = last_event->Value();
-      } else {
-        // Not converged, so give up; we can't remove this event yet.
-        return false;
-      }
-    }
-
-    // `events_` is being mutated.  `new_events_` better be empty because there
-    // are raw pointers there.
-    DCHECK_EQ(new_events_.size(), 0U);
-    // The event has finished, so just copy the default value out.
-    // Since all events are now also in the past, we can just remove all
-    // timeline events too because `default_value` has the expected
-    // value.
-    FillWithDefault(values, default_value, number_of_values, 0);
-    RemoveOldEvents(events_.size());
-
-    return true;
-  }
-
-  return false;
-}
-
-void AudioParamTimeline::ProcessSetTargetFollowedByRamp(
-    int event_index,
-    ParamEvent*& event,
-    ParamEvent::Type next_event_type,
-    size_t current_frame,
-    double sample_rate,
-    double control_rate,
-    float& value) {
-  // If the current event is SetTarget and the next event is a
-  // LinearRampToValue or ExponentialRampToValue, special handling is needed.
-  // In this case, the linear and exponential ramp should start at wherever
-  // the SetTarget processing has reached.
-  if (event->GetType() == ParamEvent::Type::kSetTarget &&
-      (next_event_type == ParamEvent::Type::kLinearRampToValue ||
-       next_event_type == ParamEvent::Type::kExponentialRampToValue)) {
-    // Replace the SetTarget with a SetValue to set the starting time and
-    // value for the ramp using the current frame.  We need to update `value`
-    // appropriately depending on whether the ramp has started or not.
-    //
-    // If SetTarget starts somewhere between currentFrame - 1 and
-    // currentFrame, we directly compute the value it would have at
-    // currentFrame.  If not, we update the value from the value from
-    // currentFrame - 1.
-    //
-    // Can't use the condition currentFrame - 1 <= t0 * sampleRate <=
-    // currentFrame because currentFrame is unsigned and could be 0.  Instead,
-    // compute the condition this way,
-    // where f = currentFrame and Fs = sampleRate:
-    //
-    //    f - 1 <= t0 * Fs <= f
-    //    2 * f - 2 <= 2 * Fs * t0 <= 2 * f
-    //    -2 <= 2 * Fs * t0 - 2 * f <= 0
-    //    -1 <= 2 * Fs * t0 - 2 * f + 1 <= 1
-    //     abs(2 * Fs * t0 - 2 * f + 1) <= 1
-    if (fabs(2 * sample_rate * event->Time() - 2 * current_frame + 1) <= 1) {
-      // SetTarget is starting somewhere between currentFrame - 1 and
-      // currentFrame. Compute the value the SetTarget would have at the
-      // currentFrame.
-      value = event->Value() +
-              (value - event->Value()) *
-                  fdlibm::exp(-(current_frame / sample_rate - event->Time()) /
-                              event->TimeConstant());
-    } else {
-      // SetTarget has already started.  Update `value` one frame because it's
-      // the value from the previous frame.
-      float discrete_time_constant =
-          static_cast<float>(audio_utilities::DiscreteTimeConstantForSampleRate(
-              event->TimeConstant(), control_rate));
-      value += (event->Value() - value) * discrete_time_constant;
-    }
-
-    // Insert a SetValueEvent to mark the starting value and time.
-    // Clear the clamp check because this doesn't need it.
-    events_[event_index] =
-        ParamEvent::CreateSetValueEvent(value, current_frame / sample_rate);
-
-    // Update our pointer to the current event because we just changed it.
-    event = events_[event_index].get();
-  }
-}
-
-std::tuple<float, double, AudioParamTimeline::ParamEvent::Type>
-AudioParamTimeline::HandleCancelValues(const ParamEvent* current_event,
-                                       ParamEvent* next_event,
-                                       float value2,
-                                       double time2) {
-  DCHECK(current_event);
-
-  ParamEvent::Type next_event_type =
-      next_event ? next_event->GetType() : ParamEvent::Type::kLastType;
-
-  if (next_event && next_event->GetType() == ParamEvent::Type::kCancelValues &&
-      next_event->SavedEvent()) {
-    float value1 = current_event->Value();
-    double time1 = current_event->Time();
-
-    switch (current_event->GetType()) {
-      case ParamEvent::Type::kCancelValues:
-      case ParamEvent::Type::kLinearRampToValue:
-      case ParamEvent::Type::kExponentialRampToValue:
-      case ParamEvent::Type::kSetValueCurveEnd:
-      case ParamEvent::Type::kSetValue: {
-        // These three events potentially establish a starting value for
-        // the following event, so we need to examine the cancelled
-        // event to see what to do.
-        const ParamEvent* saved_event = next_event->SavedEvent();
-
-        // Update the end time and type to pretend that we're running
-        // this saved event type.
-        time2 = next_event->Time();
-        next_event_type = saved_event->GetType();
-
-        if (next_event->HasDefaultCancelledValue()) {
-          // We've already established a value for the cancelled
-          // event, so just return it.
-          value2 = next_event->Value();
-        } else {
-          // If the next event would have been a LinearRamp or
-          // ExponentialRamp, we need to compute a new end value for
-          // the event so that the curve works continues as if it were
-          // not cancelled.
-          switch (saved_event->GetType()) {
-            case ParamEvent::Type::kLinearRampToValue:
-              value2 =
-                  LinearRampAtTime(next_event->Time(), value1, time1,
-                                   saved_event->Value(), saved_event->Time());
-              break;
-            case ParamEvent::Type::kExponentialRampToValue:
-              value2 = ExponentialRampAtTime(next_event->Time(), value1, time1,
-                                             saved_event->Value(),
-                                             saved_event->Time());
-              DCHECK(!std::isnan(value1));
-              break;
-            case ParamEvent::Type::kSetValueCurve:
-            case ParamEvent::Type::kSetValueCurveEnd:
-            case ParamEvent::Type::kSetValue:
-            case ParamEvent::Type::kSetTarget:
-            case ParamEvent::Type::kCancelValues:
-              // These cannot be possible types for the saved event
-              // because they can't be created.
-              // createCancelValuesEvent doesn't allow them (SetValue,
-              // SetTarget, CancelValues) or cancelScheduledValues()
-              // doesn't create such an event (SetValueCurve).
-              NOTREACHED();
-            case ParamEvent::Type::kLastType:
-              // Illegal event type.
-              NOTREACHED();
-          }
-
-          // Cache the new value so we don't keep computing it over and over.
-          next_event->SetCancelledValue(value2);
-        }
-      } break;
-      case ParamEvent::Type::kSetValueCurve:
-        // Everything needed for this was handled when cancelling was
-        // done.
-        break;
-      case ParamEvent::Type::kSetTarget:
-        // Nothing special needs to be done for SetTarget
-        // followed by CancelValues.
-        break;
-      case ParamEvent::Type::kLastType:
-        NOTREACHED();
-    }
-  }
-
-  return std::make_tuple(value2, time2, next_event_type);
-}
-
-std::tuple<size_t, float, unsigned> AudioParamTimeline::ProcessLinearRamp(
-    const AutomationState& current_state,
-    float* values,
-    size_t current_frame,
-    float value,
-    unsigned write_index) {
-#if defined(ARCH_CPU_X86_FAMILY)
-  auto number_of_values = current_state.number_of_values;
-#endif
-  auto fill_to_frame = current_state.fill_to_frame;
-  auto time1 = current_state.time1;
-  auto time2 = current_state.time2;
-  auto value1 = current_state.value1;
-  auto value2 = current_state.value2;
-  auto sample_rate = current_state.sample_rate;
-
-  double delta_time = time2 - time1;
-  DCHECK_GE(delta_time, 0);
-  // Since delta_time is a double, 1/delta_time can easily overflow a float.
-  // Thus, if delta_time is close enough to zero (less than float min), treat it
-  // as zero.
-  float k =
-      delta_time <= std::numeric_limits<float>::min() ? 0 : 1 / delta_time;
-  const float value_delta = value2 - value1;
-#if defined(ARCH_CPU_X86_FAMILY)
-  if (fill_to_frame > write_index) {
-    // Minimize in-loop operations. Calculate starting value and increment.
-    // Next step: value += inc.
-    //  value = value1 +
-    //      (currentFrame/sampleRate - time1) * k * (value2 - value1);
-    //  inc = 4 / sampleRate * k * (value2 - value1);
-    // Resolve recursion by expanding constants to achieve a 4-step loop
-    // unrolling.
-    //  value = value1 +
-    //    ((currentFrame/sampleRate - time1) + i * sampleFrameTimeIncr) * k
-    //    * (value2 -value1), i in 0..3
-    __m128 v_value =
-        _mm_mul_ps(_mm_set_ps1(1 / sample_rate), _mm_set_ps(3, 2, 1, 0));
-    v_value =
-        _mm_add_ps(v_value, _mm_set_ps1(current_frame / sample_rate - time1));
-    v_value = _mm_mul_ps(v_value, _mm_set_ps1(k * value_delta));
-    v_value = _mm_add_ps(v_value, _mm_set_ps1(value1));
-    __m128 v_inc = _mm_set_ps1(4 / sample_rate * k * value_delta);
-
-    // Truncate loop steps to multiple of 4.
-    unsigned fill_to_frame_trunc =
-        write_index + ((fill_to_frame - write_index) / 4) * 4;
-    // Compute final time.
-    DCHECK_LE(fill_to_frame_trunc, number_of_values);
-    current_frame += fill_to_frame_trunc - write_index;
-
-    // Process 4 loop steps.
-    for (; write_index < fill_to_frame_trunc; write_index += 4) {
-      _mm_storeu_ps(values + write_index, v_value);
-      v_value = _mm_add_ps(v_value, v_inc);
-    }
-  }
-  // Update `value` with the last value computed so that the
-  // `.value` attribute of the AudioParam gets the correct linear
-  // ramp value, in case the following loop doesn't execute.
-  if (write_index >= 1) {
-    value = values[write_index - 1];
-  }
-#endif
-  // Serially process remaining values.
-  for (; write_index < fill_to_frame; ++write_index) {
-    float x = (current_frame / sample_rate - time1) * k;
-    // value = (1 - x) * value1 + x * value2;
-    value = value1 + x * value_delta;
-    values[write_index] = value;
-    ++current_frame;
-  }
-
-  return std::make_tuple(current_frame, value, write_index);
-}
-
-std::tuple<size_t, float, unsigned> AudioParamTimeline::ProcessExponentialRamp(
-    const AutomationState& current_state,
-    float* values,
-    size_t current_frame,
-    float value,
-    unsigned write_index) {
-  auto fill_to_frame = current_state.fill_to_frame;
-  auto time1 = current_state.time1;
-  auto time2 = current_state.time2;
-  auto value1 = current_state.value1;
-  auto value2 = current_state.value2;
-  auto sample_rate = current_state.sample_rate;
-
-  if (value1 * value2 <= 0 || time1 >= time2) {
-    // It's an error 1) if `value1` and `value2` have opposite signs or if one
-    // of them is zero, or 2) if `time1` is greater than or equal to `time2`.
-    // Handle this by propagating the previous value.
-    value = value1;
-
-    for (; write_index < fill_to_frame; ++write_index) {
-      values[write_index] = value;
-    }
-  } else {
-    double delta_time = time2 - time1;
-    double num_sample_frames = delta_time * sample_rate;
-    // The value goes exponentially from value1 to value2 in a duration of
-    // deltaTime seconds according to
-    //
-    //  v(t) = v1*(v2/v1)^((t-t1)/(t2-t1))
-    //
-    // Let c be currentFrame and F be the sampleRate.  Then we want to
-    // sample v(t) at times t = (c + k)/F for k = 0, 1, ...:
-    //
-    //   v((c+k)/F) = v1*(v2/v1)^(((c/F+k/F)-t1)/(t2-t1))
-    //              = v1*(v2/v1)^((c/F-t1)/(t2-t1))
-    //                  *(v2/v1)^((k/F)/(t2-t1))
-    //              = v1*(v2/v1)^((c/F-t1)/(t2-t1))
-    //                  *[(v2/v1)^(1/(F*(t2-t1)))]^k
-    //
-    // Thus, this can be written as
-    //
-    //   v((c+k)/F) = V*m^k
-    //
-    // where
-    //   V = v1*(v2/v1)^((c/F-t1)/(t2-t1))
-    //   m = (v2/v1)^(1/(F*(t2-t1)))
-
-    // Compute the per-sample multiplier.
-    double multiplier = fdlibm::pow(value2 / value1, 1.0 / num_sample_frames);
-    // Set the starting value of the exponential ramp.  Do not attempt
-    // to optimize pow to powf.  See crbug.com/771306.
-    value = value1 *
-            fdlibm::pow(value2 / static_cast<double>(value1),
-                        (current_frame / sample_rate - time1) / delta_time);
-    for (double accumulator = value; write_index < fill_to_frame;
-         ++write_index) {
-      value = accumulator;
-      values[write_index] = value;
-      accumulator *= multiplier;
-      ++current_frame;
-    }
-
-    // Due to roundoff it's possible that value exceeds value2.  Clip value
-    // to value2 if we are within 1/2 frame of time2.
-    if (current_frame > time2 * sample_rate - 0.5) {
-      value = value2;
-    }
-  }
-
-  return std::make_tuple(current_frame, value, write_index);
-}
-
-std::tuple<size_t, float, unsigned> AudioParamTimeline::ProcessSetTarget(
-    const AutomationState& current_state,
-    float* values,
-    size_t current_frame,
-    float value,
-    unsigned write_index) {
-#if defined(ARCH_CPU_X86_FAMILY)
-  auto number_of_values = current_state.number_of_values;
-#endif
-  auto fill_to_frame = current_state.fill_to_frame;
-  auto time1 = current_state.time1;
-  auto value1 = current_state.value1;
-  auto sample_rate = current_state.sample_rate;
-  auto control_rate = current_state.control_rate;
-  auto fill_to_end_frame = current_state.fill_to_end_frame;
-  auto* event = current_state.event.get();
-
-  // Exponential approach to target value with given time constant.
-  //
-  //   v(t) = v2 + (v1 - v2)*exp(-(t-t1/tau))
-  //
-  float target = value1;
-  float time_constant = event->TimeConstant();
-  float discrete_time_constant =
-      static_cast<float>(audio_utilities::DiscreteTimeConstantForSampleRate(
-          time_constant, control_rate));
-
-  // Set the starting value correctly.  This is only needed when the
-  // current time is "equal" to the start time of this event.  This is
-  // to get the sampling correct if the start time of this automation
-  // isn't on a frame boundary.  Otherwise, we can just continue from
-  // where we left off from the previous rendering quantum.
-  {
-    double ramp_start_frame = time1 * sample_rate;
-    // Condition is c - 1 < r <= c where c = currentFrame and r =
-    // rampStartFrame.  Compute it this way because currentFrame is
-    // unsigned and could be 0.
-    if (ramp_start_frame <= current_frame &&
-        current_frame < ramp_start_frame + 1) {
-      value = target + (value - target) *
-                           fdlibm::exp(-(current_frame / sample_rate - time1) /
-                                       time_constant);
-    } else {
-      // Otherwise, need to compute a new value because `value` is the
-      // last computed value of SetTarget.  Time has progressed by one
-      // frame, so we need to update the value for the new frame.
-      value += (target - value) * discrete_time_constant;
-    }
-  }
-
-  // If the value is close enough to the target, just fill in the data
-  // with the target value.
-  if (HasSetTargetConverged(value, target, current_frame / sample_rate, time1,
-                            time_constant)) {
-    current_frame += fill_to_frame - write_index;
-    for (; write_index < fill_to_frame; ++write_index) {
-      values[write_index] = target;
-    }
-  } else {
-#if defined(ARCH_CPU_X86_FAMILY)
-    if (fill_to_frame > write_index) {
-      // Resolve recursion by expanding constants to achieve a 4-step
-      // loop unrolling.
-      //
-      // v1 = v0 + (t - v0) * c
-      // v2 = v1 + (t - v1) * c
-      // v2 = v0 + (t - v0) * c + (t - (v0 + (t - v0) * c)) * c
-      // v2 = v0 + (t - v0) * c + (t - v0) * c - (t - v0) * c * c
-      // v2 = v0 + (t - v0) * c * (2 - c)
-      // Thus c0 = c, c1 = c*(2-c). The same logic applies to c2 and c3.
-      const float c0 = discrete_time_constant;
-      const float c1 = c0 * (2 - c0);
-      const float c2 = c0 * ((c0 - 3) * c0 + 3);
-      const float c3 = c0 * (c0 * ((4 - c0) * c0 - 6) + 4);
-
-      float delta;
-      __m128 v_c = _mm_set_ps(c2, c1, c0, 0);
-      __m128 v_delta, v_value, v_result;
-
-      // Process 4 loop steps.
-      unsigned fill_to_frame_trunc =
-          write_index + ((fill_to_frame - write_index) / 4) * 4;
-      DCHECK_LE(fill_to_frame_trunc, number_of_values);
-
-      for (; write_index < fill_to_frame_trunc; write_index += 4) {
-        delta = target - value;
-        v_delta = _mm_set_ps1(delta);
-        v_value = _mm_set_ps1(value);
-
-        v_result = _mm_add_ps(v_value, _mm_mul_ps(v_delta, v_c));
-        _mm_storeu_ps(values + write_index, v_result);
-
-        // Update value for next iteration.
-        value += delta * c3;
-      }
-    }
-#endif
-    // Serially process remaining values
-    for (; write_index < fill_to_frame; ++write_index) {
-      values[write_index] = value;
-      value += (target - value) * discrete_time_constant;
-    }
-    // The previous loops may have updated `value` one extra time.
-    // Reset it to the last computed value.
-    if (write_index >= 1) {
-      value = values[write_index - 1];
-    }
-    current_frame = fill_to_end_frame;
-  }
-
-  return std::make_tuple(current_frame, value, write_index);
-}
-
-std::tuple<size_t, float, unsigned> AudioParamTimeline::ProcessSetValueCurve(
-    const AutomationState& current_state,
-    float* values,
-    size_t current_frame,
-    float value,
-    unsigned write_index) {
-  auto number_of_values = current_state.number_of_values;
-  auto fill_to_frame = current_state.fill_to_frame;
-  auto time1 = current_state.time1;
-  auto sample_rate = current_state.sample_rate;
-  auto start_frame = current_state.start_frame;
-  auto end_frame = current_state.end_frame;
-  auto fill_to_end_frame = current_state.fill_to_end_frame;
-  auto* event = current_state.event.get();
-
-  const Vector<float> curve = event->Curve();
-  const float* curve_data = curve.data();
-  unsigned number_of_curve_points = curve.size();
-
-  float curve_end_value = event->CurveEndValue();
-
-  // Curve events have duration, so don't just use next event time.
-  double duration = event->Duration();
-  // How much to step the curve index for each frame.  This is basically
-  // the term (N - 1)/Td in the specification.
-  double curve_points_per_frame = event->CurvePointsPerSecond() / sample_rate;
-
-  if (!number_of_curve_points || duration <= 0 || sample_rate <= 0) {
-    // Error condition - simply propagate previous value.
-    current_frame = fill_to_end_frame;
-    for (; write_index < fill_to_frame; ++write_index) {
-      values[write_index] = value;
-    }
-    return std::make_tuple(current_frame, value, write_index);
-  }
-
-  // Save old values and recalculate information based on the curve's
-  // duration instead of the next event time.
-  size_t next_event_fill_to_frame = fill_to_frame;
-
-  // fillToEndFrame = min(endFrame,
-  //                      ceil(sampleRate * (time1 + duration))),
-  // but compute this carefully in case sampleRate*(time1 + duration) is
-  // huge.  fillToEndFrame is an exclusive upper bound of the last frame
-  // to be computed, so ceil is used.
-  {
-    double curve_end_frame = ceil(sample_rate * (time1 + duration));
-    if (end_frame > curve_end_frame) {
-      fill_to_end_frame = static_cast<size_t>(curve_end_frame);
-    } else {
-      fill_to_end_frame = end_frame;
-    }
-  }
-
-  // `fill_to_frame` can be less than `start_frame` when the end of the
-  // setValueCurve automation has been reached, but the next automation
-  // has not yet started. In this case, `fill_to_frame` is clipped to
-  // `time1`+`duration` above, but `start_frame` will keep increasing
-  // (because the current time is increasing).
-  fill_to_frame = (fill_to_end_frame < start_frame)
-                      ? 0
-                      : static_cast<unsigned>(fill_to_end_frame - start_frame);
-  fill_to_frame = std::min(fill_to_frame, number_of_values);
-
-  // Index into the curve data using a floating-point value.
-  // We're scaling the number of curve points by the duration (see
-  // curvePointsPerFrame).
-  double curve_virtual_index = 0;
-  if (time1 < current_frame / sample_rate) {
-    // Index somewhere in the middle of the curve data.
-    // Don't use timeToSampleFrame() since we want the exact
-    // floating-point frame.
-    double frame_offset = current_frame - time1 * sample_rate;
-    curve_virtual_index = curve_points_per_frame * frame_offset;
-  }
-
-  // Set the default value in case fillToFrame is 0.
-  value = curve_end_value;
-
-  // Render the stretched curve data using linear interpolation.
-  // Oversampled curve data can be provided if sharp discontinuities are
-  // desired.
-  unsigned k = 0;
-#if defined(ARCH_CPU_X86_FAMILY)
-  if (fill_to_frame > write_index) {
-    const __m128 v_curve_virtual_index = _mm_set_ps1(curve_virtual_index);
-    const __m128 v_curve_points_per_frame = _mm_set_ps1(curve_points_per_frame);
-    const __m128 v_number_of_curve_points_m1 =
-        _mm_set_ps1(number_of_curve_points - 1);
-    const __m128 v_n1 = _mm_set_ps1(1.0f);
-    const __m128 v_n4 = _mm_set_ps1(4.0f);
-
-    __m128 v_k = _mm_set_ps(3, 2, 1, 0);
-    int a_curve_index0[4];
-    int a_curve_index1[4];
-
-    // Truncate loop steps to multiple of 4
-    unsigned truncated_steps = ((fill_to_frame - write_index) / 4) * 4;
-    unsigned fill_to_frame_trunc = write_index + truncated_steps;
-    DCHECK_LE(fill_to_frame_trunc, number_of_values);
-
-    for (; write_index < fill_to_frame_trunc; write_index += 4) {
-      // Compute current index this way to minimize round-off that would
-      // have occurred by incrementing the index by curvePointsPerFrame.
-      __m128 v_current_virtual_index = _mm_add_ps(
-          v_curve_virtual_index, _mm_mul_ps(v_k, v_curve_points_per_frame));
-      v_k = _mm_add_ps(v_k, v_n4);
-
-      // Clamp index to the last element of the array.
-      __m128i v_curve_index0 = _mm_cvttps_epi32(
-          _mm_min_ps(v_current_virtual_index, v_number_of_curve_points_m1));
-      __m128i v_curve_index1 =
-          _mm_cvttps_epi32(_mm_min_ps(_mm_add_ps(v_current_virtual_index, v_n1),
-                                      v_number_of_curve_points_m1));
-
-      // Linearly interpolate between the two nearest curve points.
-      // `delta` is clamped to 1 because `current_virtual_index` can exceed
-      // `curve_index0` by more than one.  This can happen when we reached
-      // the end of the curve but still need values to fill out the
-      // current rendering quantum.
-      _mm_storeu_si128(reinterpret_cast<__m128i*>(a_curve_index0),
-                       v_curve_index0);
-      _mm_storeu_si128(reinterpret_cast<__m128i*>(a_curve_index1),
-                       v_curve_index1);
-      __m128 v_c0 = _mm_set_ps(
-          curve_data[a_curve_index0[3]], curve_data[a_curve_index0[2]],
-          curve_data[a_curve_index0[1]], curve_data[a_curve_index0[0]]);
-      __m128 v_c1 = _mm_set_ps(
-          curve_data[a_curve_index1[3]], curve_data[a_curve_index1[2]],
-          curve_data[a_curve_index1[1]], curve_data[a_curve_index1[0]]);
-      __m128 v_delta = _mm_min_ps(
-          _mm_sub_ps(v_current_virtual_index, _mm_cvtepi32_ps(v_curve_index0)),
-          v_n1);
-
-      __m128 v_value =
-          _mm_add_ps(v_c0, _mm_mul_ps(_mm_sub_ps(v_c1, v_c0), v_delta));
-
-      _mm_storeu_ps(values + write_index, v_value);
-    }
-    // Pass along k to the serial loop.
-    k = truncated_steps;
-  }
-  if (write_index >= 1) {
-    value = values[write_index - 1];
-  }
-#endif
-  for (; write_index < fill_to_frame; ++write_index, ++k) {
-    // Compute current index this way to minimize round-off that would
-    // have occurred by incrementing the index by curvePointsPerFrame.
-    double current_virtual_index =
-        curve_virtual_index + k * curve_points_per_frame;
-    unsigned curve_index0;
-
-    // Clamp index to the last element of the array.
-    if (current_virtual_index < number_of_curve_points) {
-      curve_index0 = static_cast<unsigned>(current_virtual_index);
-    } else {
-      curve_index0 = number_of_curve_points - 1;
-    }
-
-    unsigned curve_index1 =
-        std::min(curve_index0 + 1, number_of_curve_points - 1);
-
-    // Linearly interpolate between the two nearest curve points.  `delta` is
-    // clamped to 1 because `current_virtual_index` can exceed `curve_index0` by
-    // more than one.  This can happen when we reached the end of the curve but
-    // still need values to fill out the current rendering quantum.
-    DCHECK_LT(curve_index0, number_of_curve_points);
-    DCHECK_LT(curve_index1, number_of_curve_points);
-    float c0 = curve_data[curve_index0];
-    float c1 = curve_data[curve_index1];
-    double delta = std::min(current_virtual_index - curve_index0, 1.0);
-
-    value = c0 + (c1 - c0) * delta;
-
-    values[write_index] = value;
-  }
-
-  // If there's any time left after the duration of this event and the
-  // start of the next, then just propagate the last value of the
-  // `curve_data`. Don't modify `value` unless there is time left.
-  if (write_index < next_event_fill_to_frame) {
-    value = curve_end_value;
-    for (; write_index < next_event_fill_to_frame; ++write_index) {
-      values[write_index] = value;
-    }
-  }
-
-  // Re-adjust current time
-  current_frame += next_event_fill_to_frame;
-
-  return std::make_tuple(current_frame, value, write_index);
-}
-
-std::tuple<size_t, float, unsigned> AudioParamTimeline::ProcessCancelValues(
-    const AutomationState& current_state,
-    float* values,
-    size_t current_frame,
-    float value,
-    unsigned write_index) {
-  auto fill_to_frame = current_state.fill_to_frame;
-  auto time1 = current_state.time1;
-  auto sample_rate = current_state.sample_rate;
-  auto control_rate = current_state.control_rate;
-  auto fill_to_end_frame = current_state.fill_to_end_frame;
-  auto* event = current_state.event.get();
-  auto event_index = current_state.event_index;
-
-  // If the previous event was a SetTarget or ExponentialRamp
-  // event, the current value is one sample behind.  Update
-  // the sample value by one sample, but only at the start of
-  // this CancelValues event.
-  if (event->HasDefaultCancelledValue()) {
-    value = event->Value();
-  } else {
-    double cancel_frame = time1 * sample_rate;
-    if (event_index >= 1 && cancel_frame <= current_frame &&
-        current_frame < cancel_frame + 1) {
-      ParamEvent::Type last_event_type = events_[event_index - 1]->GetType();
-      if (last_event_type == ParamEvent::Type::kSetTarget) {
-        float target = events_[event_index - 1]->Value();
-        float time_constant = events_[event_index - 1]->TimeConstant();
-        float discrete_time_constant = static_cast<float>(
-            audio_utilities::DiscreteTimeConstantForSampleRate(time_constant,
-                                                               control_rate));
-        value += (target - value) * discrete_time_constant;
-      }
-    }
-  }
-
-  // Simply stay at the current value.
-  for (; write_index < fill_to_frame; ++write_index) {
-    values[write_index] = value;
-  }
-
-  current_frame = fill_to_end_frame;
-
-  return std::make_tuple(current_frame, value, write_index);
-}
-
-uint32_t AudioParamTimeline::FillWithDefault(float* values,
-                                             float default_value,
-                                             uint32_t end_frame,
-                                             uint32_t write_index) {
-  uint32_t index = write_index;
-
-  for (; index < end_frame; ++index) {
-    values[index] = default_value;
-  }
-
-  return index;
-}
-
-void AudioParamTimeline::RemoveCancelledEvents(
-    wtf_size_t first_event_to_remove) {
-  // For all the events that are being removed, also remove that event
-  // from `new_events_`.
-  if (new_events_.size() > 0) {
-    for (wtf_size_t k = first_event_to_remove; k < events_.size(); ++k) {
-      new_events_.erase(events_[k].get());
-    }
-  }
-
-  // Now we can remove the cancelled events from the list.
-  events_.EraseAt(first_event_to_remove,
-                  events_.size() - first_event_to_remove);
-}
-
-void AudioParamTimeline::RemoveOldEvents(wtf_size_t event_count) {
-  wtf_size_t n_events = events_.size();
-  DCHECK(event_count <= n_events);
-
-  // Always leave at least one event in the event list!
-  if (n_events > 1) {
-    events_.EraseAt(0, std::min(event_count, n_events - 1));
-  }
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param_timeline.h b/third_party/blink/renderer/modules/webaudio/audio_param_timeline.h
deleted file mode 100644
index 39e6ffb1..0000000
--- a/third_party/blink/renderer/modules/webaudio/audio_param_timeline.h
+++ /dev/null
@@ -1,490 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1.  Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- * 2.  Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in the
- *     documentation and/or other materials provided with the distribution.
- * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- *     its contributors may be used to endorse or promote products derived
- *     from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_AUDIO_PARAM_TIMELINE_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_AUDIO_PARAM_TIMELINE_H_
-
-#include <tuple>
-
-#include "base/memory/raw_ptr.h"
-#include "base/synchronization/lock.h"
-#include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
-#include "third_party/blink/renderer/modules/webaudio/audio_destination_node.h"
-#include "third_party/blink/renderer/modules/webaudio/base_audio_context.h"
-#include "third_party/blink/renderer/platform/wtf/forward.h"
-#include "third_party/blink/renderer/platform/wtf/threading.h"
-#include "third_party/blink/renderer/platform/wtf/vector.h"
-
-namespace blink {
-
-class AudioParamTimeline {
-  DISALLOW_NEW();
-
- public:
-  AudioParamTimeline() = default;
-
-  void SetValueAtTime(float value, double time, ExceptionState&);
-  void LinearRampToValueAtTime(float value,
-                               double time,
-                               float initial_value,
-                               double call_time,
-                               ExceptionState&);
-  void ExponentialRampToValueAtTime(float value,
-                                    double time,
-                                    float initial_value,
-                                    double call_time,
-                                    ExceptionState&);
-  void SetTargetAtTime(float target,
-                       double time,
-                       double time_constant,
-                       ExceptionState&);
-  void SetValueCurveAtTime(const Vector<float>& curve,
-                           double time,
-                           double duration,
-                           ExceptionState&);
-  void CancelScheduledValues(double start_time, ExceptionState&);
-  void CancelAndHoldAtTime(double cancel_time, ExceptionState&);
-
-  // Compute the value from this AudioParamHandler at the current context frame.
-  // Returns two values:
-  //
-  //   bool has_value - to indicate if the value could be computed from the
-  //                    timeline
-  //   float value    - the timeline value if `has_value` is true; otherwise
-  //                    `default_value` is returned.
-  std::tuple<bool, float> ValueForContextTime(AudioDestinationHandler&,
-                                              float default_value,
-                                              float min_value,
-                                              float max_value,
-                                              unsigned render_quantum_frames);
-
-  // Given the time range in frames, calculates parameter values into the values
-  // buffer and returns the last parameter value calculated for "values" or the
-  // defaultValue if none were calculated.  controlRate is the rate (number per
-  // second) at which parameter values will be calculated.  It should equal
-  // sampleRate for sample-accurate parameter changes, and otherwise will
-  // usually match the render quantum size such that the parameter value changes
-  // once per render quantum.
-  float ValuesForFrameRange(size_t start_frame,
-                            size_t end_frame,
-                            float default_value,
-                            float* values,
-                            unsigned number_of_values,
-                            double sample_rate,
-                            double control_rate,
-                            float min_value,
-                            float max_value,
-                            unsigned render_quantum_frames);
-
-  // Returns true if the AudioParam timeline needs to run in this
-  // rendering quantum.  This means some automation is already running
-  // or is scheduled to run in the current rendering quantuym.
-  bool HasValues(size_t current_frame,
-                 double sample_rate,
-                 unsigned render_quantum_frames) const;
-
- private:
-  class ParamEvent {
-   public:
-    enum class Type {
-      kSetValue,
-      kLinearRampToValue,
-      kExponentialRampToValue,
-      kSetTarget,
-      kSetValueCurve,
-      // For cancelValuesAndHold
-      kCancelValues,
-      // Special marker for the end of a `kSetValueCurve` event.
-      kSetValueCurveEnd,
-      kLastType
-    };
-
-    static std::unique_ptr<ParamEvent> CreateLinearRampEvent(
-        float value,
-        double time,
-        float initial_value,
-        double call_time);
-    static std::unique_ptr<ParamEvent> CreateExponentialRampEvent(
-        float value,
-        double time,
-        float initial_value,
-        double call_time);
-    static std::unique_ptr<ParamEvent> CreateSetValueEvent(float value,
-                                                           double time);
-    static std::unique_ptr<ParamEvent>
-    CreateSetTargetEvent(float value, double time, double time_constant);
-    static std::unique_ptr<ParamEvent> CreateSetValueCurveEvent(
-        const Vector<float>& curve,
-        double time,
-        double duration);
-    static std::unique_ptr<ParamEvent> CreateSetValueCurveEndEvent(float value,
-                                                                   double time);
-    static std::unique_ptr<ParamEvent> CreateCancelValuesEvent(
-        double time,
-        std::unique_ptr<ParamEvent> saved_event);
-    // Needed for creating a saved event where we want to supply all
-    // the possible parameters because we're mostly copying an
-    // existing event.
-    static std::unique_ptr<ParamEvent> CreateGeneralEvent(
-        Type,
-        float value,
-        double time,
-        float initial_value,
-        double call_time,
-        double time_constant,
-        double duration,
-        Vector<float>& curve,
-        double curve_points_per_second,
-        float curve_end_value,
-        std::unique_ptr<ParamEvent> saved_event);
-
-    static bool EventPrecedes(const std::unique_ptr<ParamEvent>& a,
-                              const std::unique_ptr<ParamEvent>& b) {
-      return a->Time() < b->Time();
-    }
-
-    Type GetType() const { return type_; }
-    float Value() const { return value_; }
-    double Time() const { return time_; }
-    void SetTime(double new_time) { time_ = new_time; }
-    double TimeConstant() const { return time_constant_; }
-    double Duration() const { return duration_; }
-    const Vector<float>& Curve() const { return curve_; }
-    Vector<float>& Curve() { return curve_; }
-    float InitialValue() const { return initial_value_; }
-    double CallTime() const { return call_time_; }
-
-    double CurvePointsPerSecond() const { return curve_points_per_second_; }
-    float CurveEndValue() const { return curve_end_value_; }
-
-    // For CancelValues events. Not valid for any other event.
-    ParamEvent* SavedEvent() const;
-    bool HasDefaultCancelledValue() const;
-    void SetCancelledValue(float);
-
-   private:
-    // General event
-    ParamEvent(Type type,
-               float value,
-               double time,
-               float initial_value,
-               double call_time,
-               double time_constant,
-               double duration,
-               Vector<float>& curve,
-               double curve_points_per_second,
-               float curve_end_value,
-               std::unique_ptr<ParamEvent> saved_event);
-
-    // Create simplest event needing just a value and time, like
-    // setValueAtTime.
-    ParamEvent(Type, float value, double time);
-
-    // Create a linear or exponential ramp that requires an initial
-    // value and time in case there is no actual event that precedes
-    // this event.
-    ParamEvent(Type,
-               float value,
-               double time,
-               float initial_value,
-               double call_time);
-
-    // Create an event needing a time constant (setTargetAtTime)
-    ParamEvent(Type, float value, double time, double time_constant);
-
-    // Create a setValueCurve event
-    ParamEvent(Type,
-               double time,
-               double duration,
-               const Vector<float>& curve,
-               double curve_points_per_second,
-               float curve_end_value);
-
-    // Create CancelValues event
-    ParamEvent(Type, double time, std::unique_ptr<ParamEvent> saved_event);
-
-    Type type_;
-
-    // The value for the event.  The interpretation of this depends on
-    // the event type. Not used for SetValueCurve. For CancelValues,
-    // it is the end value to use when cancelling a LinearRampToValue
-    // or ExponentialRampToValue event.
-    float value_;
-
-    // The time for the event. The interpretation of this depends on
-    // the event type.
-    double time_;
-
-    // Initial value and time to use for linear and exponential ramps that don't
-    // have a preceding event.
-    float initial_value_;
-    double call_time_;
-
-    // Only used for SetTarget events
-    double time_constant_;
-
-    // The following items are only used for SetValueCurve events.
-    //
-    // The duration of the curve.
-    double duration_;
-    // The array of curve points.
-    Vector<float> curve_;
-    // The number of curve points per second. it is used to compute
-    // the curve index step when running the automation.
-    double curve_points_per_second_;
-    // The default value to use at the end of the curve.  Normally
-    // it's the last entry in m_curve, but cancelling a SetValueCurve
-    // will set this to a new value.
-    float curve_end_value_;
-
-    // For CancelValues. If CancelValues is in the middle of an event, this
-    // holds the event that is being cancelled, so that processing can
-    // continue as if the event still existed up until we reach the actual
-    // scheduled cancel time.
-    std::unique_ptr<ParamEvent> saved_event_;
-
-    // True if a default value has been assigned to the CancelValues event.
-    bool has_default_cancelled_value_;
-  };
-
-  // State of the timeline for the current event.
-  struct AutomationState {
-    // Parameters for the current automation request.  Number of
-    // values to be computed for the automation request
-    const unsigned number_of_values;
-    // Start and end frames for this automation request
-    const size_t start_frame;
-    const size_t end_frame;
-
-    // Sample rate and control rate for this request
-    const double sample_rate;
-    const double control_rate;
-
-    // Parameters needed for processing the current event.
-    const unsigned fill_to_frame;
-    const size_t fill_to_end_frame;
-
-    // Value and time for the current event
-    const float value1;
-    const double time1;
-
-    // Value and time for the next event, if any.
-    const float value2;
-    const double time2;
-
-    // The current event, and its index in the event vector.
-    raw_ptr<const ParamEvent> event;
-    const int event_index;
-  };
-
-  void InsertEvent(std::unique_ptr<ParamEvent>, ExceptionState&)
-      EXCLUSIVE_LOCKS_REQUIRED(events_lock_);
-  float ValuesForFrameRangeImpl(size_t start_frame,
-                                size_t end_frame,
-                                float default_value,
-                                float* values,
-                                unsigned number_of_values,
-                                double sample_rate,
-                                double control_rate,
-                                unsigned render_quantum_frames)
-      EXCLUSIVE_LOCKS_REQUIRED(events_lock_);
-
-  // Produce a nice string describing the event in human-readable form.
-  String EventToString(const ParamEvent&) const;
-
-  // Automation functions that compute the value of the specified
-  // automation at the specified time.
-  float LinearRampAtTime(double t,
-                         float value1,
-                         double time1,
-                         float value2,
-                         double time2);
-  float ExponentialRampAtTime(double t,
-                              float value1,
-                              double time1,
-                              float value2,
-                              double time2);
-  float TargetValueAtTime(double t,
-                          float value1,
-                          double time1,
-                          float value2,
-                          float time_constant);
-  float ValueCurveAtTime(double t,
-                         double time1,
-                         double duration,
-                         const float* curve_data,
-                         unsigned curve_length);
-
-  // Handles the special case where the first event in the timeline
-  // starts after `start_frame`.  These initial values are filled using
-  // `default_value`.  The updated `current_frame` and `write_index` is
-  // returned.
-  std::tuple<size_t, unsigned> HandleFirstEvent(float* values,
-                                                float default_value,
-                                                unsigned number_of_values,
-                                                size_t start_frame,
-                                                size_t end_frame,
-                                                double sample_rate,
-                                                size_t current_frame,
-                                                unsigned write_index)
-      EXCLUSIVE_LOCKS_REQUIRED(events_lock_);
-
-  // Return true if `current_event` starts after `current_frame`, but
-  // also takes into account the `next_event` if any.
-  bool IsEventCurrent(const ParamEvent* current_event,
-                      const ParamEvent* next_event,
-                      size_t current_frame,
-                      double sample_rate) const;
-
-  // Clamp times to current time, if needed for any new events.  Note,
-  // this method can mutate `events_`, so do call this only in safe
-  // places.
-  void ClampNewEventsToCurrentTime(double current_time)
-      EXCLUSIVE_LOCKS_REQUIRED(events_lock_);
-
-  // Handle the case where the last event in the timeline is in the
-  // past.  Returns false if any event is not in the past. Otherwise,
-  // return true and also fill in `values` with `default_value`.
-  // `default_value` may be updated with a new value.
-  bool HandleAllEventsInThePast(double current_time,
-                                double sample_rate,
-                                float& default_value,
-                                unsigned number_of_values,
-                                float* values,
-                                unsigned render_quantum_frames)
-      EXCLUSIVE_LOCKS_REQUIRED(events_lock_);
-
-  // Handle processing of CancelValue event. If cancellation happens, value2,
-  // time2, and nextEventType will be updated with the new value due to
-  // cancellation.  Note that `next_event` or its member can be null.
-  std::tuple<float, double, ParamEvent::Type> HandleCancelValues(
-      const ParamEvent* current_event,
-      ParamEvent* next_event,
-      float value2,
-      double time2) EXCLUSIVE_LOCKS_REQUIRED(events_lock_);
-
-  // Process a SetTarget event and the next event is a
-  // LinearRampToValue or ExponentialRampToValue event.  This requires
-  // special handling because the ramp should start at whatever value
-  // the SetTarget event has reached at this time, instead of using
-  // the value of the SetTarget event.
-  void ProcessSetTargetFollowedByRamp(int event_index,
-                                      ParamEvent*& current_event,
-                                      ParamEvent::Type next_event_type,
-                                      size_t current_frame,
-                                      double sample_rate,
-                                      double control_rate,
-                                      float& value)
-      EXCLUSIVE_LOCKS_REQUIRED(events_lock_);
-
-  // Handle processing of LinearRampEvent, writing the appropriate
-  // values to `values`.  Returns the updated `current_frame`, last
-  // computed `value`, and the updated `write_index`.
-  std::tuple<size_t, float, unsigned> ProcessLinearRamp(
-      const AutomationState& current_state,
-      float* values,
-      size_t current_frame,
-      float value,
-      unsigned write_index);
-
-  // Handle processing of ExponentialRampEvent, writing the appropriate
-  // values to `values`.  Returns the updated `current_frame`, last
-  // computed `value`, and the updated `write_index`.
-  std::tuple<size_t, float, unsigned> ProcessExponentialRamp(
-      const AutomationState& current_state,
-      float* values,
-      size_t current_frame,
-      float value,
-      unsigned write_index);
-
-  // Handle processing of SetTargetEvent, writing the appropriate
-  // values to `values`.  Returns the updated `current_frame`, last
-  // computed `value`, and the updated `write_index`.
-  std::tuple<size_t, float, unsigned> ProcessSetTarget(
-      const AutomationState& current_state,
-      float* values,
-      size_t current_frame,
-      float value,
-      unsigned write_index);
-
-  // Handle processing of SetValueCurveEvent, writing the appropriate
-  // values to `values`.  Returns the updated `current_frame`, last
-  // computed `value`, and the updated `write_index`.
-  std::tuple<size_t, float, unsigned> ProcessSetValueCurve(
-      const AutomationState& current_state,
-      float* values,
-      size_t current_frame,
-      float value,
-      unsigned write_index);
-
-  // Handle processing of CancelValuesEvent, writing the appropriate
-  // values to `values`.  Returns the updated `current_frame`, last
-  // computed `value`, and the updated `write_index`.
-  std::tuple<size_t, float, unsigned> ProcessCancelValues(
-      const AutomationState& current_state,
-      float* values,
-      size_t current_frame,
-      float value,
-      unsigned write_index) EXCLUSIVE_LOCKS_REQUIRED(events_lock_);
-
-  // Fill the output vector `values` with the value `default_value`,
-  // starting at `write_index` and continuing up to `end_frame`
-  // (exclusive).  `write_index` is updated with the new index.
-  uint32_t FillWithDefault(float* values,
-                           float default_value,
-                           uint32_t end_frame,
-                           uint32_t write_index);
-
-  // When cancelling events, remove the items from `events_` starting
-  // at the given index.  Update `new_events_` too.
-  void RemoveCancelledEvents(wtf_size_t first_event_to_remove)
-      EXCLUSIVE_LOCKS_REQUIRED(events_lock_);
-
-  // Remove old events, but always leave at least one event in the timeline.
-  // This is needed in case a new event is added (like linearRamp) that would
-  // use a previous event to compute the automation.
-  void RemoveOldEvents(wtf_size_t n_events)
-      EXCLUSIVE_LOCKS_REQUIRED(events_lock_);
-
-  // Vector of all automation events for the AudioParam.
-  Vector<std::unique_ptr<ParamEvent>> events_ GUARDED_BY(events_lock_);
-
-  // Vector of raw pointers to the actual ParamEvent that was
-  // inserted.  As new events are added, `new_events_` is updated with
-  // the new event.  When the timline is processed, these events are
-  // clamped to current time by `ClampNewEventsToCurrentTime`. Access
-  // must be locked via `events_lock_`.  Must be maintained together
-  // with `events_`.
-  HashSet<ParamEvent*> new_events_ GUARDED_BY(events_lock_);
-
-  mutable base::Lock events_lock_;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_AUDIO_PARAM_TIMELINE_H_
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.cc b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
index e58f9c4..0567eee 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
@@ -1011,7 +1011,8 @@
   DCHECK(sii);
   gpu::SharedImageInterface::SwapChainSharedImages shared_images =
       sii->CreateSwapChain(format, size, color_space, kTopLeft_GrSurfaceOrigin,
-                           kPremul_SkAlphaType, usage);
+                           kPremul_SkAlphaType, usage,
+                           "CanvasResourceSwapChain");
   CHECK(shared_images.back_buffer);
   CHECK(shared_images.front_buffer);
   back_buffer_shared_image_ = std::move(shared_images.back_buffer);
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
index ff68bde..dfdba91 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
@@ -6,7 +6,6 @@
 
 #include <utility>
 
-#include "base/debug/stack_trace.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/trace_event/trace_event.h"
 #include "components/viz/common/quads/compositor_frame.h"
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
index 7617519..a7d02c7 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -1956,7 +1956,8 @@
     usage = usage | gpu::SHARED_IMAGE_USAGE_CONCURRENT_READ_WRITE;
     gpu::SharedImageInterface::SwapChainSharedImages shared_images =
         sii->CreateSwapChain(color_buffer_format_, size, color_space_, origin,
-                             back_buffer_alpha_type, usage);
+                             back_buffer_alpha_type, usage,
+                             "WebGLDrawingBuffer");
     back_buffer_shared_image = std::move(shared_images.back_buffer);
     front_buffer_shared_image = std::move(shared_images.front_buffer);
   } else {
diff --git a/third_party/blink/renderer/platform/graphics/parkable_image.cc b/third_party/blink/renderer/platform/graphics/parkable_image.cc
index be4fea3..7e4dad2 100644
--- a/third_party/blink/renderer/platform/graphics/parkable_image.cc
+++ b/third_party/blink/renderer/platform/graphics/parkable_image.cc
@@ -4,7 +4,6 @@
 
 #include "third_party/blink/renderer/platform/graphics/parkable_image.h"
 
-#include "base/debug/stack_trace.h"
 #include "base/feature_list.h"
 #include "base/memory/asan_interface.h"
 #include "base/memory/ref_counted.h"
diff --git a/third_party/blink/renderer/platform/graphics/parkable_image.h b/third_party/blink/renderer/platform/graphics/parkable_image.h
index b97c7b52..068567ca 100644
--- a/third_party/blink/renderer/platform/graphics/parkable_image.h
+++ b/third_party/blink/renderer/platform/graphics/parkable_image.h
@@ -6,7 +6,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PARKABLE_IMAGE_H_
 
 #include "base/containers/span.h"
-#include "base/debug/stack_trace.h"
 #include "base/feature_list.h"
 #include "base/synchronization/lock.h"
 #include "base/task/single_thread_task_runner.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/webui_bundled_cached_metadata_handler.cc b/third_party/blink/renderer/platform/loader/fetch/webui_bundled_cached_metadata_handler.cc
index 80935214..7fb7398 100644
--- a/third_party/blink/renderer/platform/loader/fetch/webui_bundled_cached_metadata_handler.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/webui_bundled_cached_metadata_handler.cc
@@ -4,7 +4,6 @@
 
 #include "third_party/blink/renderer/platform/loader/fetch/webui_bundled_cached_metadata_handler.h"
 
-#include "base/debug/stack_trace.h"
 #include "base/metrics/histogram_functions.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_loading_log.h"
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index caedf1be..10cde69e 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -483,7 +483,7 @@
     {
       // https://drafts.csswg.org/web-animations-2/#triggers
       name: "AnimationTrigger",
-      status: "test",
+      status: "experimental",
     },
     {
       name: "AnimationWorklet",
@@ -5045,23 +5045,12 @@
       origin_trial_allows_third_party: true,
       base_feature_status: "enabled",
       copied_from_base_feature_if: "overridden",
-      implied_by: ["TranslationAPIV1"],
     },
     {
       name: "TranslationAPIForWorkers",
       public: true,
     },
     {
-      name: "TranslationAPIV1",
-      status: {
-        "Win": "experimental",
-        "Mac": "experimental",
-        "Linux": "experimental",
-        "default": "",
-      },
-      copied_from_base_feature_if: "overridden",
-    },
-    {
       // Ensures symbols are treated as word boundaries during traversal.
       // See https://crbug.com/40252642
       name: "TreatSymbolsAsWordBoundary",
diff --git a/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.cc b/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.cc
index 76514b3..b18a973 100644
--- a/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.cc
@@ -8,7 +8,6 @@
 #include <optional>
 
 #include "base/check_op.h"
-#include "base/debug/stack_trace.h"
 #include "base/functional/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_pump.h"
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
index 35c1cc8..113ce5c 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
@@ -10,7 +10,6 @@
 
 #include "base/check_op.h"
 #include "base/containers/contains.h"
-#include "base/debug/stack_trace.h"
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/memory/post_delayed_memory_reduction_task.h"
diff --git a/third_party/blink/web_tests/custom-elements/spec/custom-elements-registry/when_defined-expected.txt b/third_party/blink/web_tests/custom-elements/spec/custom-elements-registry/when_defined-expected.txt
deleted file mode 100644
index 97d9c82..0000000
--- a/third_party/blink/web_tests/custom-elements/spec/custom-elements-registry/when_defined-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = 1 duplicate test name: "whenDefined() called with invalid name bad-; should throw "SyntaxError"DOMException"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/custom-elements/spec/custom-elements-registry/when_defined.html b/third_party/blink/web_tests/custom-elements/spec/custom-elements-registry/when_defined.html
index e674311..46719d8d 100644
--- a/third_party/blink/web_tests/custom-elements/spec/custom-elements-registry/when_defined.html
+++ b/third_party/blink/web_tests/custom-elements/spec/custom-elements-registry/when_defined.html
@@ -74,12 +74,6 @@
     'div', 'p',
     'nothtmlbutnohyphen',
     '-not-initial-a-z', '0not-initial-a-z', 'Not-initial-a-z',
-    'intermediate-UPPERCASE-letters',
-    'bad-\u00b6', 'bad-\u00b8', 'bad-\u00bf', 'bad-\u00d7', 'bad-\u00f7',
-    'bad-\u037e', 'bad-\u037e', 'bad-\u2000', 'bad-\u200e', 'bad-\u203e',
-    'bad-\u2041', 'bad-\u206f', 'bad-\u2190', 'bad-\u2bff', 'bad-\u2ff0',
-    'bad-\u3000', 'bad-\ud800', 'bad-\uf8ff', 'bad-\ufdd0', 'bad-\ufdef',
-    'bad-\ufffe', 'bad-\uffff', 'bad-' + String.fromCodePoint(0xf0000)
   ];
 
   invalid_names.forEach((name) => {
diff --git a/third_party/blink/web_tests/custom-elements/spec/define-element-expected.txt b/third_party/blink/web_tests/custom-elements/spec/define-element-expected.txt
new file mode 100644
index 0000000..6be799d
--- /dev/null
+++ b/third_party/blink/web_tests/custom-elements/spec/define-element-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] Invalid names
+  assert_throws_dom: function "() => {\n      w.customElements.define(name, X);\n    }" did not throw
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/custom-elements/registries/define-expected.txt b/third_party/blink/web_tests/external/wpt/custom-elements/registries/define-expected.txt
new file mode 100644
index 0000000..5fa6248
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/custom-elements/registries/define-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+Found 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
+[FAIL] Element names: defining an element named a-a× should throw a SyntaxError
+  assert_throws_dom: function "() =>  {\n        customElements.define(name, class {});\n      }" did not throw
+[FAIL] Element names: defining an element named a-a  should throw a SyntaxError
+  assert_throws_dom: function "() =>  {\n        customElements.define(name, class {});\n      }" did not throw
+[FAIL] Element names: defining an element named a-aó°€€ should throw a SyntaxError
+  assert_throws_dom: function "() =>  {\n        customElements.define(name, class {});\n      }" did not throw
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/custom-elements/registries/valid-custom-element-names.tentative.html b/third_party/blink/web_tests/external/wpt/custom-elements/registries/valid-custom-element-names.tentative.html
new file mode 100644
index 0000000..72a5999
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/custom-elements/registries/valid-custom-element-names.tentative.html
@@ -0,0 +1,138 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/7991">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+function isAsciiLowerAlpha(codePoint) {
+  return codePoint >= 0x61 && codePoint <= 0x7A;
+}
+function isAsciiUpperAlpha(codePoint) {
+  return codePoint >= 0x41 && codePoint <= 0x5A;
+}
+function isAsciiAlpha(codePoint) {
+  return isAsciiLowerAlpha(codePoint) || isAsciiUpperAlpha(codePoint);
+}
+function isAsciiDigit(codePoint) {
+  return codePoint >= 0x30 && codePoint <= 0x39;
+}
+function isAsciiWhitespace(codePoint) {
+  return codePoint == 0x9 || codePoint == 0xA || codePoint == 0xC || codePoint == 0xD || codePoint == 0x20;
+}
+
+function debugString(str) {
+  const codePoints = [];
+  for (const c of str) {
+    codePoints.push(c.codePointAt(0));
+  }
+  return `code points: ${JSON.stringify(codePoints)}, string: "${str}"`;
+}
+
+const validCustomElementNames = [
+  'annotation-xml-custom',
+];
+const invalidCustomElementNames = [
+  '',
+  'annotation-xml',
+  'color-profile',
+  'font-face',
+  'font-face-src',
+  'font-face-uri',
+  'font-face-format',
+  'font-face-name',
+  'missing-glyph',
+];
+
+const testCodePoints = [0x1F171, 0x1F196, 0x10000];
+for (let i = 0; i < 0x80; i++) {
+  testCodePoints.push(i);
+}
+
+const elementLocalNameRegex = /^(?:[A-Za-z][^\0\t\n\f\r\u0020/>]*|[:_\u0080-\u{10FFFF}][A-Za-z0-9-.:_\u0080-\u{10FFFF}]*)$/u;
+
+function isValidCustomElementName(str) {
+  if (!str.length) {
+    return false;
+  }
+
+  if (!str.includes('-')) {
+    return false;
+  }
+
+  let first = true;
+  for (const c of str) {
+    const codePoint = c.codePointAt(0);
+    if (first) {
+      if (!isAsciiLowerAlpha(codePoint)) {
+        return false;
+      }
+      first = false;
+    }
+    if (isAsciiUpperAlpha(codePoint)) {
+      return false;
+    }
+  }
+
+  return elementLocalNameRegex.test(str);
+}
+
+// In order to test the branching logic of valid element local names and the
+// requirement of having a '-' character, this method generates different
+// variations of potential custom element names given two code points.
+function createStringWithSeparatorMode(codePoint, prefix, separatorMode) {
+  const str = String.fromCodePoint(codePoint);
+  if (separatorMode == 0) {
+    return `${prefix}${str}`;
+  } else if (separatorMode == 1) {
+    return `${prefix}-${str}`;
+  } else if (separatorMode == 2) {
+    return `${prefix}${str}-element`;
+  }
+}
+
+for (const prefix of ['', 'a', 'A', ' ', '\0']) {
+  for (const codePoint of testCodePoints) {
+    for (const separatorMode of [0, 1, 2]) {
+      const str = createStringWithSeparatorMode(
+        codePoint, prefix, separatorMode);
+      if (isValidCustomElementName(str)) {
+        validCustomElementNames.push(str);
+      } else {
+        invalidCustomElementNames.push(str);
+      }
+    }
+  }
+}
+
+let nextClassNumber = 1;
+function createElementClass() {
+  const name = `CustomElement${nextClassNumber++}`;
+  const newClass = function() {};
+  newClass.prototype = HTMLElement;
+  return newClass;
+}
+
+promise_test(async t => {
+  for (const validName of validCustomElementNames) {
+    try {
+      const newClass = createElementClass();
+      customElements.define(validName, newClass);
+      await customElements.whenDefined(validName);
+    } catch (error) {
+      assert_unreached(`Custom element name should have been valid but threw error: ${debugString(validName)} ${error.toString()}`);
+    }
+  }
+
+  for (const invalidName of invalidCustomElementNames) {
+    const newClass = createElementClass();
+    assert_throws_dom(
+      'SyntaxError',
+      () => customElements.define(invalidName, newClass),
+      `customElements.define should have thrown for invalid name: ${debugString(invalidName)}`);
+    await promise_rejects_dom(t, 'SyntaxError',
+      customElements.whenDefined(invalidName),
+      `customElements.whenDefined should have thrown for invalid name: ${debugString(invalidName)}`);
+  }
+});
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/fedcm/fedcm-login-status/idp-login-with-failed-accounts-fetch.https.html b/third_party/blink/web_tests/external/wpt/fedcm/fedcm-login-status/idp-login-with-failed-accounts-fetch.https.html
new file mode 100644
index 0000000..75d2c58
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fedcm/fedcm-login-status/idp-login-with-failed-accounts-fetch.https.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>FedCM IDP log-in status API tests</title>
+<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>
+
+<script type="module">
+import {request_options_with_mediation_required,
+        fedcm_test,
+        fedcm_get_dialog_type_promise,
+        select_manifest,
+        mark_signed_out} from '../support/fedcm-helper.sub.js';
+
+fedcm_test(async t => {
+  await mark_signed_out();
+
+  let test_options = request_options_with_mediation_required("manifest_no_accounts_login_delay.json");
+  await select_manifest(t, test_options);
+
+  test_options.identity.mode = "active";
+  let cred_promise = navigator.credentials.get(test_options);
+
+  // We should get the login popup window, which will automatically set
+  // the login status and close itself.
+  // The promise should get rejected because the accounts list is empty.
+
+  return promise_rejects_dom(t, 'NetworkError', cred_promise);
+}, 'Tests the IDP login dialog and subsequent account chooser.');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/fedcm/support/login_delay.html b/third_party/blink/web_tests/external/wpt/fedcm/support/login_delay.html
new file mode 100644
index 0000000..469a7ee
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fedcm/support/login_delay.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<script>
+async function doLogin() {
+  navigator.login.setStatus("logged-in");
+  // Delay the close call to allow the accounts fetch to complete beforehand.
+  setTimeout(() => {
+    IdentityProvider.close();
+  }, 200);
+}
+window.onload = doLogin;
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/fedcm/support/manifest_no_accounts_login_delay.json b/third_party/blink/web_tests/external/wpt/fedcm/support/manifest_no_accounts_login_delay.json
new file mode 100644
index 0000000..030e4a8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fedcm/support/manifest_no_accounts_login_delay.json
@@ -0,0 +1,6 @@
+{
+  "accounts_endpoint": "no_accounts.py",
+  "client_metadata_endpoint": "client_metadata.py",
+  "id_assertion_endpoint": "token_with_account_id.py",
+  "login_url": "login_delay.html"
+}
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/command-and-commandfor/button-type-reflection-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/command-and-commandfor/button-type-reflection-expected.txt
deleted file mode 100644
index d8cc0d9..0000000
--- a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/command-and-commandfor/button-type-reflection-expected.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Button with id missing-in-form should reflect type correctly
-  assert_equals: type of missing-in-form should be button expected "button" but got "submit"
-[FAIL] Button with id missing-in-form-command-only should reflect type correctly
-  assert_equals: type of missing-in-form-command-only should be button expected "button" but got "submit"
-[FAIL] Button with id missing-in-form-commandfor-only should reflect type correctly
-  assert_equals: type of missing-in-form-commandfor-only should be button expected "button" but got "submit"
-[FAIL] Button with id missing-attr-form should reflect type correctly
-  assert_equals: type of missing-attr-form should be button expected "button" but got "submit"
-[FAIL] Button with id missing-attr-form-command-only should reflect type correctly
-  assert_equals: type of missing-attr-form-command-only should be button expected "button" but got "submit"
-[FAIL] Button with id missing-attr-form-commandfor-only should reflect type correctly
-  assert_equals: type of missing-attr-form-commandfor-only should be button expected "button" but got "submit"
-[FAIL] Button with id missing-outside-form should reflect type correctly
-  assert_equals: type of missing-outside-form should be button expected "button" but got "submit"
-[FAIL] Button with id missing-outside-form-command-only should reflect type correctly
-  assert_equals: type of missing-outside-form-command-only should be button expected "button" but got "submit"
-[FAIL] Button with id missing-outside-form-commandfor-only should reflect type correctly
-  assert_equals: type of missing-outside-form-commandfor-only should be button expected "button" but got "submit"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/lint.ignore b/third_party/blink/web_tests/external/wpt/lint.ignore
index 470a1fd..1577148 100644
--- a/third_party/blink/web_tests/external/wpt/lint.ignore
+++ b/third_party/blink/web_tests/external/wpt/lint.ignore
@@ -173,6 +173,7 @@
 SET TIMEOUT: encrypted-media/scripts/playback-temporary-events.js
 SET TIMEOUT: fedcm/support/fedcm-iframe.html
 SET TIMEOUT: fedcm/support/fedcm/disconnect-iframe.html
+SET TIMEOUT: fedcm/support/login_delay.html
 SET TIMEOUT: fetch/fetch-later/resources/fetch-later-helper.js
 SET TIMEOUT: fetch/metadata/resources/helper.sub.js
 SET TIMEOUT: fetch/metadata/resources/message-opener.html
diff --git a/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/dom/tentative/distant-leaf.window.js b/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/dom/tentative/distant-leaf.window.js
new file mode 100644
index 0000000..c599985
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/dom/tentative/distant-leaf.window.js
@@ -0,0 +1,47 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=../../resources/soft-navigation-test-helper.js
+
+// This test is intended to verify that when a distant leaf of a
+// deeply nested div element is attached to the DOM, its painting can
+// trigger a soft navigation.
+//
+// To show this, we create a button that, when clicked, creates a deeply
+// nested div element and attaches it to the DOM - only the leaf, a text
+// node saying "Hello, World.", 10 levels below the attachment point actually
+// gets painted.
+
+function clickHandler() {
+  let div = document.createElement('div');
+  div.textContent = 'Hello, World.';  // The leaf node that gets painted.
+  for (let i = 0; i < 10; i++) {
+    const tmp = document.createElement('div');
+    tmp.appendChild(div);
+    div = tmp;
+  }
+  document.body.appendChild(div);
+  history.pushState({}, '', '/greeting');
+}
+
+const button = document.createElement('div');
+button.textContent = 'Click here!';
+button.onclick = clickHandler;
+document.body.appendChild(button);
+
+promise_test(async (t) => {
+  if (test_driver) {
+    test_driver.click(button);
+  }
+  const helper = new SoftNavigationTestHelper(t);
+  const entries = await helper.getBufferedPerformanceEntriesWithTimeout(
+      /*type=*/ 'soft-navigation',
+      /*includeSoftNavigationObservations=*/ false,
+      /*minNumEntries=*/ 1,
+  );
+  assert_equals(entries.length, 1, 'Expected exactly one soft navigation.');
+  assert_equals(
+      entries[0].name.replace(/.*\//, ''),
+      'greeting',
+      'URL ends with \'greeting\'.',
+  );
+}, 'DOM: Distant leaf satisfies Soft Navigation paint criterion.');
diff --git a/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/history/tentative/navigation-api-prevent-default.window.js b/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/history/tentative/navigation-api-prevent-default.window.js
new file mode 100644
index 0000000..3df2192
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/history/tentative/navigation-api-prevent-default.window.js
@@ -0,0 +1,48 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+
+// This test shows that preventDefault() on the navigate event can
+// prevent a soft navigation, because it ensures that neither the a.href
+// (foobar.html in our example) is visited, nor the handler specified in the
+// intercept on the navigate event is called.
+
+const link = document.createElement('a');
+link.href = 'foobar.html';
+link.textContent = 'Click me!';
+document.body.appendChild(link);
+
+promise_test(async (t) => {
+  let navigateProcessed = false;
+
+  navigation.addEventListener('navigate', (e) => {
+    e.intercept({
+      async handler() {
+        assert_unreached('preventDefault() should prevent the navigation');
+      },
+    });
+    e.preventDefault();
+    navigateProcessed = true;
+  });
+
+  if (test_driver) {
+    test_driver.click(link);
+  }
+
+  await t.step_wait(
+      () => navigateProcessed, '\'navigate\' event not processed');
+
+  const observer = new PerformanceObserver(() => {
+    assert_unreached('Soft navigation should not be triggered');
+  });
+  observer.observe({type: 'soft-navigation', buffered: true});
+
+  await new Promise((resolve) => {
+    t.step_timeout(resolve, 3000);
+  }).then(() => {
+    observer.disconnect();
+  });
+  if (document.softNavigations) {
+    assert_equals(document.softNavigations, 0, 'Soft Navigation not detected');
+  }
+  assert_false(location.href.includes('foobar.html'), 'foobar.html not visited');
+}, 'Navigation API: Aborted navigate event is not a soft navigation');
diff --git a/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/navigation-api-preventDefault.tentative.html b/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/navigation-api-preventDefault.tentative.html
deleted file mode 100644
index b7b2a24..0000000
--- a/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/navigation-api-preventDefault.tentative.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-<meta charset="utf-8">
-<title>Don't detect a navigate event which got aborted as a soft navigation.
-</title>
-<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>
-<script src="resources/soft-navigation-helper.js"></script>
-</head>
-<body>
-  <main id=main>
-  <a href="foobar.html" id=link>Click me!</a>
-  </main>
-  <script>
-    const link = document.getElementById("link");
-    testSoftNavigationNotDetected({
-      testName: "Aborted navigate event is not a soft navigation",
-      eventHandler: e => {
-        e.intercept({handler: async () => {
-          await addImageToMain();
-          main.appendChild(img);
-        }});
-        e.preventDefault();
-        timestamps[counter]["eventEnd"] = performance.now();
-      },
-      eventTarget: navigation,
-      eventName: "navigate",
-      link: link});
-  </script>
-</body>
-</html>
-
diff --git a/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/resources/soft-navigation-helper.js b/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/resources/soft-navigation-helper.js
index 1b244eb4..4bc16b4 100644
--- a/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/resources/soft-navigation-helper.js
+++ b/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/resources/soft-navigation-helper.js
@@ -113,28 +113,6 @@
   }, testName);
 };
 
-const testSoftNavigationNotDetected = options => {
-  promise_test(async t => {
-    const preClickLcp = await getLcpEntries();
-    options.eventTarget.addEventListener(
-        options.eventName, options.eventHandler);
-    interact(options.link);
-    await new Promise((resolve, reject) => {
-      new PerformanceObserver(() => {
-        reject('Soft navigation should not be triggered');
-      }).observe({type: 'soft-navigation', buffered: true});
-      t.step_timeout(resolve, 1000);
-    });
-    if (document.softNavigations) {
-      assert_equals(
-          document.softNavigations, 0, 'Soft Navigation not detected');
-    }
-    const postClickLcp = await getLcpEntries();
-    assert_equals(
-        preClickLcp.length, postClickLcp.length, 'No LCP entries accumulated');
-  }, options.testName);
-};
-
 const runEntryValidations = async (
     preClickLcp, first_navigation_id, entries_expected_number = 2,
     validate = null) => {
diff --git a/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/smoke/tentative/supported-entry-types.window.js b/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/smoke/tentative/supported-entry-types.window.js
new file mode 100644
index 0000000..50e22df
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/smoke/tentative/supported-entry-types.window.js
@@ -0,0 +1,7 @@
+// PerformanceObserver.supportedEntryTypes is a good way to detect whether
+// soft navigations are supported. See also:
+// https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver/supportedEntryTypes_static
+
+test(() => {
+  assert_in_array('soft-navigation', PerformanceObserver.supportedEntryTypes);
+}, 'Soft navigations are a supported entry type for PerformanceObserver');
diff --git a/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/supported-entry-types.tentative.html b/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/supported-entry-types.tentative.html
deleted file mode 100644
index 4ab408e..0000000
--- a/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/supported-entry-types.tentative.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-<meta charset="utf-8">
-<title>Soft navigations are a supported entry type</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script>
-  promise_test(async () => {
-    assert_true(PerformanceObserver.supportedEntryTypes.includes(
-      "soft-navigation"));
-  }, "Soft navigations are a supported entry type");
-</script>
-
-
diff --git a/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any.serviceworker_gpu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any.serviceworker_gpu-expected.txt
deleted file mode 100644
index 409fb5d..0000000
--- a/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any.serviceworker_gpu-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Create context with GPUDevice.
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'createContext' on 'ML': ML.createContext(GPUDevice) is not supported."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any.sharedworker_gpu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any.sharedworker_gpu-expected.txt
deleted file mode 100644
index 409fb5d..0000000
--- a/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any.sharedworker_gpu-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Create context with GPUDevice.
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'createContext' on 'ML': ML.createContext(GPUDevice) is not supported."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any.worker_gpu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any.worker_gpu-expected.txt
deleted file mode 100644
index 409fb5d..0000000
--- a/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any.worker_gpu-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Create context with GPUDevice.
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'createContext' on 'ML': ML.createContext(GPUDevice) is not supported."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any_gpu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any_gpu-expected.txt
deleted file mode 100644
index 409fb5d..0000000
--- a/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any_gpu-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Create context with GPUDevice.
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'createContext' on 'ML': ML.createContext(GPUDevice) is not supported."
-Harness: the test ran to completion.
-
diff --git a/third_party/boringssl/src b/third_party/boringssl/src
index 9a895ea..60d933e 160000
--- a/third_party/boringssl/src
+++ b/third_party/boringssl/src
@@ -1 +1 @@
-Subproject commit 9a895ea00a34ce7ce2d9f087c9d8bbfc639e233d
+Subproject commit 60d933e9d62d33a8d58bb350dea7c27b6b18b619
diff --git a/third_party/chromite b/third_party/chromite
index 1b3f6f4..a61d516 160000
--- a/third_party/chromite
+++ b/third_party/chromite
@@ -1 +1 @@
-Subproject commit 1b3f6f4b3bf374f7fe826a19bd009d346f024ddf
+Subproject commit a61d51627b18bfe921b8698a092f28523d96bd44
diff --git a/third_party/cpuinfo/README.chromium b/third_party/cpuinfo/README.chromium
index cabfcab..2e2ea93 100644
--- a/third_party/cpuinfo/README.chromium
+++ b/third_party/cpuinfo/README.chromium
@@ -1,8 +1,8 @@
 Name: cpuinfo
 Short Name: cpuinfo
 URL: https://github.com/pytorch/cpuinfo
-Version: de0ce7c7251372892e53ce9bc891750d2c9a4fd8
-Date: 2025-05-23
+Version: 6c9eb84ba310f237cea13c478be50102e1128e9b
+Date: 2025-06-05
 License: BSD-2-Clause
 License File: src/LICENSE
 Security Critical: Yes
diff --git a/third_party/cpuinfo/src b/third_party/cpuinfo/src
index de0ce7c..6c9eb84 160000
--- a/third_party/cpuinfo/src
+++ b/third_party/cpuinfo/src
@@ -1 +1 @@
-Subproject commit de0ce7c7251372892e53ce9bc891750d2c9a4fd8
+Subproject commit 6c9eb84ba310f237cea13c478be50102e1128e9b
diff --git a/third_party/depot_tools b/third_party/depot_tools
index 6d52c22..4e7e235 160000
--- a/third_party/depot_tools
+++ b/third_party/depot_tools
@@ -1 +1 @@
-Subproject commit 6d52c22ee313eb5701f326a9eca98acb41b669b4
+Subproject commit 4e7e235dc89c740ddb1cca39ef2ad1fca7745be1
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index ef9aad9..71c4f8a 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit ef9aad93988e3ff4149c5ea6e337abf234dc74de
+Subproject commit 71c4f8a7f072f0f31369d45241ffb6c24769a320
diff --git a/third_party/eigen3/README.chromium b/third_party/eigen3/README.chromium
index 4b59774..48daa7f2 100644
--- a/third_party/eigen3/README.chromium
+++ b/third_party/eigen3/README.chromium
@@ -1,8 +1,8 @@
 Name: Eigen
 Short Name: eigen3
 URL: https://gitlab.com/libeigen/eigen
-Version: 171bd08ca987987c3c50f0fa5dd8914bdd42dd3b
-Date: 2025-05-23
+Version: 21e89b930c6af56dbdaeea2a91d8b9d6fd2c208a
+Date: 2025-06-05
 License: MPL-2.0
 License File: LICENSE
 Security Critical: Yes
diff --git a/third_party/eigen3/src b/third_party/eigen3/src
index 171bd08..21e89b9 160000
--- a/third_party/eigen3/src
+++ b/third_party/eigen3/src
@@ -1 +1 @@
-Subproject commit 171bd08ca987987c3c50f0fa5dd8914bdd42dd3b
+Subproject commit 21e89b930c6af56dbdaeea2a91d8b9d6fd2c208a
diff --git a/third_party/perfetto b/third_party/perfetto
index 557fafc..5761103 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit 557fafc38445fdde33d19b1cf2d784686e51d120
+Subproject commit 576110306936083fafbdab5594b96d25d4b4106d
diff --git a/third_party/swiftshader b/third_party/swiftshader
index b62ac8a..974741b 160000
--- a/third_party/swiftshader
+++ b/third_party/swiftshader
@@ -1 +1 @@
-Subproject commit b62ac8aa106b86158bb5925004d1b3658d664756
+Subproject commit 974741b1cbc8c648e6beb74ff93182665af6286a
diff --git a/third_party/tflite/README.chromium b/third_party/tflite/README.chromium
index 8c885e30..f3b72f67 100644
--- a/third_party/tflite/README.chromium
+++ b/third_party/tflite/README.chromium
@@ -1,8 +1,8 @@
 Name: TensorFlow Lite
 Short Name: tflite
 URL: https://github.com/tensorflow/tensorflow
-Version: 2acd05be6d3a637cb2ed2b040879c55b02d6bc19
-Date: 2025-05-23
+Version: 60fb9c76b41f0e4d6cfdd6b1bd2fabe443455306
+Date: 2025-06-05
 License: Caffe, Apache-2.0
 License File: LICENSE
 Security Critical: Yes
diff --git a/third_party/tflite/src b/third_party/tflite/src
index 2acd05b..60fb9c7 160000
--- a/third_party/tflite/src
+++ b/third_party/tflite/src
@@ -1 +1 @@
-Subproject commit 2acd05be6d3a637cb2ed2b040879c55b02d6bc19
+Subproject commit 60fb9c76b41f0e4d6cfdd6b1bd2fabe443455306
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps
index 0dbc058..d96f5ec 160000
--- a/third_party/vulkan-deps
+++ b/third_party/vulkan-deps
@@ -1 +1 @@
-Subproject commit 0dbc0583398406ec3c74f88cc1fb4dc21f478040
+Subproject commit d96f5ecf24060ed2e820d8f1f74a2b1bfb3b0c64
diff --git a/third_party/vulkan-validation-layers/src b/third_party/vulkan-validation-layers/src
index 596534d..287caf1b 160000
--- a/third_party/vulkan-validation-layers/src
+++ b/third_party/vulkan-validation-layers/src
@@ -1 +1 @@
-Subproject commit 596534df7e93137255f803653de4614e9d9e7ba2
+Subproject commit 287caf1be206dca1b8a391b10eab474a7e238fd6
diff --git a/third_party/webrtc b/third_party/webrtc
index fd4a59c..382cb45 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit fd4a59c2a43b208d965899a6cef10404d5cdc43d
+Subproject commit 382cb45e818024f4168918f6b04f99dd659f50ca
diff --git a/third_party/xnnpack/BUILD.gn b/third_party/xnnpack/BUILD.gn
index 385b6e62..af38305 100644
--- a/third_party/xnnpack/BUILD.gn
+++ b/third_party/xnnpack/BUILD.gn
@@ -249,10 +249,6 @@
     ":f32-vlrelu_x64",
     ":f32-vmulcaddc_sse-no-sse2",
     ":f32-vmulcaddc_x64",
-    ":f32-vrelu_avx-no-avx2-no-f16c-no-fma",
-    ":f32-vrelu_avx512f",
-    ":f32-vrelu_sse2-no-sse3",
-    ":f32-vrelu_x64",
     ":f32-vrnd_avx-no-avx2-no-f16c-no-fma",
     ":f32-vrnd_avx512f",
     ":f32-vrnd_sse2-no-sse3",
@@ -753,10 +749,6 @@
     ":f32-vlrelu_x64_standalone",
     ":f32-vmulcaddc_sse-no-sse2_standalone",
     ":f32-vmulcaddc_x64_standalone",
-    ":f32-vrelu_avx-no-avx2-no-f16c-no-fma_standalone",
-    ":f32-vrelu_avx512f_standalone",
-    ":f32-vrelu_sse2-no-sse3_standalone",
-    ":f32-vrelu_x64_standalone",
     ":f32-vrnd_avx-no-avx2-no-f16c-no-fma_standalone",
     ":f32-vrnd_avx512f_standalone",
     ":f32-vrnd_sse2-no-sse3_standalone",
@@ -1154,7 +1146,6 @@
     ":f32-vlog_arm64",
     ":f32-vlrelu_arm64",
     ":f32-vmulcaddc_arm64",
-    ":f32-vrelu_arm64",
     ":f32-vrnd_arm64",
     ":f32-vrsqrt_arm64",
     ":f32-vsigmoid_arm64",
@@ -1368,7 +1359,6 @@
     ":f32-vlog_arm64_standalone",
     ":f32-vlrelu_arm64_standalone",
     ":f32-vmulcaddc_arm64_standalone",
-    ":f32-vrelu_arm64_standalone",
     ":f32-vrnd_arm64_standalone",
     ":f32-vrsqrt_arm64_standalone",
     ":f32-vsigmoid_arm64_standalone",
@@ -1559,8 +1549,6 @@
     ":f32-vlrelu_arch=rv64gcv-abi=lp64d",
     ":f32-vlrelu_riscv64",
     ":f32-vmulcaddc_riscv64",
-    ":f32-vrelu_arch=rv64gcv-abi=lp64d",
-    ":f32-vrelu_riscv64",
     ":f32-vrnd_arch=rv64gcv-abi=lp64d",
     ":f32-vrnd_riscv64",
     ":f32-vrsqrt_arch=rv64gcv-abi=lp64d",
@@ -1572,8 +1560,11 @@
     ":f32-vunary_riscv64",
     ":operators_riscv64",
     ":qd8-f32-qb4w-gemm_riscv64",
+    ":qd8-f32-qc4w-gemm_arch=rv64gcv-abi=lp64d",
     ":qd8-f32-qc4w-gemm_riscv64",
+    ":qd8-f32-qc8w-gemm_arch=rv64gcv-abi=lp64d",
     ":qd8-f32-qc8w-gemm_riscv64",
+    ":qd8-f32-qc8w-igemm_arch=rv64gcv-abi=lp64d",
     ":qd8-f32-qc8w-igemm_riscv64",
     ":qs8-dwconv_arch=rv64gcv-abi=lp64d",
     ":qs8-dwconv_riscv64",
@@ -1729,8 +1720,6 @@
     ":f32-vlrelu_arch=rv64gcv-abi=lp64d_standalone",
     ":f32-vlrelu_riscv64_standalone",
     ":f32-vmulcaddc_riscv64_standalone",
-    ":f32-vrelu_arch=rv64gcv-abi=lp64d_standalone",
-    ":f32-vrelu_riscv64_standalone",
     ":f32-vrnd_arch=rv64gcv-abi=lp64d_standalone",
     ":f32-vrnd_riscv64_standalone",
     ":f32-vrsqrt_arch=rv64gcv-abi=lp64d_standalone",
@@ -1742,8 +1731,11 @@
     ":f32-vunary_riscv64_standalone",
     ":operators_riscv64_standalone",
     ":qd8-f32-qb4w-gemm_riscv64_standalone",
+    ":qd8-f32-qc4w-gemm_arch=rv64gcv-abi=lp64d_standalone",
     ":qd8-f32-qc4w-gemm_riscv64_standalone",
+    ":qd8-f32-qc8w-gemm_arch=rv64gcv-abi=lp64d_standalone",
     ":qd8-f32-qc8w-gemm_riscv64_standalone",
+    ":qd8-f32-qc8w-igemm_arch=rv64gcv-abi=lp64d_standalone",
     ":qd8-f32-qc8w-igemm_riscv64_standalone",
     ":qs8-dwconv_arch=rv64gcv-abi=lp64d_standalone",
     ":qs8-dwconv_riscv64_standalone",
@@ -12774,218 +12766,6 @@
     }
   }
 
-  source_set("f32-vrelu_avx-no-avx2-no-f16c-no-fma") {
-    cflags = [
-      "-mavx",
-      "-mno-avx2",
-      "-mno-f16c",
-      "-mno-fma",
-    ]
-
-    sources = [
-      "src/include/xnnpack.h",
-      "src/src/f32-vrelu/gen/f32-vrelu-avx.c",
-    ]
-
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
-
-    deps = [
-      "//third_party/cpuinfo",
-      "//third_party/fp16",
-      "//third_party/fxdiv",
-      "//third_party/pthreadpool",
-    ]
-
-    public_configs = [ ":xnnpack_config" ]
-  }
-
-  # This is a target that cannot depend on //base.
-  source_set("f32-vrelu_avx-no-avx2-no-f16c-no-fma_standalone") {
-    cflags = [
-      "-mavx",
-      "-mno-avx2",
-      "-mno-f16c",
-      "-mno-fma",
-    ]
-
-    sources = [
-      "src/include/xnnpack.h",
-      "src/src/f32-vrelu/gen/f32-vrelu-avx.c",
-    ]
-
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
-
-    deps = [
-      "//third_party/cpuinfo",
-      "//third_party/fp16",
-      "//third_party/fxdiv",
-      "//third_party/pthreadpool:pthreadpool_standalone",
-    ]
-
-    public_configs = [ ":xnnpack_config" ]
-
-    if (!(is_android && use_order_profiling)) {
-      assert_no_deps = [ "//base" ]
-    }
-  }
-
-  source_set("f32-vrelu_avx512f") {
-    cflags = [ "-mavx512f" ]
-
-    sources = [
-      "src/include/xnnpack.h",
-      "src/src/f32-vrelu/gen/f32-vrelu-avx512f.c",
-    ]
-
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
-
-    deps = [
-      "//third_party/cpuinfo",
-      "//third_party/fp16",
-      "//third_party/fxdiv",
-      "//third_party/pthreadpool",
-    ]
-
-    public_configs = [ ":xnnpack_config" ]
-  }
-
-  # This is a target that cannot depend on //base.
-  source_set("f32-vrelu_avx512f_standalone") {
-    cflags = [ "-mavx512f" ]
-
-    sources = [
-      "src/include/xnnpack.h",
-      "src/src/f32-vrelu/gen/f32-vrelu-avx512f.c",
-    ]
-
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
-
-    deps = [
-      "//third_party/cpuinfo",
-      "//third_party/fp16",
-      "//third_party/fxdiv",
-      "//third_party/pthreadpool:pthreadpool_standalone",
-    ]
-
-    public_configs = [ ":xnnpack_config" ]
-
-    if (!(is_android && use_order_profiling)) {
-      assert_no_deps = [ "//base" ]
-    }
-  }
-
-  source_set("f32-vrelu_sse2-no-sse3") {
-    cflags = [
-      "-mno-sse3",
-      "-msse2",
-    ]
-
-    sources = [
-      "src/include/xnnpack.h",
-      "src/src/f32-vrelu/gen/f32-vrelu-sse2.c",
-    ]
-
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
-
-    deps = [
-      "//third_party/cpuinfo",
-      "//third_party/fp16",
-      "//third_party/fxdiv",
-      "//third_party/pthreadpool",
-    ]
-
-    public_configs = [ ":xnnpack_config" ]
-  }
-
-  # This is a target that cannot depend on //base.
-  source_set("f32-vrelu_sse2-no-sse3_standalone") {
-    cflags = [
-      "-mno-sse3",
-      "-msse2",
-    ]
-
-    sources = [
-      "src/include/xnnpack.h",
-      "src/src/f32-vrelu/gen/f32-vrelu-sse2.c",
-    ]
-
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
-
-    deps = [
-      "//third_party/cpuinfo",
-      "//third_party/fp16",
-      "//third_party/fxdiv",
-      "//third_party/pthreadpool:pthreadpool_standalone",
-    ]
-
-    public_configs = [ ":xnnpack_config" ]
-
-    if (!(is_android && use_order_profiling)) {
-      assert_no_deps = [ "//base" ]
-    }
-  }
-
-  source_set("f32-vrelu_x64") {
-    cflags = []
-
-    sources = [
-      "src/include/xnnpack.h",
-      "src/src/f32-vrelu/gen/f32-vrelu-scalar.c",
-    ]
-
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
-
-    deps = [
-      "//third_party/cpuinfo",
-      "//third_party/fp16",
-      "//third_party/fxdiv",
-      "//third_party/pthreadpool",
-    ]
-
-    public_configs = [ ":xnnpack_config" ]
-  }
-
-  # This is a target that cannot depend on //base.
-  source_set("f32-vrelu_x64_standalone") {
-    cflags = []
-
-    sources = [
-      "src/include/xnnpack.h",
-      "src/src/f32-vrelu/gen/f32-vrelu-scalar.c",
-    ]
-
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
-
-    deps = [
-      "//third_party/cpuinfo",
-      "//third_party/fp16",
-      "//third_party/fxdiv",
-      "//third_party/pthreadpool:pthreadpool_standalone",
-    ]
-
-    public_configs = [ ":xnnpack_config" ]
-
-    if (!(is_android && use_order_profiling)) {
-      assert_no_deps = [ "//base" ]
-    }
-  }
-
   source_set("f32-vrnd_avx-no-avx2-no-f16c-no-fma") {
     cflags = [
       "-mavx",
@@ -35637,57 +35417,6 @@
     }
   }
 
-  source_set("f32-vrelu_arm64") {
-    cflags = []
-
-    sources = [
-      "src/include/xnnpack.h",
-      "src/src/f32-vrelu/gen/f32-vrelu-neon.c",
-      "src/src/f32-vrelu/gen/f32-vrelu-scalar.c",
-    ]
-
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
-
-    deps = [
-      "//third_party/cpuinfo",
-      "//third_party/fp16",
-      "//third_party/fxdiv",
-      "//third_party/pthreadpool",
-    ]
-
-    public_configs = [ ":xnnpack_config" ]
-  }
-
-  # This is a target that cannot depend on //base.
-  source_set("f32-vrelu_arm64_standalone") {
-    cflags = []
-
-    sources = [
-      "src/include/xnnpack.h",
-      "src/src/f32-vrelu/gen/f32-vrelu-neon.c",
-      "src/src/f32-vrelu/gen/f32-vrelu-scalar.c",
-    ]
-
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
-
-    deps = [
-      "//third_party/cpuinfo",
-      "//third_party/fp16",
-      "//third_party/fxdiv",
-      "//third_party/pthreadpool:pthreadpool_standalone",
-    ]
-
-    public_configs = [ ":xnnpack_config" ]
-
-    if (!(is_android && use_order_profiling)) {
-      assert_no_deps = [ "//base" ]
-    }
-  }
-
   source_set("f32-vrnd_arm64") {
     cflags = []
 
@@ -45819,110 +45548,6 @@
     }
   }
 
-  source_set("f32-vrelu_arch=rv64gcv-abi=lp64d") {
-    cflags = [
-      "-mabi=lp64d",
-      "-march=rv64gcv",
-    ]
-
-    sources = [
-      "src/include/xnnpack.h",
-      "src/src/f32-vrelu/gen/f32-vrelu-rvv-u4v.c",
-    ]
-
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
-
-    deps = [
-      "//third_party/cpuinfo",
-      "//third_party/fp16",
-      "//third_party/fxdiv",
-      "//third_party/pthreadpool",
-    ]
-
-    public_configs = [ ":xnnpack_config" ]
-  }
-
-  # This is a target that cannot depend on //base.
-  source_set("f32-vrelu_arch=rv64gcv-abi=lp64d_standalone") {
-    cflags = [
-      "-mabi=lp64d",
-      "-march=rv64gcv",
-    ]
-
-    sources = [
-      "src/include/xnnpack.h",
-      "src/src/f32-vrelu/gen/f32-vrelu-rvv-u4v.c",
-    ]
-
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
-
-    deps = [
-      "//third_party/cpuinfo",
-      "//third_party/fp16",
-      "//third_party/fxdiv",
-      "//third_party/pthreadpool:pthreadpool_standalone",
-    ]
-
-    public_configs = [ ":xnnpack_config" ]
-
-    if (!(is_android && use_order_profiling)) {
-      assert_no_deps = [ "//base" ]
-    }
-  }
-
-  source_set("f32-vrelu_riscv64") {
-    cflags = []
-
-    sources = [
-      "src/include/xnnpack.h",
-      "src/src/f32-vrelu/gen/f32-vrelu-scalar.c",
-    ]
-
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
-
-    deps = [
-      "//third_party/cpuinfo",
-      "//third_party/fp16",
-      "//third_party/fxdiv",
-      "//third_party/pthreadpool",
-    ]
-
-    public_configs = [ ":xnnpack_config" ]
-  }
-
-  # This is a target that cannot depend on //base.
-  source_set("f32-vrelu_riscv64_standalone") {
-    cflags = []
-
-    sources = [
-      "src/include/xnnpack.h",
-      "src/src/f32-vrelu/gen/f32-vrelu-scalar.c",
-    ]
-
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
-
-    deps = [
-      "//third_party/cpuinfo",
-      "//third_party/fp16",
-      "//third_party/fxdiv",
-      "//third_party/pthreadpool:pthreadpool_standalone",
-    ]
-
-    public_configs = [ ":xnnpack_config" ]
-
-    if (!(is_android && use_order_profiling)) {
-      assert_no_deps = [ "//base" ]
-    }
-  }
-
   source_set("f32-vrnd_arch=rv64gcv-abi=lp64d") {
     cflags = [
       "-mabi=lp64d",
@@ -46534,6 +46159,63 @@
     }
   }
 
+  source_set("qd8-f32-qc4w-gemm_arch=rv64gcv-abi=lp64d") {
+    cflags = [
+      "-mabi=lp64d",
+      "-march=rv64gcv",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qd8-f32-qc4w-gemm/gen/qd8-f32-qc4w-gemm-1x4v-minmax-rvv.c",
+      "src/src/qd8-f32-qc4w-gemm/gen/qd8-f32-qc4w-gemm-4x4v-minmax-rvv.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qd8-f32-qc4w-gemm_arch=rv64gcv-abi=lp64d_standalone") {
+    cflags = [
+      "-mabi=lp64d",
+      "-march=rv64gcv",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qd8-f32-qc4w-gemm/gen/qd8-f32-qc4w-gemm-1x4v-minmax-rvv.c",
+      "src/src/qd8-f32-qc4w-gemm/gen/qd8-f32-qc4w-gemm-4x4v-minmax-rvv.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
   source_set("qd8-f32-qc4w-gemm_riscv64") {
     cflags = []
 
@@ -46585,6 +46267,63 @@
     }
   }
 
+  source_set("qd8-f32-qc8w-gemm_arch=rv64gcv-abi=lp64d") {
+    cflags = [
+      "-mabi=lp64d",
+      "-march=rv64gcv",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qd8-f32-qc8w-gemm/gen/qd8-f32-qc8w-gemm-1x4v-minmax-rvv.c",
+      "src/src/qd8-f32-qc8w-gemm/gen/qd8-f32-qc8w-gemm-4x4v-minmax-rvv.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qd8-f32-qc8w-gemm_arch=rv64gcv-abi=lp64d_standalone") {
+    cflags = [
+      "-mabi=lp64d",
+      "-march=rv64gcv",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qd8-f32-qc8w-gemm/gen/qd8-f32-qc8w-gemm-1x4v-minmax-rvv.c",
+      "src/src/qd8-f32-qc8w-gemm/gen/qd8-f32-qc8w-gemm-4x4v-minmax-rvv.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
   source_set("qd8-f32-qc8w-gemm_riscv64") {
     cflags = []
 
@@ -46638,6 +46377,63 @@
     }
   }
 
+  source_set("qd8-f32-qc8w-igemm_arch=rv64gcv-abi=lp64d") {
+    cflags = [
+      "-mabi=lp64d",
+      "-march=rv64gcv",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qd8-f32-qc8w-igemm/gen/qd8-f32-qc8w-igemm-1x4v-minmax-rvv.c",
+      "src/src/qd8-f32-qc8w-igemm/gen/qd8-f32-qc8w-igemm-4x4v-minmax-rvv.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qd8-f32-qc8w-igemm_arch=rv64gcv-abi=lp64d_standalone") {
+    cflags = [
+      "-mabi=lp64d",
+      "-march=rv64gcv",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qd8-f32-qc8w-igemm/gen/qd8-f32-qc8w-igemm-1x4v-minmax-rvv.c",
+      "src/src/qd8-f32-qc8w-igemm/gen/qd8-f32-qc8w-igemm-4x4v-minmax-rvv.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
   source_set("qd8-f32-qc8w-igemm_riscv64") {
     cflags = []
 
diff --git a/third_party/xnnpack/README.chromium b/third_party/xnnpack/README.chromium
index d7dbf430..71017e3 100644
--- a/third_party/xnnpack/README.chromium
+++ b/third_party/xnnpack/README.chromium
@@ -1,8 +1,8 @@
 Name: XNNPACK
 Short Name: xnnpack
 URL: https://github.com/google/xnnpack
-Version: 84f98c030577fc1d8c3a5fcc703f765fd8a976be
-Date: 2025-05-23
+Version: 888719e9a4b11251d03ad2d6526da7f2e2b57308
+Date: 2025-06-05
 License: BSD-3-Clause
 License File: src/LICENSE
 Security Critical: Yes
diff --git a/third_party/xnnpack/build_identifier.c b/third_party/xnnpack/build_identifier.c
index 0f31862..069eb2a 100644
--- a/third_party/xnnpack/build_identifier.c
+++ b/third_party/xnnpack/build_identifier.c
@@ -443,10 +443,6 @@
 // - external/xnnpack+/src/f32-vlrelu/gen/f32-vlrelu-sse41-u8.c
 // - external/xnnpack+/src/f32-vmulcaddc/gen/f32-vmulcaddc-c1-minmax-scalar-2x.c
 // - external/xnnpack+/src/f32-vmulcaddc/gen/f32-vmulcaddc-c4-minmax-sse-2x.c
-// - external/xnnpack+/src/f32-vrelu/gen/f32-vrelu-avx.c
-// - external/xnnpack+/src/f32-vrelu/gen/f32-vrelu-avx512f.c
-// - external/xnnpack+/src/f32-vrelu/gen/f32-vrelu-scalar.c
-// - external/xnnpack+/src/f32-vrelu/gen/f32-vrelu-sse2.c
 // - external/xnnpack+/src/f32-vrnd/gen/f32-vrndd-avx-u16.c
 // - external/xnnpack+/src/f32-vrnd/gen/f32-vrndd-avx512f-u16.c
 // - external/xnnpack+/src/f32-vrnd/gen/f32-vrndd-scalar-libm-u1.c
@@ -508,9 +504,7 @@
 // - external/xnnpack+/src/f32-vunary/gen/f32-vsqr-scalar.c
 // - external/xnnpack+/src/f32-vunary/gen/f32-vsqr-sse2.c
 // - external/xnnpack+/src/log.c
-// - external/xnnpack+/src/memory.c
 // - external/xnnpack+/src/microparams-init.c
-// - external/xnnpack+/src/mutex.c
 // - external/xnnpack+/src/qd8-f16-qb4w-gemm/gen/qd8-f16-qb4w-gemm-1x8c8-minmax-avx2.c
 // - external/xnnpack+/src/qd8-f16-qb4w-gemm/gen/qd8-f16-qb4w-gemm-3x8c8-minmax-avx2.c
 // - external/xnnpack+/src/qd8-f16-qc4w-gemm/gen/qd8-f16-qc4w-gemm-1x8c8-minmax-avx2-madd-prfm.c
@@ -961,10 +955,10 @@
 #include <string.h>
 
 static const uint8_t xnn_build_identifier[] = {
-  148,   5, 105, 197, 100, 103, 237,  68,
-  187, 178, 174, 198, 125, 155, 232,  30,
-  119, 251, 111, 140, 128, 138, 239, 243,
-  255, 126,  27, 130, 105, 187, 194, 112
+   12,  22, 222,  18, 189, 150, 218, 146,
+  154, 210,  20,  25,  31,  16, 206, 242,
+   49,  58,  83,   0,  18,  91, 126,  53,
+  116, 131,  35, 169, 153, 175, 107,  45
 };
 
 size_t xnn_experimental_get_build_identifier_size() {
diff --git a/third_party/xnnpack/src b/third_party/xnnpack/src
index 84f98c0..888719e 160000
--- a/third_party/xnnpack/src
+++ b/third_party/xnnpack/src
@@ -1 +1 @@
-Subproject commit 84f98c030577fc1d8c3a5fcc703f765fd8a976be
+Subproject commit 888719e9a4b11251d03ad2d6526da7f2e2b57308
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 5f084cd..31d1f15 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -43818,6 +43818,12 @@
   </description>
 </action>
 
+<action name="SystemContextMenu_Opened">
+  <owner>corising@chromium.org</owner>
+  <owner>top-chrome-desktop-ui@google.com</owner>
+  <description>The user opened the system menu context.</description>
+</action>
+
 <action name="Tab.PinnedTabToastClosedAfterConfirmation">
   <owner>dljames@google.com</owner>
   <owner>chrome-desktop-ui@google.com</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index dc11747..5ab3c4e75 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -9116,6 +9116,7 @@
   <int value="-1797183993" label="LacrosMergeIcuDataFile:enabled"/>
   <int value="-1796537928"
       label="CustomizeChromeWallpaperSearchButton:enabled"/>
+  <int value="-1795137192" label="AccessibilityManifestV3GoogleTts:disabled"/>
   <int value="-1794801189" label="VcStudioLook:disabled"/>
   <int value="-1794783272" label="PdfViewerDocumentProperties:disabled"/>
   <int value="-1793587118"
@@ -9836,6 +9837,7 @@
       label="HoldingSpaceWallpaperNudgeForceEligibility:disabled"/>
   <int value="-1520645293" label="InterestFeedNoticeCardAutoDismiss:disabled"/>
   <int value="-1520630395" label="DefaultPassthroughCommandDecoder:disabled"/>
+  <int value="-1520542714" label="AccessibilityManifestV3GoogleTts:enabled"/>
   <int value="-1517929127" label="DelegatedCompositing:enabled"/>
   <int value="-1517518406" label="force-update-menu-type"/>
   <int value="-1516955483" label="AutoDisableAccessibility:enabled"/>
diff --git a/tools/metrics/histograms/metadata/accessibility/histograms.xml b/tools/metrics/histograms/metadata/accessibility/histograms.xml
index 5e69c38..60626827 100644
--- a/tools/metrics/histograms/metadata/accessibility/histograms.xml
+++ b/tools/metrics/histograms/metadata/accessibility/histograms.xml
@@ -3053,7 +3053,7 @@
 </histogram>
 
 <histogram name="Accessibility.ReadAnything.SpeechStopSource"
-    enum="ReadAnythingSpeechError" expires_after="2026-06-01">
+    enum="ReadAnythingSpeechStopSource" expires_after="2026-06-01">
   <owner>kristislee@google.com</owner>
   <owner>komo-eng@google.com</owner>
   <summary>Records when speech stops and why.</summary>
diff --git a/tools/metrics/histograms/metadata/arc/histograms.xml b/tools/metrics/histograms/metadata/arc/histograms.xml
index b866b44..a32c57d 100644
--- a/tools/metrics/histograms/metadata/arc/histograms.xml
+++ b/tools/metrics/histograms/metadata/arc/histograms.xml
@@ -1655,7 +1655,7 @@
 </histogram>
 
 <histogram name="Arc.Net.Qos.QosSocketPercentage" units="%"
-    expires_after="2025-07-13">
+    expires_after="2026-07-13">
   <owner>chuweih@google.com</owner>
   <owner>cros-network-metrics@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/download/enums.xml b/tools/metrics/histograms/metadata/download/enums.xml
index a360fc1..40ad27ee 100644
--- a/tools/metrics/histograms/metadata/download/enums.xml
+++ b/tools/metrics/histograms/metadata/download/enums.xml
@@ -26,6 +26,13 @@
 
 <enums>
 
+<enum name="AndroidDownloadWarningBypassDialogEvent">
+  <int value="0" label="Dialog shown"/>
+  <int value="1" label="User validated download"/>
+  <int value="2" label="User tapped Learn More"/>
+  <int value="3" label="Dialog dismissed"/>
+</enum>
+
 <enum name="BooleanOpenFromDuplicates">
   <int value="0" label="Created New file"/>
   <int value="1" label="Opened existing duplicate"/>
diff --git a/tools/metrics/histograms/metadata/download/histograms.xml b/tools/metrics/histograms/metadata/download/histograms.xml
index 913cd718c..fa0c1dd 100644
--- a/tools/metrics/histograms/metadata/download/histograms.xml
+++ b/tools/metrics/histograms/metadata/download/histograms.xml
@@ -133,6 +133,17 @@
   </summary>
 </histogram>
 
+<histogram name="Download.Android.WarningBypassDialog.Events"
+    enum="AndroidDownloadWarningBypassDialogEvent" expires_after="2025-11-30">
+  <owner>chlily@chromium.org</owner>
+  <owner>chrome-counter-abuse-alerts@google.com</owner>
+  <summary>
+    Records events and user interactions with the download warning bypass dialog
+    on Android. Records when the dialog is shown, or when the user takes an
+    action or dismisses the dialog.
+  </summary>
+</histogram>
+
 <histogram name="Download.AndroidDownload.FileExtension"
     enum="SBClientDownloadExtensions" expires_after="2025-10-17">
   <owner>chlily@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/enterprise/enums.xml b/tools/metrics/histograms/metadata/enterprise/enums.xml
index a8448b6..8f06c50 100644
--- a/tools/metrics/histograms/metadata/enterprise/enums.xml
+++ b/tools/metrics/histograms/metadata/enterprise/enums.xml
@@ -2235,6 +2235,7 @@
   <int value="1364" label="LocalNetworkAccessRestrictionsEnabled"/>
   <int value="1365" label="PrefetchWithServiceWorkerEnabled"/>
   <int value="1366" label="AIModeSearchSuggestSettings"/>
+  <int value="1367" label="AIModeSettings"/>
 </enum>
 
 <enum name="EnterprisePoliciesSources">
diff --git a/tools/metrics/histograms/metadata/glic/enums.xml b/tools/metrics/histograms/metadata/glic/enums.xml
index d93b6eb..b074423 100644
--- a/tools/metrics/histograms/metadata/glic/enums.xml
+++ b/tools/metrics/histograms/metadata/glic/enums.xml
@@ -106,6 +106,19 @@
 
 <!-- LINT.ThenChange(//chrome/browser/glic/glic_metrics.h:AttachChangeReason) -->
 
+<!-- LINT.IfChange(GlicBrowserActiveState) -->
+
+<enum name="GlicBrowserActiveState">
+  <int value="0" label="Browser active"/>
+  <int value="1" label="Browser recently active 1 to 5s"/>
+  <int value="2" label="Browser recently active 5 to 10s"/>
+  <int value="3" label="Browser recently active 10 to 30s"/>
+  <int value="4" label="Browser inactive"/>
+  <int value="5" label="Browser hidden"/>
+</enum>
+
+<!-- LINT.ThenChange(//chrome/browser/glic/glic_metrics.cc:BrowserActiveState) -->
+
 <!-- LINT.IfChange(GlicDetailedWebClientState) -->
 
 <enum name="GlicDetailedWebClientState">
diff --git a/tools/metrics/histograms/metadata/glic/histograms.xml b/tools/metrics/histograms/metadata/glic/histograms.xml
index 6b74460..509d32bd 100644
--- a/tools/metrics/histograms/metadata/glic/histograms.xml
+++ b/tools/metrics/histograms/metadata/glic/histograms.xml
@@ -697,6 +697,16 @@
   </summary>
 </histogram>
 
+<histogram name="Glic.Session.InputSubmit.BrowserActiveState"
+    enum="GlicBrowserActiveState" expires_after="2026-03-15">
+  <owner>harringtond@chromium.org</owner>
+  <owner>carlosk@chromium.org</owner>
+  <summary>
+    Records the browser active state when user input is submitted in a Glic
+    session.
+  </summary>
+</histogram>
+
 <histogram name="Glic.Session.Open.Attached" enum="Boolean"
     expires_after="2026-01-15">
   <owner>carlosk@chromium.org</owner>
@@ -707,6 +717,15 @@
   </summary>
 </histogram>
 
+<histogram name="Glic.Session.Open.BrowserActiveState"
+    enum="GlicBrowserActiveState" expires_after="2026-03-15">
+  <owner>harringtond@chromium.org</owner>
+  <owner>carlosk@chromium.org</owner>
+  <summary>
+    Records the browser active state when a Glic session starts.
+  </summary>
+</histogram>
+
 <histogram name="Glic.Session.Open.InvocationSource"
     enum="GlicInvocationSource" expires_after="2026-01-15">
   <owner>carlosk@chromium.org</owner>
@@ -727,6 +746,15 @@
   </summary>
 </histogram>
 
+<histogram name="Glic.Session.ResponseStart.BrowserActiveState"
+    enum="GlicBrowserActiveState" expires_after="2026-03-15">
+  <owner>harringtond@chromium.org</owner>
+  <owner>carlosk@chromium.org</owner>
+  <summary>
+    Records the browser active state when a Glic response starts rendering.
+  </summary>
+</histogram>
+
 <histogram name="Glic.ShouldPreload" enum="Boolean" expires_after="2026-01-15">
   <owner>dullweber@chromium.org</owner>
   <owner>vollick@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/gpu/histograms.xml b/tools/metrics/histograms/metadata/gpu/histograms.xml
index 7d5f7792..c56ba9d 100644
--- a/tools/metrics/histograms/metadata/gpu/histograms.xml
+++ b/tools/metrics/histograms/metadata/gpu/histograms.xml
@@ -1788,8 +1788,7 @@
   <owner>chrome-gpu-metric-alerts@chromium.org</owner>
   <summary>
     An enum status result for attempted delegated compositing (success or
-    failure reason) recorded every drawn frame. Currently only recorded for
-    LaCros delegated compositing.
+    failure reason) recorded every drawn frame.
   </summary>
 </histogram>
 
@@ -1875,7 +1874,7 @@
   <summary>
     Time spent computing the number of active File Descriptors. This is logged
     once every 5 minutes as the cost of this computation is estimated to be at
-    least 1ms. Currently only recorded for LaCros delegated compositing.
+    least 1ms.
 
     Warning: This metric does not include reports from clients with
     low-resolution clocks.
@@ -1889,7 +1888,6 @@
   <summary>
     {FdStat} File Descriptors for the GPU process. This is logged once every 5
     minutes as the cost of this computation is estimated to be at least 1ms.
-    Currently only recorded for LaCros delegated compositing.
   </summary>
   <token key="FdStat">
     <variant name="NumActive" summary="Current number of active"/>
diff --git a/tools/metrics/histograms/metadata/history/histograms.xml b/tools/metrics/histograms/metadata/history/histograms.xml
index 29bdea66..0522170 100644
--- a/tools/metrics/histograms/metadata/history/histograms.xml
+++ b/tools/metrics/histograms/metadata/history/histograms.xml
@@ -2717,6 +2717,23 @@
   </summary>
 </histogram>
 
+<histogram name="History.MostVisitedTilesVisualDeduplication" units="tiles"
+    expires_after="2025-11-30">
+  <owner>hitarthkothari@google.com</owner>
+  <owner>ckitagawa@chromium.org</owner>
+  <summary>
+    Records the number of most visited site suggestions that are removed because
+    they are considered visual duplicates of a higher-scoring suggestion. The
+    deduplication logic is triggered when two tiles share the same hostname and
+    have titles that are identical within the first 10 characters.
+
+    This metric is recorded on the first call to
+    VisitSegmentDatabase::QuerySegmentUsage during a browser session where the
+    visual deduplication feature is active. This function is called each time
+    the user loads the New Tab Page (NTP).
+  </summary>
+</histogram>
+
 <histogram name="History.QueryAppDuration" units="ms"
     expires_after="2025-11-30">
   <owner>jinsukkim@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/optimization/histograms.xml b/tools/metrics/histograms/metadata/optimization/histograms.xml
index 5652ecc..6476a7b3 100644
--- a/tools/metrics/histograms/metadata/optimization/histograms.xml
+++ b/tools/metrics/histograms/metadata/optimization/histograms.xml
@@ -165,6 +165,8 @@
   <variant name="SegmentationFedCmUser" summary="Segmentation: FedCM user"/>
   <variant name="SegmentationFeedUser"
       summary="Segmentation: Feed usage in Chrome"/>
+  <variant name="SegmentationIosDefaultBrowserPromo"
+      summary="Segmentation: Ios Default Browser Promo"/>
   <variant name="SegmentationIosModuleRanker"
       summary="Segmentation: Ios module ranker"/>
   <variant name="SegmentationMetricsClustering"
diff --git a/tools/metrics/histograms/metadata/sb_client/histograms.xml b/tools/metrics/histograms/metadata/sb_client/histograms.xml
index 9168978..5757bd6 100644
--- a/tools/metrics/histograms/metadata/sb_client/histograms.xml
+++ b/tools/metrics/histograms/metadata/sb_client/histograms.xml
@@ -239,7 +239,7 @@
 </histogram>
 
 <histogram name="SBClientDownload.PPAPIDownloadRequest.NetworkResult"
-    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2025-06-13">
+    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2026-06-13">
   <owner>chlily@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/segmentation_platform/enums.xml b/tools/metrics/histograms/metadata/segmentation_platform/enums.xml
index 1f915cde..55863efb3 100644
--- a/tools/metrics/histograms/metadata/segmentation_platform/enums.xml
+++ b/tools/metrics/histograms/metadata/segmentation_platform/enums.xml
@@ -103,6 +103,7 @@
   <int value="46" label="UrlVisitResumptionRanker"/>
   <int value="50" label="MetricsClustering"/>
   <int value="58" label="FedCmUser"/>
+  <int value="63" label="IOSDefaultBrowserPromo"/>
 </enum>
 
 <enum name="SegmentationPlatformTrainingDataCollectionEvent">
diff --git a/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml b/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml
index c7fef15..41b9424 100644
--- a/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml
+++ b/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml
@@ -121,6 +121,7 @@
   <variant name="FeedUser"/>
   <variant name="FrequentFeatureUserSegment"/>
   <variant name="IntentionalUserSegment"/>
+  <variant name="IosDefaultBrowserPromo"/>
   <variant name="IosModuleRanker"/>
   <variant name="IosModuleRankerTest"/>
   <variant name="MetricsClustering"/>
diff --git a/tools/metrics/histograms/metadata/task_manager/OWNERS b/tools/metrics/histograms/metadata/task_manager/OWNERS
index 4350c85..84cc738 100644
--- a/tools/metrics/histograms/metadata/task_manager/OWNERS
+++ b/tools/metrics/histograms/metadata/task_manager/OWNERS
@@ -2,4 +2,3 @@
 
 # Prefer sending CLs to the owners listed below.
 # Use chromium-metrics-reviews@google.com as a backup.
-michelefan@chromium.org
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 4b9c077..388c409 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -6,7 +6,7 @@
         },
         "win": {
             "hash": "04cbab8845b573ce0310a7cf9350291bb5219411",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/4f93293dbb8f7728c40e34ce8098892a35988790/trace_processor_shell.exe"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/557fafc38445fdde33d19b1cf2d784686e51d120/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "99f971ca131f6d11c73f4b918099d434bdd8093c",
@@ -21,8 +21,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v50.1/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "3516ed04e4cd3e7730785b16937bb8bb85625db1",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/4f93293dbb8f7728c40e34ce8098892a35988790/trace_processor_shell"
+            "hash": "32bd605dd2deaff435271f3582cd73df7ca99b08",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/9b02a69cbe47e5e625cd712a20c0eb4d04fca4ca/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/pgo/generate_profile.py b/tools/pgo/generate_profile.py
index 6042a50..eb3eca06 100755
--- a/tools/pgo/generate_profile.py
+++ b/tools/pgo/generate_profile.py
@@ -569,10 +569,9 @@
             '--story-tag-filter=motionmark_fixed_2_seconds',
         ]
 
-        if platform == 'desktop':
-            benchmarks.append(
-                Benchmark('motionmark', motionmark_benchmark_args))
-        else:
+        # Android arm32 runs on older phones so these benchmarks should only run
+        # for arm64.
+        if platform == 'mobile' and '64' in args.android_browser:
             # Exercise the Skia Graphite/Dawn/Vulkan path.
             benchmarks.append(
                 Benchmark('motionmark_graphite_dawn_vk',
@@ -594,6 +593,9 @@
                           disable_features=[
                               'Vulkan', 'SkiaGraphite', 'DefaultANGLEVulkan'
                           ]))
+        else:
+            benchmarks.append(
+                Benchmark('motionmark', motionmark_benchmark_args))
 
     fail_count = run_benchmarks(benchmarks, args)
     if fail_count:
diff --git a/ui/accessibility/accessibility_features.cc b/ui/accessibility/accessibility_features.cc
index fb8fbb8..e958fcda4 100644
--- a/ui/accessibility/accessibility_features.cc
+++ b/ui/accessibility/accessibility_features.cc
@@ -347,6 +347,14 @@
       ::features::kAccessibilityManifestV3EspeakNGTts);
 }
 
+BASE_FEATURE(kAccessibilityManifestV3GoogleTts,
+             "AccessibilityManifestV3GoogleTts",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+bool IsAccessibilityManifestV3EnabledForGoogleTts() {
+  return base::FeatureList::IsEnabled(
+      ::features::kAccessibilityManifestV3GoogleTts);
+}
+
 BASE_FEATURE(kAccessibilityManifestV3AccessibilityCommon,
              "AccessibilityManifestV3AccessibilityCommon",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/ui/accessibility/accessibility_features.h b/ui/accessibility/accessibility_features.h
index 5f8e0770..c501bc4 100644
--- a/ui/accessibility/accessibility_features.h
+++ b/ui/accessibility/accessibility_features.h
@@ -245,6 +245,9 @@
 AX_BASE_EXPORT BASE_DECLARE_FEATURE(kAccessibilityManifestV3EspeakNGTts);
 AX_BASE_EXPORT bool IsAccessibilityManifestV3EnabledForEspeakNGTts();
 
+AX_BASE_EXPORT BASE_DECLARE_FEATURE(kAccessibilityManifestV3GoogleTts);
+AX_BASE_EXPORT bool IsAccessibilityManifestV3EnabledForGoogleTts();
+
 AX_BASE_EXPORT BASE_DECLARE_FEATURE(kAccessibilityManifestV3SelectToSpeak);
 AX_BASE_EXPORT bool IsAccessibilityManifestV3EnabledForSelectToSpeak();
 
diff --git a/ui/accessibility/platform/browser_accessibility_mac.mm b/ui/accessibility/platform/browser_accessibility_mac.mm
index d53e0e0..77cf0af 100644
--- a/ui/accessibility/platform/browser_accessibility_mac.mm
+++ b/ui/accessibility/platform/browser_accessibility_mac.mm
@@ -7,7 +7,6 @@
 #import <Cocoa/Cocoa.h>
 
 #include "base/apple/foundation_util.h"
-#include "base/debug/stack_trace.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/scoped_policy.h"
 #import "base/task/single_thread_task_runner.h"
diff --git a/ui/accessibility/platform/inspect/ax_element_wrapper_mac.mm b/ui/accessibility/platform/inspect/ax_element_wrapper_mac.mm
index 49dc466..f2e316f 100644
--- a/ui/accessibility/platform/inspect/ax_element_wrapper_mac.mm
+++ b/ui/accessibility/platform/inspect/ax_element_wrapper_mac.mm
@@ -15,7 +15,6 @@
 #include "base/apple/scoped_cftyperef.h"
 #include "base/compiler_specific.h"
 #include "base/containers/fixed_flat_set.h"
-#include "base/debug/stack_trace.h"
 #include "base/functional/callback.h"
 #include "base/logging.h"
 #include "base/strings/pattern.h"
diff --git a/ui/accessibility/platform/inspect/ax_inspect_utils_mac.mm b/ui/accessibility/platform/inspect/ax_inspect_utils_mac.mm
index ea885a1..f1f03ab 100644
--- a/ui/accessibility/platform/inspect/ax_inspect_utils_mac.mm
+++ b/ui/accessibility/platform/inspect/ax_inspect_utils_mac.mm
@@ -12,7 +12,6 @@
 #include "base/apple/bridging.h"
 #include "base/apple/foundation_util.h"
 #include "base/containers/fixed_flat_set.h"
-#include "base/debug/stack_trace.h"
 #include "base/functional/callback.h"
 #include "base/logging.h"
 #include "base/memory/scoped_policy.h"
diff --git a/ui/android/java/res/values/one_off_colors.xml b/ui/android/java/res/values/one_off_colors.xml
index ef089b0..90aec8b 100644
--- a/ui/android/java/res/values/one_off_colors.xml
+++ b/ui/android/java/res/values/one_off_colors.xml
@@ -62,6 +62,17 @@
     <color name="tab_group_card_color_red_gm3">#FF8983</color>
     <color name="tab_group_card_color_yellow_gm3">#FCBD00</color>
 
+    <!-- Tab group card text related colors. These should not be used elsewhere. -->
+    <color name="tab_group_card_text_color_grey_gm3">#1B1B1C</color>
+    <color name="tab_group_card_text_color_blue_gm3">#001944</color>
+    <color name="tab_group_card_text_color_cyan_gm3">#001F26</color>
+    <color name="tab_group_card_text_color_green_gm3">#002110</color>
+    <color name="tab_group_card_text_color_orange_gm3">#321200</color>
+    <color name="tab_group_card_text_color_pink_gm3">#3D0023</color>
+    <color name="tab_group_card_text_color_purple_gm3">#280255</color>
+    <color name="tab_group_card_text_color_red_gm3">#3A0907</color>
+    <color name="tab_group_card_text_color_yellow_gm3">#2F1400</color>
+
     <!-- Colors used in Google Pay icon -->
     <color name="google_pay_icon_grey_dark">#3C4043</color>
 
diff --git a/ui/base/interaction/interaction_sequence_unittest.cc b/ui/base/interaction/interaction_sequence_unittest.cc
index 54aa8ad..99c3f9e 100644
--- a/ui/base/interaction/interaction_sequence_unittest.cc
+++ b/ui/base/interaction/interaction_sequence_unittest.cc
@@ -7,7 +7,6 @@
 #include <optional>
 #include <sstream>
 
-#include "base/debug/stack_trace.h"
 #include "base/functional/callback_forward.h"
 #include "base/location.h"
 #include "base/logging.h"
diff --git a/ui/display/manager/display_manager.cc b/ui/display/manager/display_manager.cc
index 696c90c6..43b8fe2 100644
--- a/ui/display/manager/display_manager.cc
+++ b/ui/display/manager/display_manager.cc
@@ -17,7 +17,6 @@
 #include "base/auto_reset.h"
 #include "base/command_line.h"
 #include "base/containers/contains.h"
-#include "base/debug/stack_trace.h"
 #include "base/functional/bind.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn
index 9c8e7e1..cbfc0154 100644
--- a/ui/gl/BUILD.gn
+++ b/ui/gl/BUILD.gn
@@ -146,8 +146,6 @@
     "shader_tracking.h",
     "shared_gl_fence_egl.cc",
     "shared_gl_fence_egl.h",
-    "startup_trace.cc",
-    "startup_trace.h",
     "sync_control_vsync_provider.cc",
     "sync_control_vsync_provider.h",
     "trace_util.cc",
diff --git a/ui/gl/generate_bindings.py b/ui/gl/generate_bindings.py
index 294427f..886fa7fc 100755
--- a/ui/gl/generate_bindings.py
+++ b/ui/gl/generate_bindings.py
@@ -2976,7 +2976,6 @@
                    'ui/gl/gl_context.h',
                    'ui/gl/gl_implementation.h',
                    'ui/gl/gl_version_info.h',
-                   'ui/gl/startup_trace.h',
                    set_header_name ]
 
   includes_string = "\n".join(["#include \"{0}\"".format(h)
@@ -3022,7 +3021,8 @@
 void Driver%s::InitializeStaticBindings(GLGetProcAddressProc get_proc_address) {
 """ % set_name.upper())
 
-  file.write('  GPU_STARTUP_TRACE_EVENT("Driver%s::InitializeStaticBindings");'
+  file.write('  TRACE_EVENT("gpu,startup", '
+             '"Driver%s::InitializeStaticBindings");'
              '\n' % (set_name.upper()))
 
   def BindingsAreAllStatic(api_set_name):
@@ -3114,7 +3114,7 @@
   elif set_name == 'egl':
     file.write("""\
 void ClientExtensionsEGL::InitializeClientExtensionSettings() {
-  GPU_STARTUP_TRACE_EVENT("DriverEGL::InitializeClientExtensionSettings");
+  TRACE_EVENT("gpu,startup", "DriverEGL::InitializeClientExtensionSettings");
   std::string client_extensions(GetClientExtensions());
   [[maybe_unused]] gfx::ExtensionSet extensions(
       gfx::MakeExtensionSet(client_extensions));
@@ -3155,7 +3155,7 @@
 }
 
 void DisplayExtensionsEGL::InitializeExtensionSettings(EGLDisplay display) {
-  GPU_STARTUP_TRACE_EVENT("DriverEGL::InitializeExtensionSettings");
+  TRACE_EVENT("gpu,startup", "DriverEGL::InitializeExtensionSettings");
   std::string platform_extensions(GetPlatformExtensions(display));
   [[maybe_unused]] gfx::ExtensionSet extensions(
       gfx::MakeExtensionSet(platform_extensions));
diff --git a/ui/gl/gl_bindings_autogen_egl.cc b/ui/gl/gl_bindings_autogen_egl.cc
index 66162bc..9ec2edf1 100644
--- a/ui/gl/gl_bindings_autogen_egl.cc
+++ b/ui/gl/gl_bindings_autogen_egl.cc
@@ -18,7 +18,6 @@
 #include "ui/gl/gl_enums.h"
 #include "ui/gl/gl_implementation.h"
 #include "ui/gl/gl_version_info.h"
-#include "ui/gl/startup_trace.h"
 
 namespace gl {
 
@@ -26,7 +25,7 @@
 
 void DriverEGL::InitializeStaticBindings(
     GLGetProcAddressProc get_proc_address) {
-  GPU_STARTUP_TRACE_EVENT("DriverEGL::InitializeStaticBindings");
+  TRACE_EVENT("gpu,startup", "DriverEGL::InitializeStaticBindings");
   fn.eglAcquireExternalContextANGLEFn =
       reinterpret_cast<eglAcquireExternalContextANGLEProc>(
           get_proc_address("eglAcquireExternalContextANGLE"));
@@ -266,7 +265,7 @@
 }
 
 void ClientExtensionsEGL::InitializeClientExtensionSettings() {
-  GPU_STARTUP_TRACE_EVENT("DriverEGL::InitializeClientExtensionSettings");
+  TRACE_EVENT("gpu,startup", "DriverEGL::InitializeClientExtensionSettings");
   std::string client_extensions(GetClientExtensions());
   [[maybe_unused]] gfx::ExtensionSet extensions(
       gfx::MakeExtensionSet(client_extensions));
@@ -307,7 +306,7 @@
 }
 
 void DisplayExtensionsEGL::InitializeExtensionSettings(EGLDisplay display) {
-  GPU_STARTUP_TRACE_EVENT("DriverEGL::InitializeExtensionSettings");
+  TRACE_EVENT("gpu,startup", "DriverEGL::InitializeExtensionSettings");
   std::string platform_extensions(GetPlatformExtensions(display));
   [[maybe_unused]] gfx::ExtensionSet extensions(
       gfx::MakeExtensionSet(platform_extensions));
diff --git a/ui/gl/gl_bindings_autogen_gl.cc b/ui/gl/gl_bindings_autogen_gl.cc
index 74ed5496..53554e94 100644
--- a/ui/gl/gl_bindings_autogen_gl.cc
+++ b/ui/gl/gl_bindings_autogen_gl.cc
@@ -18,12 +18,11 @@
 #include "ui/gl/gl_gl_api_implementation.h"
 #include "ui/gl/gl_implementation.h"
 #include "ui/gl/gl_version_info.h"
-#include "ui/gl/startup_trace.h"
 
 namespace gl {
 
 void DriverGL::InitializeStaticBindings(GLGetProcAddressProc get_proc_address) {
-  GPU_STARTUP_TRACE_EVENT("DriverGL::InitializeStaticBindings");
+  TRACE_EVENT("gpu,startup", "DriverGL::InitializeStaticBindings");
   fn.glActiveTextureFn = reinterpret_cast<glActiveTextureProc>(
       get_proc_address("glActiveTexture"));
   fn.glAttachShaderFn =
diff --git a/ui/gl/gl_display.cc b/ui/gl/gl_display.cc
index 70253d3..c4034e3 100644
--- a/ui/gl/gl_display.cc
+++ b/ui/gl/gl_display.cc
@@ -19,6 +19,7 @@
 #include "base/strings/string_split.h"
 #include "base/synchronization/atomic_flag.h"
 #include "base/system/sys_info.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "gl_display.h"
 #include "gl_switches.h"
@@ -31,7 +32,6 @@
 #include "ui/gl/gl_features.h"
 #include "ui/gl/gl_implementation.h"
 #include "ui/gl/gl_surface.h"
-#include "ui/gl/startup_trace.h"
 
 #if BUILDFLAG(IS_ANDROID)
 #include "base/android/build_info.h"
@@ -64,7 +64,7 @@
     const std::vector<std::string>& enabled_features,
     const std::vector<std::string>& disabled_features,
     const std::vector<EGLAttrib>& extra_display_attribs) {
-  GPU_STARTUP_TRACE_EVENT("gl_display::GetPlatformANGLEDisplay");
+  TRACE_EVENT("gpu,startup", "gl_display::GetPlatformANGLEDisplay");
   std::vector<EGLAttrib> display_attribs(extra_display_attribs);
 
   display_attribs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
@@ -576,7 +576,7 @@
                                      std::vector<DisplayType> init_displays,
                                      EGLDisplayPlatform native_display,
                                      gl::GLDisplayEGL* existing_display) {
-  GPU_STARTUP_TRACE_EVENT("gl::GLDisplayEGL::InitializeDisplay");
+  TRACE_EVENT("gpu,startup", "gl::GLDisplayEGL::InitializeDisplay");
   if (display_ != EGL_NO_DISPLAY)
     return true;
 
@@ -630,7 +630,7 @@
     }
 
     {
-      GPU_STARTUP_TRACE_EVENT("eglInitializeFn display");
+      TRACE_EVENT("gpu,startup", "eglInitializeFn display");
       if (!eglInitialize(display, nullptr, nullptr)) {
         bool is_last = disp_index == init_displays.size() - 1;
 
@@ -675,7 +675,7 @@
 }
 
 void GLDisplayEGL::InitializeCommon(bool for_testing) {
-  GPU_STARTUP_TRACE_EVENT("gl::GLDisplayEGL::InitializeCommon");
+  TRACE_EVENT("gpu,startup", "gl::GLDisplayEGL::InitializeCommon");
   // According to https://source.android.com/compatibility/android-cdd.html the
   // EGL_IMG_context_priority extension is mandatory for Virtual Reality High
   // Performance support, but due to a bug in Android Nougat the extension
diff --git a/ui/gl/gl_switches.cc b/ui/gl/gl_switches.cc
index 3da4914..2a196ec 100644
--- a/ui/gl/gl_switches.cc
+++ b/ui/gl/gl_switches.cc
@@ -4,11 +4,11 @@
 
 #include "ui/gl/gl_switches.h"
 
+#include "base/trace_event/trace_event.h"
 #include "build/android_buildflags.h"
 #include "build/build_config.h"
 #include "ui/gl/buildflags.h"
 #include "ui/gl/gl_display_manager.h"
-#include "ui/gl/startup_trace.h"
 
 #if BUILDFLAG(IS_ANDROID)
 #include "base/android/build_info.h"
@@ -326,7 +326,7 @@
     (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID))
   angle::SystemInfo system_info;
   {
-    GPU_STARTUP_TRACE_EVENT("angle::GetSystemInfoVulkan");
+    TRACE_EVENT("gpu,startup", "angle::GetSystemInfoVulkan");
     if (!angle::GetSystemInfoVulkan(&system_info)) {
       return false;
     }
diff --git a/ui/gl/init/gl_display_initializer.cc b/ui/gl/init/gl_display_initializer.cc
index 5c81113..8b3a196 100644
--- a/ui/gl/init/gl_display_initializer.cc
+++ b/ui/gl/init/gl_display_initializer.cc
@@ -6,12 +6,12 @@
 
 #include "base/command_line.h"
 #include "base/containers/contains.h"
+#include "base/trace_event/trace_event.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_features.h"
 #include "ui/gl/gl_implementation.h"
 #include "ui/gl/gl_utils.h"
 #include "ui/gl/init/gl_factory.h"
-#include "ui/gl/startup_trace.h"
 
 namespace gl::init {
 
@@ -34,7 +34,7 @@
                         bool supports_angle_metal,
                         const base::CommandLine* command_line,
                         std::vector<DisplayType>* init_displays) {
-  GPU_STARTUP_TRACE_EVENT("gl_display_initializer::GetEGLInitDisplays");
+  TRACE_EVENT("gpu,startup", "gl_display_initializer::GetEGLInitDisplays");
   // Check which experiment groups we're in. Check these early in the function
   // so that finch assigns a group before the final decision to use the API is
   // made. If we check too late, it will appear that some users are missing from
diff --git a/ui/gl/init/gl_factory.cc b/ui/gl/init/gl_factory.cc
index 0b975c7..ab18aa5 100644
--- a/ui/gl/init/gl_factory.cc
+++ b/ui/gl/init/gl_factory.cc
@@ -20,7 +20,6 @@
 #include "ui/gl/gl_surface.h"
 #include "ui/gl/gl_utils.h"
 #include "ui/gl/init/gl_initializer.h"
-#include "ui/gl/startup_trace.h"
 
 #if BUILDFLAG(IS_OZONE)
 #include "ui/base/ui_base_features.h"
@@ -110,7 +109,6 @@
                                             gl::GpuPreference gpu_preference) {
   TRACE_EVENT1("gpu,startup", "gl::init::InitializeGLOneOffPlatformHelper",
                "init_extensions", init_extensions);
-  GPU_STARTUP_TRACE_EVENT("gl::init::InitializeGLOneOffPlatformHelper");
 
   const base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
   bool disable_gl_drawing = cmd->HasSwitch(switches::kDisableGLDrawingForTests);
@@ -122,7 +120,7 @@
 }  // namespace
 
 GLDisplay* InitializeGLOneOff(gl::GpuPreference gpu_preference) {
-  GPU_STARTUP_TRACE_EVENT("gl::init::InitializeOneOff");
+  TRACE_EVENT("gpu,startup", "gl::init::InitializeOneOff");
 
   if (!InitializeStaticGLBindingsOneOff())
     return nullptr;
@@ -137,7 +135,6 @@
                                           gl::GpuPreference gpu_preference) {
   TRACE_EVENT1("gpu,startup", "gl::init::InitializeNoExtensionsOneOff",
                "init_bindings", init_bindings);
-  GPU_STARTUP_TRACE_EVENT("gl::init::InitializeNoExtensionsOneOff");
   if (init_bindings) {
     if (!InitializeStaticGLBindingsOneOff())
       return nullptr;
@@ -151,7 +148,7 @@
 
 bool InitializeStaticGLBindingsOneOff() {
   DCHECK_EQ(kGLImplementationNone, GetGLImplementation());
-  GPU_STARTUP_TRACE_EVENT("gl::init::InitializeStaticGLBindingsOneOff");
+  TRACE_EVENT("gpu,startup", "gl::init::InitializeStaticGLBindingsOneOff");
 
   GLImplementationParts impl = GetRequestedGLImplementation();
   if (impl.gl == kGLImplementationDisabled) {
diff --git a/ui/gl/init/gl_initializer_android.cc b/ui/gl/init/gl_initializer_android.cc
index 854602ff..094037f 100644
--- a/ui/gl/init/gl_initializer_android.cc
+++ b/ui/gl/init/gl_initializer_android.cc
@@ -9,13 +9,13 @@
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/native_library.h"
+#include "base/trace_event/trace_event.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_display.h"
 #include "ui/gl/gl_egl_api_implementation.h"
 #include "ui/gl/gl_gl_api_implementation.h"
 #include "ui/gl/gl_utils.h"
 #include "ui/gl/init/gl_display_initializer.h"
-#include "ui/gl/startup_trace.h"
 
 namespace gl {
 namespace init {
@@ -25,14 +25,14 @@
 bool InitializeStaticNativeEGLInternal() {
   base::NativeLibrary gles_library;
   {
-    GPU_STARTUP_TRACE_EVENT("Load gles_library");
+    TRACE_EVENT("gpu,startup", "Load gles_library");
     gles_library = LoadLibraryAndPrintError("libGLESv2.so");
   }
   if (!gles_library)
     return false;
   base::NativeLibrary egl_library;
   {
-    GPU_STARTUP_TRACE_EVENT("Load egl_library");
+    TRACE_EVENT("gpu,startup", "Load egl_library");
     egl_library = LoadLibraryAndPrintError("libEGL.so");
   }
   if (!egl_library) {
diff --git a/ui/gl/init/gl_initializer_mac.cc b/ui/gl/init/gl_initializer_mac.cc
index 712aca96..a6c99fb 100644
--- a/ui/gl/init/gl_initializer_mac.cc
+++ b/ui/gl/init/gl_initializer_mac.cc
@@ -12,6 +12,7 @@
 #include "base/native_library.h"
 #include "base/path_service.h"
 #include "base/threading/thread_restrictions.h"
+#include "base/trace_event/trace_event.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_display.h"
@@ -22,7 +23,6 @@
 #include "ui/gl/gl_utils.h"
 #include "ui/gl/gpu_switching_manager.h"
 #include "ui/gl/init/gl_display_initializer.h"
-#include "ui/gl/startup_trace.h"
 
 namespace gl {
 namespace init {
@@ -50,7 +50,7 @@
   base::FilePath glesv2_path = base_dir.Append(kGLESv2ANGLELibraryName);
   base::NativeLibrary gles_library;
   {
-    GPU_STARTUP_TRACE_EVENT("Load gles_library");
+    TRACE_EVENT("gpu,startup", "Load gles_library");
     gles_library = LoadLibraryAndPrintError(glesv2_path);
   }
   if (!gles_library) {
@@ -60,7 +60,7 @@
   base::FilePath egl_path = base_dir.Append(kEGLANGLELibraryName);
   base::NativeLibrary egl_library;
   {
-    GPU_STARTUP_TRACE_EVENT("Load egl_library");
+    TRACE_EVENT("gpu,startup", "Load egl_library");
     egl_library = LoadLibraryAndPrintError(egl_path);
   }
   if (!egl_library) {
diff --git a/ui/gl/init/gl_initializer_win.cc b/ui/gl/init/gl_initializer_win.cc
index eabf2c3..918dda1 100644
--- a/ui/gl/init/gl_initializer_win.cc
+++ b/ui/gl/init/gl_initializer_win.cc
@@ -23,7 +23,6 @@
 #include "ui/gl/gl_gl_api_implementation.h"
 #include "ui/gl/gl_utils.h"
 #include "ui/gl/init/gl_display_initializer.h"
-#include "ui/gl/startup_trace.h"
 #include "ui/gl/vsync_provider_win.h"
 
 namespace gl {
@@ -35,7 +34,7 @@
 
 bool LoadD3DXLibrary(const base::FilePath& module_path,
                      const base::FilePath::StringType& name) {
-  GPU_STARTUP_TRACE_EVENT(__func__);
+  TRACE_EVENT("gpu,startup", __func__);
   base::NativeLibrary library =
       base::LoadNativeLibrary(module_path.Append(name), nullptr);
   if (!library) {
@@ -71,7 +70,7 @@
   // search path, it will get loaded instead.
   base::NativeLibrary gles_library;
   {
-    GPU_STARTUP_TRACE_EVENT("Load gles_library");
+    TRACE_EVENT("gpu,startup", "Load gles_library");
     gles_library =
         base::LoadNativeLibrary(gles_path.Append(L"libglesv2.dll"), nullptr);
   }
@@ -84,7 +83,7 @@
   // GetProcAddress on both the EGL and GLES2 DLLs.
   base::NativeLibrary egl_library;
   {
-    GPU_STARTUP_TRACE_EVENT("Load egl_library");
+    TRACE_EVENT("gpu,startup", "Load egl_library");
     egl_library =
         base::LoadNativeLibrary(gles_path.Append(L"libegl.dll"), nullptr);
   }
diff --git a/ui/gl/startup_trace.cc b/ui/gl/startup_trace.cc
deleted file mode 100644
index 7a31db7e..0000000
--- a/ui/gl/startup_trace.cc
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/gl/startup_trace.h"
-
-#include "base/no_destructor.h"
-#include "base/task/sequenced_task_runner.h"
-#include "base/trace_event/trace_event.h"
-
-namespace gl {
-namespace {
-constexpr char kTraceCategory[] = "gpu,startup";
-}  // namespace
-
-std::atomic<bool> StartupTrace::startup_in_progress_ = false;
-
-// static
-StartupTrace* StartupTrace::GetInstance() {
-  static base::NoDestructor<StartupTrace> g_instance;
-  return g_instance.get();
-}
-
-// static
-void StartupTrace::Startup() {
-  startup_in_progress_.store(true);
-}
-
-// static
-void StartupTrace::StarupDone() {
-  startup_in_progress_.store(false);
-  gl::StartupTrace::GetInstance()->RecordAndClearStages();
-}
-
-StartupTrace::StartupTrace() {
-  BindToCurrentThread();
-}
-
-StartupTrace::~StartupTrace() = default;
-
-void StartupTrace::BindToCurrentThread() {
-  if (!task_runner_ && base::SequencedTaskRunner::HasCurrentDefault()) {
-    task_runner_ = base::SequencedTaskRunner::GetCurrentDefault();
-  }
-}
-
-StartupTrace::ScopedStage::ScopedStage(size_t size) : size(size) {}
-StartupTrace::ScopedStage::~ScopedStage() {
-  if (size) {
-    StartupTrace::GetInstance()->stages_[size - 1].end = base::TimeTicks::Now();
-  }
-}
-
-StartupTrace::ScopedStage StartupTrace::AddStage(const char* name) {
-  DCHECK(!base::SequencedTaskRunner::HasCurrentDefault() ||
-         task_runner_->RunsTasksInCurrentSequence());
-  stages_.emplace_back(name, base::TimeTicks::Now(), base::TimeTicks());
-  return StartupTrace::ScopedStage{stages_.size()};
-}
-
-void StartupTrace::RecordAndClearStages() {
-  auto t = perfetto::ThreadTrack::Current();
-
-  for (auto& stage : stages_) {
-    TRACE_EVENT_BEGIN(kTraceCategory, perfetto::StaticString{stage.name}, t,
-                      stage.start);
-    TRACE_EVENT_END(kTraceCategory, t, stage.end);
-  }
-
-  stages_.clear();
-}
-
-}  // namespace gl
diff --git a/ui/gl/startup_trace.h b/ui/gl/startup_trace.h
deleted file mode 100644
index d3bae4e7..0000000
--- a/ui/gl/startup_trace.h
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_GL_STARTUP_TRACE_H_
-#define UI_GL_STARTUP_TRACE_H_
-
-#include <vector>
-
-#include "base/memory/scoped_refptr.h"
-#include "base/time/time.h"
-#include "base/trace_event/trace_event.h"
-#include "ui/gl/gl_export.h"
-
-namespace base {
-class SequencedTaskRunner;
-}
-
-namespace gl {
-
-// This class provides a method to trace the initialization of GPU process. When
-// tracing instance is not initialized it will accumulate stages with start and
-// end time stamp so they can emit traces once tracing inits. Calling this after
-// `StartupDone()` will not add a stage. The caveat is that it cannot know
-// before-hand whether the tracing category is on or not so the stage is
-// recorded nonetheless.
-//
-// Example:
-//   GPU_STARTUP_TRACE_EVENT(__func__);
-//
-// Note: Currently it's only safe to invoke `GPU_STARTUP_TRACE_EVENT` from the
-// gpu main thread between `Startup()` and `StartupDone()`.
-class GL_EXPORT StartupTrace {
- public:
-  struct Stage {
-    const char* name = nullptr;
-    const base::TimeTicks start;
-    base::TimeTicks end;
-  };
-
-  class GL_EXPORT ScopedStage {
-   public:
-    explicit ScopedStage(size_t size);
-    ~ScopedStage();
-
-   private:
-    size_t size = 0;
-  };
-
-  StartupTrace();
-  StartupTrace(const StartupTrace&) = delete;
-  StartupTrace(StartupTrace&&) = delete;
-  StartupTrace& operator=(const StartupTrace&) = delete;
-  StartupTrace& operator=(StartupTrace&&) = delete;
-  ~StartupTrace();
-
-  static StartupTrace* GetInstance();
-  ALWAYS_INLINE static bool IsEnabled() {
-    return startup_in_progress_.load(std::memory_order_acquire);
-  }
-
-  void BindToCurrentThread();
-  ScopedStage AddStage(const char* name);
-
-  // Called by the main thread before/after the tracing.
-  static void Startup();
-  static void StarupDone();
-
- private:
-  void RecordAndClearStages();
-
-  static std::atomic<bool> startup_in_progress_;
-
-  scoped_refptr<base::SequencedTaskRunner> task_runner_;
-  std::vector<Stage> stages_;
-};
-
-}  // namespace gl
-
-#define GPU_STARTUP_TRACE_EVENT(name) TRACE_EVENT("gpu,startup", name);
-
-#endif  // UI_GL_STARTUP_TRACE_H_
diff --git a/ui/ozone/common/egl_util.cc b/ui/ozone/common/egl_util.cc
index 92b2a6d..3226f7c 100644
--- a/ui/ozone/common/egl_util.cc
+++ b/ui/ozone/common/egl_util.cc
@@ -6,12 +6,12 @@
 
 #include "base/files/file_path.h"
 #include "base/path_service.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "ui/gl/buildflags.h"
 #include "ui/gl/egl_util.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_implementation.h"
-#include "ui/gl/startup_trace.h"
 
 #if BUILDFLAG(USE_OPENGL_APITRACE)
 #include <stdlib.h>
@@ -51,7 +51,7 @@
   base::NativeLibraryLoadError error;
   base::NativeLibrary gles_library;
   {
-    GPU_STARTUP_TRACE_EVENT("Load gles_library");
+    TRACE_EVENT("gpu,startup", "Load gles_library");
     gles_library = base::LoadNativeLibrary(gles_library_path, &error);
   }
   if (!gles_library) {
@@ -62,7 +62,7 @@
 
   base::NativeLibrary egl_library;
   {
-    GPU_STARTUP_TRACE_EVENT("Load egl_library");
+    TRACE_EVENT("gpu,startup", "Load egl_library");
     egl_library =
         base::LoadNativeLibrary(base::FilePath(egl_library_path), &error);
   }
diff --git a/ui/ozone/platform/wayland/common/drm_render_node_path_finder.cc b/ui/ozone/platform/wayland/common/drm_render_node_path_finder.cc
index 2fca3836..1a76ad1 100644
--- a/ui/ozone/platform/wayland/common/drm_render_node_path_finder.cc
+++ b/ui/ozone/platform/wayland/common/drm_render_node_path_finder.cc
@@ -19,8 +19,8 @@
 #include "base/command_line.h"
 #include "base/files/scoped_file.h"
 #include "base/logging.h"
+#include "base/trace_event/trace_event.h"
 #include "ui/gfx/linux/scoped_gbm_device.h"  // nogncheck
-#include "ui/gl/startup_trace.h"
 #include "ui/ozone/public/ozone_switches.h"
 
 namespace ui {
@@ -84,7 +84,7 @@
     // In case the first node /dev/dri/renderD128 can be opened but fails to
     // create gbm device on certain driver (E.g. PowerVR). Skip such paths.
     {
-      GPU_STARTUP_TRACE_EVENT("scoped attempt of gbm_create_device");
+      TRACE_EVENT("gpu,startup", "scoped attempt of gbm_create_device");
       ScopedGbmDevice gbm_device(gbm_create_device(drm_fd.get()));
       if (!gbm_device) {
         continue;
diff --git a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc
index a27ed66..34cea94 100644
--- a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc
+++ b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc
@@ -10,6 +10,7 @@
 #include "base/process/process.h"
 #include "base/task/current_thread.h"
 #include "base/task/single_thread_task_runner.h"
+#include "base/trace_event/trace_event.h"
 #include "base/version.h"
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/geometry/rrect_f.h"
@@ -17,7 +18,6 @@
 #include "ui/gfx/overlay_priority_hint.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_surface_egl.h"
-#include "ui/gl/startup_trace.h"
 #include "ui/ozone/platform/wayland/common/wayland_overlay_config.h"
 #include "ui/ozone/platform/wayland/gpu/wayland_surface_gpu.h"
 #include "ui/ozone/public/overlay_plane.h"
@@ -333,7 +333,7 @@
     return nullptr;
   }
 
-  GPU_STARTUP_TRACE_EVENT("ui::CreateGbmDevice");
+  TRACE_EVENT("gpu,startup", "ui::CreateGbmDevice");
   gbm_device_ = CreateGbmDevice(drm_render_node_fd_.get());
   if (!gbm_device_) {
     supports_dmabuf_ = false;
diff --git a/ui/ozone/public/ozone_platform.cc b/ui/ozone/public/ozone_platform.cc
index 748d1a5..69b2267 100644
--- a/ui/ozone/public/ozone_platform.cc
+++ b/ui/ozone/public/ozone_platform.cc
@@ -12,7 +12,6 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/trace_event/trace_event.h"
 #include "ui/events/devices/device_data_manager.h"
-#include "ui/gl/startup_trace.h"
 #include "ui/ozone/platform_object.h"
 #include "ui/ozone/platform_selection.h"
 #include "ui/ozone/public/platform_global_shortcut_listener.h"
@@ -91,7 +90,7 @@
 
 // static
 void OzonePlatform::InitializeForGPU(const InitParams& args) {
-  GPU_STARTUP_TRACE_EVENT("ui::OzonePlatform::InitializeForGPU");
+  TRACE_EVENT("gpu,startup", "ui::OzonePlatform::InitializeForGPU");
   EnsureInstance();
   if (g_instance->initialized_gpu_)
     return;
diff --git a/ui/views/interaction/element_tracker_views.cc b/ui/views/interaction/element_tracker_views.cc
index 080b217..bce2f51 100644
--- a/ui/views/interaction/element_tracker_views.cc
+++ b/ui/views/interaction/element_tracker_views.cc
@@ -11,7 +11,6 @@
 #include <string>
 
 #include "base/containers/contains.h"
-#include "base/debug/stack_trace.h"
 #include "base/logging.h"
 #include "base/memory/raw_ptr.h"
 #include "base/no_destructor.h"
diff --git a/v8 b/v8
index 3e4d2e9..e3624ed 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit 3e4d2e9bda03056d1e4f1434f95777aeb03fbe1f
+Subproject commit e3624ed658727258f0fe4b3d0ff11a9d27881ac4