diff --git a/.gitmodules b/.gitmodules
index 1862419..2f894c4 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -199,10 +199,6 @@
 [submodule "third_party/crossbench"]
 	path = third_party/crossbench
 	url = https://chromium.googlesource.com/crossbench
-[submodule "third_party/crubit/src"]
-	path = third_party/crubit/src
-	url = https://chromium.googlesource.com/external/github.com/google/crubit
-	gclient-condition = checkout_crubit
 [submodule "third_party/depot_tools"]
 	path = third_party/depot_tools
 	url = https://chromium.googlesource.com/chromium/tools/depot_tools
diff --git a/DEPS b/DEPS
index c31cf6f..c3284ca 100644
--- a/DEPS
+++ b/DEPS
@@ -152,19 +152,6 @@
   # Fetch clangd into the same bin/ directory as our clang binary.
   'checkout_clangd': False,
 
-  # Fetch prebuilt and prepackaged Bazel binary/executable. Bazel is currently
-  # only needed by `chromium/src/tools/rust/build_crubit.py` and therefore
-  # shouldn't be used outside of Chromium Rust Experiments project.
-  # Furthermore note that Bazel is only needed when building Crubit during Rust
-  # toolchain build (and is *not* needed during regular Chromium builds).
-  'checkout_bazel': False,
-
-  # Fetch Crubit support libraries in order to build ..._rs_api.rs and
-  # ..._rs_api_impl.cc that are generated by prebuilt (see
-  # tools/rust/build_crubit.py) Crubit tools during Chromium build (see
-  # also //build/rust/rs_bindings_from_cc.gni).
-  'checkout_crubit': False,
-
   # By default checkout the OpenXR loader library only on Windows and Android.
   # The OpenXR backend for VR in Chromium is currently only supported for these
   # platforms, but support for other platforms may be added in the future.
@@ -204,12 +191,6 @@
   # qemu on linux-arm64 machines.
   'checkout_fuchsia_for_arm64_host': False,
 
-  # Revision of Crubit (trunk on 2022-10-15).  This should typically be the
-  # same as the revision specified in CRUBIT_REVISION in
-  # tools/rust/update_rust.py.  More details and roll instructions can be
-  # found in tools/rust/README.md.
-  'crubit_revision': 'f5cbdf4b54b0e6b9f63a4464a2c901c82e0f0209',
-
   # By default, download the fuchsia sdk from the public sdk directory.
   'fuchsia_sdk_cipd_prefix': 'fuchsia/sdk/core/',
 
@@ -300,11 +281,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'src_internal_revision': 'ee6b0d656f2c6feb3d3e4b3a7371439443e87957',
+  'src_internal_revision': '882104e8ef00c37b322eb765dca65f7220a7a854',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '5218f67ec716edb39f5d005ddaf6e9d70eca7cc9',
+  'skia_revision': '5fb36dd08a257623ee0738747286de09662e4591',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -312,7 +293,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '0040cda1170f9d481135a95d2fbff616120f404b',
+  'angle_revision': 'cc44090d3483efdbae0dacf9c3fdb6c5d5a950fa',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -320,7 +301,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'fba5ab2f1c9c9b66b61e94d00662f9c12fcb692b',
+  'pdfium_revision': 'cd4887caa580fe6b54be2fd5abe87dc3fb4de27e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -396,7 +377,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': '5fea877e0b17578765cd4520857b5b3b172b5199',
+  'devtools_frontend_revision': 'b40634d52b0f5dcde839e73c8bdac4909458217a',
   # 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.
@@ -420,7 +401,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '690b037a7532f18b68e4907ddb0ddff6b1a8f1e7',
+  'dawn_revision': '9d1b7b42eb282d2ce24e329a8bf41c89fdd67973',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -488,7 +469,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'libunwind_revision':    'b7cdacaadbc4d121081ad6b146c7e94acec4c7ff',
+  'libunwind_revision':    '37c7d984b0b8520a0f99c6e6bbb0514e9996edc8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -952,31 +933,31 @@
     'bucket': 'chromium-browser-clang',
     'objects': [
       {
-        'object_name': 'Linux_x64/rust-toolchain-009e73825af0e59ad4fc603562e038b3dbd6593a-22-llvmorg-20-init-3847-g69c43468.tar.xz',
-        'sha256sum': '28f91ad46cce2750da95a6de305909a7f3904878cb1294de530ea962d19ccf19',
-        'size_bytes': 115038168,
-        'generation': 1727459972380358,
+        'object_name': 'Linux_x64/rust-toolchain-009e73825af0e59ad4fc603562e038b3dbd6593a-23-llvmorg-20-init-3847-g69c43468.tar.xz',
+        'sha256sum': 'a6c4e006002fd396331d77a1ccc8d2ab41fcfd0df5d3df7a6660b6aa7dc6f961',
+        'size_bytes': 123945520,
+        'generation': 1727798075788514,
         'condition': 'host_os == "linux" and non_git_source',
       },
       {
-        'object_name': 'Mac/rust-toolchain-009e73825af0e59ad4fc603562e038b3dbd6593a-22-llvmorg-20-init-3847-g69c43468.tar.xz',
-        'sha256sum': '7aa67826a75a39344a0eb1a7ca0ce439c811eb0318a7e1229cff1b4771d51a84',
-        'size_bytes': 108381448,
-        'generation': 1727459977622304,
+        'object_name': 'Mac/rust-toolchain-009e73825af0e59ad4fc603562e038b3dbd6593a-23-llvmorg-20-init-3847-g69c43468.tar.xz',
+        'sha256sum': '15927f50613ea0740e3fd7ff1fa40c255dcd4d7e7cd2a48fc85e6723ff9362fa',
+        'size_bytes': 117066196,
+        'generation': 1727798077728157,
         'condition': 'host_os == "mac" and host_cpu == "x64"',
       },
       {
-        'object_name': 'Mac_arm64/rust-toolchain-009e73825af0e59ad4fc603562e038b3dbd6593a-22-llvmorg-20-init-3847-g69c43468.tar.xz',
-        'sha256sum': '5b7399445e48c0cf87d4c56a577505ab7f03f936fc3d9eb538f3a5545d740497',
-        'size_bytes': 97828296,
-        'generation': 1727459981528990,
+        'object_name': 'Mac_arm64/rust-toolchain-009e73825af0e59ad4fc603562e038b3dbd6593a-23-llvmorg-20-init-3847-g69c43468.tar.xz',
+        'sha256sum': 'fb8daa448ed50eea88ac9c25c871332213a5c12a7c9beff0261478a6195b994d',
+        'size_bytes': 103504896,
+        'generation': 1727798079645218,
         'condition': 'host_os == "mac" and host_cpu == "arm64"',
       },
       {
-        'object_name': 'Win/rust-toolchain-009e73825af0e59ad4fc603562e038b3dbd6593a-22-llvmorg-20-init-3847-g69c43468.tar.xz',
-        'sha256sum': 'b518511a6278bbd7673c4eb42c3650160655ab3a6b8eeaefe0640de879123585',
-        'size_bytes': 172446400,
-        'generation': 1727459983614492,
+        'object_name': 'Win/rust-toolchain-009e73825af0e59ad4fc603562e038b3dbd6593a-23-llvmorg-20-init-3847-g69c43468.tar.xz',
+        'sha256sum': '139fbd7092526abcb91d5df67d4fc472a2bdae9a6487fcf3ec122c8ac112b4f8',
+        'size_bytes': 178572296,
+        'generation': 1727798081586976,
         'condition': 'host_os == "win"',
       },
     ],
@@ -1295,7 +1276,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '59c00202c7c9270d760bf9a57ffd242ef6b0f271',
+    '4120c4d811f4c534ae4be631037163de6f797935',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1733,7 +1714,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' + '@' + 'f834f05d9e79b960930b90da2d6e8f824c85a9b1',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'c621276d4f2ec43ef4346a40f5dd328aa8bcbaaf',
       'condition': 'checkout_chromeos',
   },
 
@@ -1762,19 +1743,14 @@
     Var('chromium_git') + '/crossbench.git' + '@' + Var('crossbench_revision'),
 
 
-  'src/third_party/crubit/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/crubit.git' + '@' + Var('crubit_revision'),
-      'condition': 'checkout_crubit',
-  },
-
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '6dec85272d23ae587984cdd78eae428ce3b2ad9b',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'cc39a5681f48f42e8f0d16b9979bd3085c93de0a',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '51db6cf38207673d6bfa25c8e136277c3cfb6e9c',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '84c1220a80b203163a2c3d124ca103f63580d8ce',
     'condition': 'checkout_src_internal',
   },
 
@@ -2240,7 +2216,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '478e5ab3eca30e600006d5a0a08b176fd34d3bd1',
 
   'src/third_party/openscreen/src':
-    Var('chromium_git') + '/openscreen' + '@' + '6508900f6f6cbe87a61abdc337fcdfc289c43cea',
+    Var('chromium_git') + '/openscreen' + '@' + '1335531d0d7847625b52e9365902d7fcba82f3c6',
 
   'src/third_party/openxr/src': {
     'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + '288d3a7ebc1ad959f62d51da75baa3d27438c499',
@@ -2266,7 +2242,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '077bb360edecb33551e88c8097035e4b8cef245b',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '226197a61ac2b08b3860b5c73f8411ba0ba43947',
 
   'src/base/tracing/test/data': {
     'bucket': 'perfetto',
@@ -2554,7 +2530,7 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@fb8f0127fca4d687f0584b62183572ed39ffc198',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@0828416b3eae17db848880d486e5c7f0038e46b0',
   'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@a729c86d78552ec7e05e3748448e7a99f6f2a696',
   '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@ec59c77a3bb5c747a369931ef101ac7c14823f2f',
@@ -2603,7 +2579,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '9d029d337ae1832b5a7b9bf049a87076c12f749d',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '8569a0d5346330eb6641512df65b864bbaed11e9',
+    Var('webrtc_git') + '/src.git' + '@' + '076eb6cdf236cd6125ab126df2340ca3ee265425',
 
   # 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.
@@ -2743,7 +2719,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'RlxrAZbJURNWeJnj9fybs_LDcbvjGjPa82Jx5_rz6K8C',
+        'version': 'swnHsMK93RsqwT2pV61xuuotI2-ti-_np8j5BSXvmpoC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -2754,7 +2730,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'uLcuCv4vP-zbrrt6V9EDkeKSlPWvYejOMIU3WfWaDcQC',
+        'version': 'xlRQrk5SDu4Nzw6mU6FfUm0VlXN1RlAz6CynFnVnnRgC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4068,42 +4044,6 @@
       'dep_type': 'cipd',
   },
 
-  'src/tools/bazel/linux-amd64': {
-    'packages': [{
-       'package': 'infra/3pp/tools/bazel_bootstrap/linux-amd64',
-       'version': 'version:2@5.3.2.1',
-    }],
-    'dep_type': 'cipd',
-    'condition': 'host_os == "linux" and checkout_bazel and non_git_source',
-  },
-
-  'src/tools/bazel/mac-amd64': {
-    'packages': [{
-       'package': 'infra/3pp/tools/bazel_bootstrap/mac-amd64',
-       'version': 'version:2@5.3.2.1',
-    }],
-    'dep_type': 'cipd',
-    'condition': 'host_os == "mac" and host_cpu == "x64" and checkout_bazel',
-  },
-
-  'src/tools/bazel/mac-arm64': {
-    'packages': [{
-       'package': 'infra/3pp/tools/bazel_bootstrap/mac-arm64',
-       'version': 'version:2@5.3.2.1',
-    }],
-    'dep_type': 'cipd',
-    'condition': 'host_os == "mac" and host_cpu == "arm64" and checkout_bazel',
-  },
-
-  'src/tools/bazel/windows-amd64': {
-    'packages': [{
-       'package': 'infra/3pp/tools/bazel_bootstrap/windows-amd64',
-       'version': 'version:2@5.3.2',
-    }],
-    'dep_type': 'cipd',
-    'condition': 'host_os == "win" and checkout_bazel',
-  },
-
   # Dependencies from src_internal
   'src/chromeos/ash/resources/internal': {
       'url': Var('chrome_git') + '/chrome/chromeos/ash/resources/internal.git' + '@' +
@@ -4396,7 +4336,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        'c7ca556bc732c861810971db63ceffa352c2442e',
+        '028fcf85bcfd643709d3174307c2e4303e76fcd7',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index 96b45f5..acdd7cc 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -2144,6 +2144,12 @@
         'content/browser/tab_contents/|'\
         'chrome/browser/ui/tab_contents/',
     },
+    'tab_group_infra': {
+      'filepath': 'chrome/browser/data_sharing/'\
+        '|chrome/browser/tab_group_sync/'\
+        '|components/data_sharing/'\
+        '|components/saved_tab_groups/'
+    },
     'tab_interface': {
       'filepath': 'chrome/browser/tab/'
     },
@@ -3271,6 +3277,7 @@
     'tab_contents': ['ajwong+watch@chromium.org',
                      'avi@chromium.org',
                      'creis+watch@chromium.org'],
+    'tab_group_infra' : [ 'chrome-tab-group-eng-leads+watch-infra@google.com' ],
     'tab_interface': ['jinsukkim+watch@chromium.org'],
     'tab_ui_and_start_surface': ['meiliang+watch@chromium.org',
                                  'yuezhanggg+watch@chromium.org',
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwMetricsIntegrationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwMetricsIntegrationTest.java
index bc446b4..efdb190 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwMetricsIntegrationTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwMetricsIntegrationTest.java
@@ -491,11 +491,13 @@
             ChromeComponent expected) {
         return new TypeSafeMatcher<ChromeComponent>() {
             @Override
+            @SuppressWarnings("LiteProtoToString")
             public void describeTo(Description description) {
                 description.appendText(expected.toString());
             }
 
             @Override
+            @SuppressWarnings("LiteProtoToString")
             protected void describeMismatchSafely(
                     ChromeComponent item, Description mismatchDescription) {
                 mismatchDescription.appendText("Doesn't match " + item.toString());
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ClientHintsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ClientHintsTest.java
index 6e296e8..f8c33ad 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/ClientHintsTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/ClientHintsTest.java
@@ -810,8 +810,7 @@
                 "fake_platform",
                 customUserAgentMetadata.get(AwUserAgentMetadata.MetadataKeys.PLATFORM));
         Assert.assertEquals(
-                new Boolean(true),
-                customUserAgentMetadata.get(AwUserAgentMetadata.MetadataKeys.WOW64));
+                true, customUserAgentMetadata.get(AwUserAgentMetadata.MetadataKeys.WOW64));
         Assert.assertEquals(
                 Arrays.deepToString(overrideBrands),
                 Arrays.deepToString(
@@ -835,8 +834,7 @@
         Assert.assertEquals(
                 "Android", customUserAgentMetadata.get(AwUserAgentMetadata.MetadataKeys.PLATFORM));
         Assert.assertEquals(
-                new Boolean(false),
-                customUserAgentMetadata.get(AwUserAgentMetadata.MetadataKeys.WOW64));
+                false, customUserAgentMetadata.get(AwUserAgentMetadata.MetadataKeys.WOW64));
 
         String[][] actualOverrideBrands =
                 (String[][])
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java
index 4d74af7..b7087c5 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java
@@ -301,7 +301,7 @@
                                         mWebServer.getBaseUrl(),
                                         null);
                             } catch (UnsupportedEncodingException e) {
-                                Assert.fail();
+                                throw new RuntimeException(e);
                             }
                         });
         expectTitle(testString);
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/devui/FlagsFragmentTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/devui/FlagsFragmentTest.java
index a8355f48..86a4a97 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/devui/FlagsFragmentTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/devui/FlagsFragmentTest.java
@@ -572,10 +572,10 @@
         return flagInteraction;
     }
 
+    /** Verify if the baseFeature flag contains only "Default", "Enabled" , "Disabled" states. */
     @Test
     @MediumTest
     @Feature({"AndroidWebView"})
-    /** Verify if the baseFeature flag contains only "Default", "Enabled" , "Disabled" states. */
     public void testFlagStates_baseFeature() throws Throwable {
         ListView flagsList = mRule.getActivity().findViewById(R.id.flags_list);
 
@@ -594,10 +594,10 @@
         testFlagStatesHelper(firstBaseFeaturePosition);
     }
 
+    /** Verify if the commandline flag contains only "Default", "Enabled" states. */
     @Test
     @MediumTest
     @Feature({"AndroidWebView"})
-    /** Verify if the commandline flag contains only "Default", "Enabled" states. */
     public void testFlagStates_commandLineFlag() throws Throwable {
         ListView flagsList = mRule.getActivity().findViewById(R.id.flags_list);
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/services/JsSandboxServiceTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/services/JsSandboxServiceTest.java
index 4e5f301..68dac32 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/services/JsSandboxServiceTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/services/JsSandboxServiceTest.java
@@ -465,7 +465,7 @@
         // Invalid offset extending beyond end of file.
         // Note that file contains only ascii characters for testing purposes, hence we
         // can assume the length of the string to be the number of bytes it contains.
-        long offsetBeyondEof = fileContent.length() + 10;
+        long offsetBeyondEof = (long) fileContent.length() + 10;
         try (AssetFileDescriptor afd =
                 new AssetFileDescriptor(pfd, offsetBeyondEof, fileContent.length())) {
             ListenableFuture<JavaScriptSandbox> jsSandboxFuture =
@@ -532,7 +532,7 @@
         // Read only up to call to `hello();
         // File contains only ascii characters for testing purposes, hence we can predict the
         // number of bytes to remove from the end.
-        long length = fileContent.length() - "bye();".length();
+        long length = (long) fileContent.length() - "bye();".length();
         try (AssetFileDescriptor afd = new AssetFileDescriptor(pfd, 0, length)) {
             ListenableFuture<JavaScriptSandbox> jsSandboxFuture =
                     JavaScriptSandbox.createConnectedInstanceForTestingAsync(context);
@@ -588,7 +588,7 @@
         // Declare length beyond EOF.
         // Note that file contains only ascii characters for testing purposes, hence we
         // can assume the length of the string to be the number of bytes it contains.
-        long length = fileContent.length() + 10;
+        long length = (long) fileContent.length() + 10;
         try (AssetFileDescriptor afd = new AssetFileDescriptor(pfd, 0, length)) {
             ListenableFuture<JavaScriptSandbox> jsSandboxFuture =
                     JavaScriptSandbox.createConnectedInstanceForTestingAsync(context);
diff --git a/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/ActionModeTest.java b/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/ActionModeTest.java
index 4579269..4dab588 100644
--- a/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/ActionModeTest.java
+++ b/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/ActionModeTest.java
@@ -192,10 +192,8 @@
             onData(new MenuItemMatcher(equalTo(name))).inRoot(rootMatcher).perform(click());
         }
 
-        /**
-         * After select all action is clicked, the PopUp Menu may disappear
-         * briefly due to selection change, wait for the menu to reappear
-         */
+        // After select all action is clicked, the PopUp Menu may disappear briefly due to selection
+        // change, wait for the menu to reappear
         if (name.equals(SELECT_ALL_ACTION)) {
             assertTrue(mWebViewActivityRule.waitForActionBarPopup());
         }
diff --git a/ash/auth/active_session_auth_controller_impl.cc b/ash/auth/active_session_auth_controller_impl.cc
index 2e6d9807..ed524273 100644
--- a/ash/auth/active_session_auth_controller_impl.cc
+++ b/ash/auth/active_session_auth_controller_impl.cc
@@ -141,6 +141,52 @@
 
 }  // namespace
 
+// This class manages the closing process after successful fingerprint
+// authentication. It listens for two signals:
+//  1. The completion of the successful authentication animation.
+//  2. The authentication callback from cryptohome.
+// Once both signals are received, the class triggers the closing process.
+class ActiveSessionAuthControllerImpl::FingerprintAuthTracker {
+ public:
+  explicit FingerprintAuthTracker(ActiveSessionAuthControllerImpl* owner)
+      : owner_(owner) {
+    CHECK(owner_);
+  }
+
+  void OnAuthenticationFinished(
+      std::unique_ptr<UserContext> user_context,
+      std::optional<AuthenticationError> authentication_error) {
+    CHECK_EQ(authentication_finished_, false);
+    authentication_finished_ = true;
+    if (authentication_error.has_value()) {
+      LOG(ERROR) << "Authentication error during OnFingerprintSuccess code: "
+                 << authentication_error->get_cryptohome_code();
+    }
+    owner_->user_context_ = std::move(user_context);
+    MaybeNotifyOwner();
+  }
+
+  void OnAnimationFinished() {
+    VLOG(1) << "OnAnimationFinished";
+    CHECK_EQ(animation_finished_, false);
+    animation_finished_ = true;
+    MaybeNotifyOwner();
+  }
+
+  void MaybeNotifyOwner() {
+    if (authentication_finished_ && animation_finished_) {
+      owner_->StartClose();
+    }
+    CHECK(owner_);
+    CHECK(owner_->fp_auth_tracker_);
+  }
+
+ private:
+  const raw_ptr<ActiveSessionAuthControllerImpl> owner_;
+  bool animation_finished_ = false;
+  bool authentication_finished_ = false;
+};
+
 ActiveSessionAuthControllerImpl::TestApi::TestApi(
     ActiveSessionAuthControllerImpl* controller)
     : controller_(controller) {}
@@ -207,6 +253,9 @@
   auth_performer_ = std::make_unique<AuthPerformer>(UserDataAuthClient::Get());
   account_id_ = Shell::Get()->session_controller()->GetActiveAccountId();
 
+  fingerprint_animation_finished_ = false;
+  fingerprint_authentication_finished_ = false;
+
   user_manager::User* active_user =
       user_manager::UserManager::Get()->GetActiveUser();
   auto user_context = std::make_unique<UserContext>(*active_user);
@@ -299,6 +348,7 @@
     LOG(ERROR) << "Failed to start fingerprint auth session - only "
                   "non-fingerprint factors will be available.";
   } else {
+    fp_auth_tracker_ = std::make_unique<FingerprintAuthTracker>(this);
     available_factors_.Put(AuthInputType::kFingerprint);
   }
   std::move(on_auth_factors_ready).Run(std::move(user_context));
@@ -319,6 +369,9 @@
   }
   switch (scan_result) {
     case FingerprintAuthScanResult::kSuccess:
+      contents_view_->NotifyFingerprintAuthSuccess(
+          base::BindOnce(&FingerprintAuthTracker::OnAnimationFinished,
+                         base::Unretained(fp_auth_tracker_.get())));
       if (state_ == ActiveSessionAuthState::kPasswordAuthStarted ||
           state_ == ActiveSessionAuthState::kPinAuthStarted) {
         SetState(ActiveSessionAuthState::kFingerprintAuthSucceededWaiting);
@@ -346,25 +399,14 @@
   NOTREACHED();
 }
 
-void ActiveSessionAuthControllerImpl::OnFingerprintSuccess(
-    std::unique_ptr<UserContext> user_context,
-    std::optional<AuthenticationError> authentication_error) {
-  if (authentication_error.has_value()) {
-    LOG(ERROR) << "Authentication error during OnFingerprintSuccess code: "
-               << authentication_error->get_cryptohome_code();
-  }
-  user_context_ = std::move(user_context);
-  StartClose();
-}
-
 void ActiveSessionAuthControllerImpl::HandleFingerprintAuthSuccess() {
   CHECK(user_context_);
   uma_recorder_.RecordAuthSucceeded(AuthInputType::kFingerprint);
   SetState(ActiveSessionAuthState::kFingerprintAuthSucceeded);
   auth_performer_->AuthenticateWithLegacyFingerprint(
       std::move(user_context_),
-      base::BindOnce(&ActiveSessionAuthControllerImpl::OnFingerprintSuccess,
-                     weak_ptr_factory_.GetWeakPtr()));
+      base::BindOnce(&FingerprintAuthTracker::OnAuthenticationFinished,
+                     base::Unretained(fp_auth_tracker_.get())));
 }
 
 void ActiveSessionAuthControllerImpl::InitUi() {
@@ -410,12 +452,14 @@
 
   auth_performer_->InvalidateCurrentAttempts();
   if (fp_client_ && available_factors_.Has(AuthInputType::kFingerprint)) {
+    CHECK(fp_auth_tracker_);
     fp_client_->TerminateFingerprintAuth(
         std::move(user_context_),
         base::BindOnce(&ActiveSessionAuthControllerImpl::CompleteClose,
                        weak_ptr_factory_.GetWeakPtr()));
     return;
   }
+  CHECK(!fp_auth_tracker_);
   CompleteClose(std::move(user_context_), std::nullopt);
 }
 
@@ -441,7 +485,7 @@
 
   title_.clear();
   description_.clear();
-
+  fp_auth_tracker_.reset();
   widget_.reset();
 }
 
diff --git a/ash/auth/active_session_auth_controller_impl.h b/ash/auth/active_session_auth_controller_impl.h
index fcc26595..3a8e3fc 100644
--- a/ash/auth/active_session_auth_controller_impl.h
+++ b/ash/auth/active_session_auth_controller_impl.h
@@ -115,6 +115,7 @@
   void OnFingerprintTerminated(
       std::unique_ptr<UserContext> user_context,
       std::optional<AuthenticationError> authentication_error);
+  void OnFingerprintAnimationFinished();
 
   // Tracks the authentication flow for the active session.
   enum class ActiveSessionAuthState {
@@ -136,6 +137,9 @@
   };
 
  private:
+  class FingerprintAuthTracker;
+  friend class FingerprintAuthTracker;
+
   using AuthFactorsReadyCallback =
       base::OnceCallback<void(std::unique_ptr<UserContext>)>;
 
@@ -197,7 +201,11 @@
 
   std::unique_ptr<AuthRequest> auth_request_;
 
+  bool fingerprint_animation_finished_ = false;
+  bool fingerprint_authentication_finished_ = false;
+
   raw_ptr<ActiveSessionFingerprintClient> fp_client_;
+  std::unique_ptr<FingerprintAuthTracker> fp_auth_tracker_;
 
   ActiveSessionAuthMetricsRecorder uma_recorder_;
 
diff --git a/ash/auth/views/active_session_auth_view.cc b/ash/auth/views/active_session_auth_view.cc
index e1e2b04..62bfc1f1 100644
--- a/ash/auth/views/active_session_auth_view.cc
+++ b/ash/auth/views/active_session_auth_view.cc
@@ -290,6 +290,12 @@
   auth_container_->SetFingerprintState(state);
 }
 
+void ActiveSessionAuthView::NotifyFingerprintAuthSuccess(
+    base::OnceClosure on_success_animation_finished) {
+  auth_container_->NotifyFingerprintAuthSuccess(
+      std::move(on_success_animation_finished));
+}
+
 void ActiveSessionAuthView::NotifyFingerprintAuthFailure() {
   auth_container_->NotifyFingerprintAuthFailure();
 }
diff --git a/ash/auth/views/active_session_auth_view.h b/ash/auth/views/active_session_auth_view.h
index a6dc9f60..2ab4ed4 100644
--- a/ash/auth/views/active_session_auth_view.h
+++ b/ash/auth/views/active_session_auth_view.h
@@ -118,6 +118,8 @@
 
   // FingerprintView actions:
   void SetFingerprintState(FingerprintState state);
+  void NotifyFingerprintAuthSuccess(
+      base::OnceClosure on_success_animation_finished);
   void NotifyFingerprintAuthFailure();
 
  private:
diff --git a/ash/auth/views/auth_container_view.cc b/ash/auth/views/auth_container_view.cc
index 2f23fdb..10ad61e 100644
--- a/ash/auth/views/auth_container_view.cc
+++ b/ash/auth/views/auth_container_view.cc
@@ -330,6 +330,13 @@
   fingerprint_view_->SetState(state);
 }
 
+void AuthContainerView::NotifyFingerprintAuthSuccess(
+    base::OnceCallback<void()> on_success_animation_finished) {
+  CHECK(fingerprint_view_);
+  fingerprint_view_->NotifyAuthSuccess(
+      std::move(on_success_animation_finished));
+}
+
 void AuthContainerView::NotifyFingerprintAuthFailure() {
   CHECK(fingerprint_view_);
   fingerprint_view_->NotifyAuthFailure();
diff --git a/ash/auth/views/auth_container_view.h b/ash/auth/views/auth_container_view.h
index 967876e..8561cc14 100644
--- a/ash/auth/views/auth_container_view.h
+++ b/ash/auth/views/auth_container_view.h
@@ -127,6 +127,8 @@
 
   // FingerprintView actions:
   void SetFingerprintState(FingerprintState state);
+  void NotifyFingerprintAuthSuccess(
+      base::OnceClosure on_success_animation_finished);
   void NotifyFingerprintAuthFailure();
 
  private:
diff --git a/ash/auth/views/fingerprint_view.cc b/ash/auth/views/fingerprint_view.cc
index 86fd725..f0f4271 100644
--- a/ash/auth/views/fingerprint_view.cc
+++ b/ash/auth/views/fingerprint_view.cc
@@ -5,8 +5,10 @@
 #include "ash/auth/views/fingerprint_view.h"
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <string_view>
+#include <vector>
 
 #include "ash/auth/views/auth_common.h"
 #include "ash/login/resources/grit/login_resources.h"
@@ -18,6 +20,7 @@
 #include "ash/style/ash_color_id.h"
 #include "base/check.h"
 #include "base/check_op.h"
+#include "base/containers/flat_tree.h"
 #include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
@@ -28,7 +31,9 @@
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/chromeos/styles/cros_tokens_color_mappings.h"
+#include "ui/color/color_provider.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/lottie/animation.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/box_layout.h"
@@ -68,6 +73,34 @@
 constexpr ui::ColorId kFingerprintIconDisabledColorId =
     cros_tokens::kCrosSysDisabled;
 
+constexpr float kCheckmarkAnimationPlaybackSpeed = 2.25;
+
+std::unique_ptr<lottie::Animation> GetCheckmarkAnimation(
+    ui::ColorProvider* color_provider) {
+  std::optional<std::vector<uint8_t>> lottie_data =
+      ui::ResourceBundle::GetSharedInstance().GetLottieData(
+          IDR_LOGIN_ARROW_CHECKMARK_ANIMATION);
+  CHECK(lottie_data.has_value());
+
+  cc::SkottieColorMap color_map = cc::SkottieColorMap{
+      cc::SkottieMapColor(
+          "cros.sys.illo.color2",
+          color_provider->GetColor(cros_tokens::kCrosSysPositive)),
+      cc::SkottieMapColor(
+          "cros.sys.app_base_shaded",
+          color_provider->GetColor(cros_tokens::kCrosSysOnSurface)),
+  };
+
+  std::unique_ptr<lottie::Animation> animation =
+      std::make_unique<lottie::Animation>(
+          cc::SkottieWrapper::UnsafeCreateSerializable(lottie_data.value()),
+          std::move(color_map));
+
+  animation->SetPlaybackSpeed(kCheckmarkAnimationPlaybackSpeed);
+
+  return animation;
+}
+
 }  // namespace
 
 //----------------------- FingerprintView Test API ------------------------
@@ -125,6 +158,12 @@
       gfx::Size(kFingerprintIconSizeDp, kFingerprintIconSizeDp),
       0 /*corner_radius*/));
 
+  lottie_animation_view_ =
+      AddChildView(std::make_unique<views::AnimatedImageView>());
+  lottie_animation_view_->SetImageSize(
+      gfx::Size(kFingerprintIconSizeDp, kFingerprintIconSizeDp));
+  lottie_animation_view_->SetVisible(false);
+
   label_ = AddChildView(std::make_unique<views::Label>());
   label_->SetSubpixelRenderingEnabled(false);
   label_->SetAutoColorReadabilityEnabled(false);
@@ -141,6 +180,8 @@
 
 FingerprintView::~FingerprintView() {
   icon_ = nullptr;
+  scoped_animation_observer_.Reset();
+  lottie_animation_view_ = nullptr;
   label_ = nullptr;
 }
 
@@ -148,6 +189,11 @@
   if (state_ == state) {
     return;
   }
+
+  if (has_success_) {
+    return;
+  }
+
   reset_state_.Stop();
   state_ = state;
   DisplayCurrentState();
@@ -158,11 +204,39 @@
     return;
   }
 
+  if (has_success_) {
+    return;
+  }
+
   has_pin_ = has_pin;
   DisplayCurrentState();
 }
 
+void FingerprintView::NotifyAuthSuccess(
+    base::OnceClosure on_success_animation_finished) {
+  has_success_ = true;
+  CHECK(on_success_animation_finished_.is_null());
+  on_success_animation_finished_ = std::move(on_success_animation_finished);
+  CHECK(GetColorProvider());
+  std::unique_ptr<lottie::Animation> animation =
+      GetCheckmarkAnimation(GetColorProvider());
+  CHECK(animation);
+  auto playback_config = lottie::Animation::PlaybackConfig::CreateWithStyle(
+      lottie::Animation::Style::kLinear, *animation);
+  // Observe animation to know when it finishes playing.
+  scoped_animation_observer_.Observe(animation.get());
+  lottie_animation_view_->SetAnimatedImage(std::move(animation));
+  lottie_animation_view_->Play(playback_config);
+
+  icon_->SetVisible(false);
+  lottie_animation_view_->SetVisible(true);
+}
+
 void FingerprintView::NotifyAuthFailure() {
+  if (has_success_) {
+    return;
+  }
+
   SetState(FingerprintState::AVAILABLE_WITH_FAILED_ATTEMPT);
   reset_state_.Start(
       FROM_HERE, kResetToDefaultIconDelay,
@@ -171,9 +245,14 @@
 }
 
 void FingerprintView::OnGestureEvent(ui::GestureEvent* event) {
+  if (has_success_) {
+    return;
+  }
+
   if (event->type() != ui::EventType::kGestureTap) {
     return;
   }
+
   if (state_ == FingerprintState::AVAILABLE_DEFAULT ||
       state_ == FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING ||
       state_ == FingerprintState::AVAILABLE_WITH_FAILED_ATTEMPT) {
@@ -186,6 +265,7 @@
 }
 
 void FingerprintView::DisplayCurrentState() {
+  CHECK(!has_success_);
   if (state_ == FingerprintState::UNAVAILABLE) {
     SetVisible(false);
     return;
@@ -198,6 +278,7 @@
 }
 
 void FingerprintView::SetIcon() {
+  CHECK(!has_success_);
   switch (state_) {
     case FingerprintState::AVAILABLE_DEFAULT:
     case FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING:
@@ -222,6 +303,7 @@
 }
 
 ui::ColorId FingerprintView::GetIconColorIdFromState() const {
+  CHECK(!has_success_);
   switch (state_) {
     case FingerprintState::AVAILABLE_DEFAULT:
     case FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING:
@@ -236,6 +318,7 @@
 }
 
 int FingerprintView::GetTextIdFromState() const {
+  CHECK(!has_success_);
   switch (state_) {
     case FingerprintState::AVAILABLE_DEFAULT:
     case FingerprintState::AVAILABLE_WITH_FAILED_ATTEMPT:
@@ -255,6 +338,7 @@
 }
 
 int FingerprintView::GetA11yTextIdFromState() const {
+  CHECK(!has_success_);
   switch (state_) {
     case FingerprintState::AVAILABLE_DEFAULT:
     case FingerprintState::AVAILABLE_WITH_FAILED_ATTEMPT:
@@ -284,6 +368,15 @@
   return gfx::Size(kTextLineWidthDp, preferred_height);
 }
 
+void FingerprintView::AnimationCycleEnded(const lottie::Animation* animation) {
+  CHECK(has_success_);
+  scoped_animation_observer_.Reset();
+
+  if (on_success_animation_finished_) {
+    std::move(on_success_animation_finished_).Run();
+  }
+}
+
 BEGIN_METADATA(FingerprintView)
 END_METADATA
 
diff --git a/ash/auth/views/fingerprint_view.h b/ash/auth/views/fingerprint_view.h
index dfff9f9..887aeac 100644
--- a/ash/auth/views/fingerprint_view.h
+++ b/ash/auth/views/fingerprint_view.h
@@ -11,10 +11,13 @@
 #include "ash/style/ash_color_id.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
 #include "base/timer/timer.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/lottie/animation_observer.h"
+#include "ui/views/controls/animated_image_view.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/view.h"
 
@@ -23,7 +26,8 @@
 // FingerprintView is a view displaying a fingerprint icon and label,
 // dynamically adapting based on fingerprint availability,
 // authentication state, and the presence of a PIN.
-class ASH_EXPORT FingerprintView : public views::View {
+class ASH_EXPORT FingerprintView : public views::View,
+                                   public lottie::AnimationObserver {
   METADATA_HEADER(FingerprintView, views::View)
  public:
   class TestApi {
@@ -66,6 +70,9 @@
   // Indicates if a PIN is set, potentially influencing the label text.
   void SetHasPin(bool has_pin);
 
+  // Triggers a brief animation to signal an authentication success.
+  void NotifyAuthSuccess(base::OnceClosure on_success_animation_finished);
+
   // Triggers a brief animation to signal an authentication failure.
   void NotifyAuthFailure();
 
@@ -74,6 +81,9 @@
   gfx::Size CalculatePreferredSize(
       const views::SizeBounds& available_size) const override;
 
+  // lottie::AnimationObserver:
+  void AnimationCycleEnded(const lottie::Animation* animation) override;
+
  private:
   // Updates the visual elements to reflect the current state and PIN
   // availability.
@@ -90,6 +100,16 @@
   raw_ptr<views::Label> label_ = nullptr;
   raw_ptr<AnimatedRoundedImageView> icon_ = nullptr;
 
+  // A green checkmark animation shown when NotifyAuthSuccess called.
+  raw_ptr<views::AnimatedImageView> lottie_animation_view_;
+
+  base::ScopedObservation<lottie::Animation, lottie::AnimationObserver>
+      scoped_animation_observer_{this};
+
+  base::OnceClosure on_success_animation_finished_;
+
+  bool has_success_ = false;
+
   // State:
   FingerprintState state_ = FingerprintState::UNAVAILABLE;
   bool has_pin_ = false;
diff --git a/ash/capture_mode/base_capture_mode_session.h b/ash/capture_mode/base_capture_mode_session.h
index ff704734..e7a16ec 100644
--- a/ash/capture_mode/base_capture_mode_session.h
+++ b/ash/capture_mode/base_capture_mode_session.h
@@ -10,6 +10,7 @@
 #include "ash/capture_mode/capture_mode_controller.h"
 #include "ash/shell_observer.h"
 #include "ui/compositor/layer_owner.h"
+#include "ui/views/controls/button/button.h"
 
 namespace ash {
 
@@ -154,6 +155,11 @@
   // the captured region as `image`.
   virtual void ShowSearchResultsPanel(const gfx::ImageSkia& image) = 0;
 
+  // Adds an action button below the selected region during an active session.
+  virtual void AddActionButton(views::Button::PressedCallback callback,
+                               std::u16string text,
+                               const gfx::VectorIcon* icon) = 0;
+
   // ShellObserver:
   void OnRootWindowWillShutdown(aura::Window* root_window) override;
 
diff --git a/ash/capture_mode/capture_mode_session.cc b/ash/capture_mode/capture_mode_session.cc
index c9e2c6c..3648816 100644
--- a/ash/capture_mode/capture_mode_session.cc
+++ b/ash/capture_mode/capture_mode_session.cc
@@ -32,12 +32,14 @@
 #include "ash/keyboard/ui/keyboard_ui_controller.h"
 #include "ash/public/cpp/resources/grit/ash_public_unscaled_resources.h"
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_id.h"
 #include "ash/style/color_util.h"
 #include "ash/style/icon_button.h"
+#include "ash/style/pill_button.h"
 #include "ash/style/tab_slider_button.h"
 #include "ash/utility/cursor_setter.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
@@ -188,6 +190,9 @@
 // The horizontal distance between action buttons in a row.
 constexpr int kActionButtonSpacing = 10;
 
+// The corner radius for an action button.
+constexpr int kActionButtonRadius = 18;
+
 // Mouse cursor warping is disabled when the capture source is a custom region.
 // Sets the mouse warp status to |enable| and return the original value.
 bool SetMouseWarpEnabled(bool enable) {
@@ -484,6 +489,59 @@
   return widget_bounds;
 }
 
+class ActionButtonView : public PillButton {
+  METADATA_HEADER(ActionButtonView, PillButton)
+
+ public:
+  ActionButtonView(views::Button::PressedCallback callback,
+                   std::u16string text,
+                   const gfx::VectorIcon* icon)
+      : PillButton(std::move(callback),
+                   text,
+                   Type::kDefaultLargeWithIconLeading,
+                   icon),
+        // Since this view has fully circular rounded corners, we can't use a
+        // nine patch layer for the shadow. We have to use the
+        // `ShadowOnTextureLayer`. For more info, see https://crbug.com/1308800.
+        shadow_(SystemShadow::CreateShadowOnTextureLayer(
+            SystemShadow::Type::kElevation12)) {
+    shadow_->SetRoundedCornerRadius(kActionButtonRadius);
+    capture_mode_util::SetHighlightBorder(
+        this, kActionButtonRadius,
+        views::HighlightBorder::Type::kHighlightBorderNoShadow);
+  }
+  ActionButtonView(const ActionButtonView&) = delete;
+  ActionButtonView& operator=(const ActionButtonView&) = delete;
+  ~ActionButtonView() override = default;
+
+  // views::View:
+  void AddedToWidget() override {
+    PillButton::AddedToWidget();
+
+    // Attach the shadow at the bottom of the widget layer.
+    auto* shadow_layer = shadow_->GetLayer();
+    auto* widget_layer = GetWidget()->GetLayer();
+    widget_layer->Add(shadow_layer);
+    widget_layer->StackAtBottom(shadow_layer);
+
+    // Make the shadow observe the color provider source change to update the
+    // colors.
+    shadow_->ObserveColorProviderSource(GetWidget());
+  }
+
+  void OnBoundsChanged(const gfx::Rect& previous_bounds) override {
+    // The shadow layer is a sibling of this view's layer, and should have the
+    // same bounds.
+    shadow_->SetContentBounds(layer()->bounds());
+  }
+
+ private:
+  std::unique_ptr<SystemShadow> shadow_;
+};
+
+BEGIN_METADATA(ActionButtonView)
+END_METADATA
+
 }  // namespace
 
 // -----------------------------------------------------------------------------
@@ -1306,10 +1364,6 @@
 void CaptureModeSession::ShowSearchResultsPanel(const gfx::ImageSkia& image) {
   DCHECK_EQ(active_behavior()->behavior_type(), BehaviorType::kSunfish);
 
-  // When we show the panel, we also want to update the action button container
-  // and any buttons that might be visible.
-  UpdateActionContainerWidget();
-
   if (!search_results_panel_widget_) {
     search_results_panel_widget_ =
         SearchResultsPanel::CreateWidget(current_root());
@@ -1320,6 +1374,27 @@
   auto* search_results_panel = views::AsViewClass<SearchResultsPanel>(
       search_results_panel_widget_->GetContentsView());
   search_results_panel->SetSearchBoxImage(image);
+
+  UpdateActionContainerWidget();
+}
+
+void CaptureModeSession::AddActionButton(
+    views::Button::PressedCallback callback,
+    std::u16string text,
+    const gfx::VectorIcon* icon) {
+  // Another process may try to add an action button before the container is
+  // created, or while it is invalid. In these cases, we don't want to do
+  // anything.
+  if (!action_container_widget_) {
+    return;
+  }
+
+  // TODO(http://b/368674223): Add a ranking when the button is added.
+  CHECK(action_container_view_);
+  action_container_view_->AddChildView(std::make_unique<ActionButtonView>(
+      std::move(callback), text, &kCaptureModeImageIcon));
+
+  UpdateActionContainerWidget();
 }
 
 void CaptureModeSession::OnPaintLayer(const ui::PaintContext& context) {
@@ -2202,6 +2277,10 @@
 
   fine_tune_position_ = GetFineTunePosition(screen_location, is_touch);
 
+  // The capture region will be changing, so remove any existing action buttons,
+  // if any, as they will no longer be applicable.
+  RemoveAllActionButtons();
+
   if (fine_tune_position_ == FineTunePosition::kNone) {
     // If the point is outside the capture region and not on the capture bar or
     // settings menu, restart to the select phase.
@@ -2664,62 +2743,6 @@
   return capture_label_view_->ShouldHandleEvent();
 }
 
-// TODO(http://b/363069895): Upload strings for translation.
-void CaptureModeSession::UpdateActionContainerWidget() {
-  CHECK(features::IsSunfishFeatureEnabled());
-
-  if (!action_container_widget_) {
-    action_container_widget_ = std::make_unique<views::Widget>();
-    auto* parent = GetParentContainer(current_root_);
-    action_container_widget_->Init(
-        CreateWidgetParams(parent, gfx::Rect(), "ActionButtonsContainer"));
-
-    action_container_widget_->SetContentsView(
-        views::Builder<views::BoxLayoutView>()
-            .CopyAddressTo(&action_container_view_)
-            .SetOrientation(views::BoxLayout::Orientation::kHorizontal)
-            .SetBetweenChildSpacing(kActionButtonSpacing)
-            .SetMainAxisAlignment(views::BoxLayout::MainAxisAlignment::kCenter)
-            .SetCrossAxisAlignment(
-                views::BoxLayout::CrossAxisAlignment::kStretch)
-            .Build());
-
-    action_container_widget_->Show();
-  }
-
-  UpdateActionContainerWidgetBounds();
-}
-
-void CaptureModeSession::UpdateActionContainerWidgetBounds() {
-  DCHECK(action_container_widget_);
-
-  const gfx::Rect bounds = CalculateActionContainerWidgetBounds();
-  const gfx::Rect old_bounds =
-      action_container_widget_->GetNativeWindow()->GetBoundsInScreen();
-  if (old_bounds == bounds) {
-    return;
-  }
-
-  action_container_widget_->SetBounds(bounds);
-}
-
-gfx::Rect CaptureModeSession::CalculateActionContainerWidgetBounds() const {
-  DCHECK(action_container_widget_);
-
-  const gfx::Size preferred_size = action_container_view_->GetPreferredSize();
-  const gfx::Rect capture_bar_bounds =
-      action_container_widget_->GetNativeWindow()->bounds();
-
-  gfx::Rect bounds(current_root_->bounds());
-  const gfx::Rect capture_region = controller_->user_capture_region();
-  bounds = CalculateRegionEdgeBounds(preferred_size, capture_bar_bounds,
-                                     capture_region, current_root_);
-
-  // User capture bounds are in root window coordinates so convert them here.
-  wm::ConvertRectToScreen(current_root_, &bounds);
-  return bounds;
-}
-
 void CaptureModeSession::UpdateRootWindowDimmers() {
   root_window_dimmers_.clear();
 
@@ -2955,6 +2978,65 @@
           selected_window);
 }
 
+// TODO(http://b/363069895): Upload strings for translation.
+void CaptureModeSession::UpdateActionContainerWidget() {
+  DCHECK_EQ(active_behavior()->behavior_type(), BehaviorType::kSunfish);
+
+  if (!action_container_widget_) {
+    action_container_widget_ = std::make_unique<views::Widget>();
+    auto* parent = GetParentContainer(current_root_);
+    action_container_widget_->Init(
+        CreateWidgetParams(parent, gfx::Rect(), "ActionButtonsContainer"));
+
+    action_container_widget_->SetContentsView(
+        views::Builder<views::BoxLayoutView>()
+            .CopyAddressTo(&action_container_view_)
+            .SetOrientation(views::BoxLayout::Orientation::kHorizontal)
+            .SetBetweenChildSpacing(kActionButtonSpacing)
+            .SetMainAxisAlignment(views::BoxLayout::MainAxisAlignment::kCenter)
+            .SetCrossAxisAlignment(
+                views::BoxLayout::CrossAxisAlignment::kStretch)
+            .Build());
+
+    action_container_widget_->Show();
+  }
+
+  UpdateActionContainerWidgetBounds();
+}
+
+void CaptureModeSession::UpdateActionContainerWidgetBounds() {
+  DCHECK(action_container_widget_);
+
+  const gfx::Rect bounds = CalculateActionContainerWidgetBounds();
+  if (bounds != action_container_widget_->GetWindowBoundsInScreen()) {
+    action_container_widget_->SetBounds(bounds);
+  }
+}
+
+gfx::Rect CaptureModeSession::CalculateActionContainerWidgetBounds() const {
+  DCHECK(action_container_widget_);
+
+  const gfx::Size preferred_size = action_container_view_->GetPreferredSize();
+  const gfx::Rect capture_bar_bounds =
+      action_container_widget_->GetNativeWindow()->bounds();
+
+  const gfx::Rect capture_region = controller_->user_capture_region();
+  gfx::Rect bounds = CalculateRegionEdgeBounds(
+      preferred_size, capture_bar_bounds, capture_region, current_root_);
+
+  // User capture bounds are in root window coordinates so convert them here.
+  wm::ConvertRectToScreen(current_root_, &bounds);
+  return bounds;
+}
+
+void CaptureModeSession::RemoveAllActionButtons() {
+  // Remove all children from the action button container, if the widget exists.
+  if (action_container_widget_) {
+    CHECK(action_container_view_);
+    action_container_view_->RemoveAllChildViews();
+  }
+}
+
 void CaptureModeSession::InitInternal() {
   layer()->set_delegate(this);
   auto* parent = GetParentContainer(current_root_);
diff --git a/ash/capture_mode/capture_mode_session.h b/ash/capture_mode/capture_mode_session.h
index 6e42a3b1..253c53f 100644
--- a/ash/capture_mode/capture_mode_session.h
+++ b/ash/capture_mode/capture_mode_session.h
@@ -179,6 +179,9 @@
                        bool root_window_will_shutdown) override;
   std::set<aura::Window*> GetWindowsToIgnoreFromWidgets() override;
   void ShowSearchResultsPanel(const gfx::ImageSkia& image) override;
+  void AddActionButton(views::Button::PressedCallback callback,
+                       std::u16string text,
+                       const gfx::VectorIcon* icon) override;
 
   // ui::LayerDelegate:
   void OnPaintLayer(const ui::PaintContext& context) override;
@@ -329,17 +332,6 @@
   // child is visible.
   bool ShouldCaptureLabelHandleEvent(aura::Window* event_target);
 
-  // Creates the the action container widget if it wasn't previously created,
-  // and updates the widget's bounds.
-  void UpdateActionContainerWidget();
-
-  // Updates the action container widget's bounds.
-  void UpdateActionContainerWidgetBounds();
-
-  // Calculates the targeted action container widget bounds in screen
-  // coordinates.
-  gfx::Rect CalculateActionContainerWidgetBounds() const;
-
   // Updates |root_window_dimmers_| to dim the correct root windows.
   void UpdateRootWindowDimmers();
 
@@ -401,6 +393,21 @@
   // capturable window at `screen_point`. Returns false otherwise.
   bool IsPointOverSelectedWindow(const gfx::Point& screen_point) const;
 
+  // Creates the the action container widget if it wasn't previously created,
+  // and updates the widget's bounds.
+  void UpdateActionContainerWidget();
+
+  // Updates the action container widget's bounds.
+  void UpdateActionContainerWidgetBounds();
+
+  // Calculates the targeted action container widget bounds in screen
+  // coordinates.
+  gfx::Rect CalculateActionContainerWidgetBounds() const;
+
+  // Removes any existing action buttons from `action_container_view_` if the
+  // `action_container_widget_` exists,
+  void RemoveAllActionButtons();
+
   // BaseCaptureModeSession:
   void InitInternal() override;
   void ShutdownInternal() override;
diff --git a/ash/capture_mode/capture_mode_session_test_api.cc b/ash/capture_mode/capture_mode_session_test_api.cc
index 50a2363..11231a5 100644
--- a/ash/capture_mode/capture_mode_session_test_api.cc
+++ b/ash/capture_mode/capture_mode_session_test_api.cc
@@ -8,6 +8,9 @@
 #include "ash/capture_mode/capture_mode_session.h"
 #include "ash/capture_mode/capture_mode_types.h"
 #include "ash/capture_mode/recording_type_menu_view.h"
+#include "ash/style/pill_button.h"
+#include "ui/views/layout/box_layout_view.h"
+#include "ui/views/view_utils.h"
 
 namespace ash {
 
@@ -118,4 +121,20 @@
   return session_->GetSelectedWindowTargetBounds();
 }
 
+std::vector<PillButton*> CaptureModeSessionTestApi::GetActionButtons() const {
+  std::vector<PillButton*> action_buttons;
+
+  // The action container widget, and thus the container view, may not have been
+  // created yet when this function is called. In this case, return an empty
+  // vector.
+  if (session_->action_container_widget_) {
+    CHECK(session_->action_container_view_);
+    for (views::View* button : session_->action_container_view_->children()) {
+      action_buttons.emplace_back(views::AsViewClass<PillButton>(button));
+    }
+  }
+
+  return action_buttons;
+}
+
 }  // namespace ash
diff --git a/ash/capture_mode/capture_mode_session_test_api.h b/ash/capture_mode/capture_mode_session_test_api.h
index cf563cd..e382440 100644
--- a/ash/capture_mode/capture_mode_session_test_api.h
+++ b/ash/capture_mode/capture_mode_session_test_api.h
@@ -19,6 +19,7 @@
 class CaptureModeBarView;
 class CaptureModeSettingsView;
 class MagnifierGlass;
+class PillButton;
 class RecordingTypeMenuView;
 class UserNudgeController;
 
@@ -77,6 +78,10 @@
 
   gfx::Rect GetSelectedWindowTargetBounds();
 
+  // A vector of the current action buttons for a Sunfish session. Will return
+  // an empty vector if there are no buttons or there is no selected region.
+  std::vector<PillButton*> GetActionButtons() const;
+
  private:
   const raw_ptr<CaptureModeSession, DanglingUntriaged> session_;
 };
diff --git a/ash/capture_mode/capture_mode_util.cc b/ash/capture_mode/capture_mode_util.cc
index 19c20ee..3dd5a40 100644
--- a/ash/capture_mode/capture_mode_util.cc
+++ b/ash/capture_mode/capture_mode_util.cc
@@ -585,4 +585,13 @@
   return result;
 }
 
+void AddActionButton(views::Button::PressedCallback callback,
+                     std::u16string text,
+                     const gfx::VectorIcon* icon) {
+  if (auto* controller = CaptureModeController::Get(); controller->IsActive()) {
+    controller->capture_mode_session()->AddActionButton(std::move(callback),
+                                                        text, icon);
+  }
+}
+
 }  // namespace ash::capture_mode_util
diff --git a/ash/capture_mode/capture_mode_util.h b/ash/capture_mode/capture_mode_util.h
index 12657bc..f8d205b1 100644
--- a/ash/capture_mode/capture_mode_util.h
+++ b/ash/capture_mode/capture_mode_util.h
@@ -14,6 +14,7 @@
 #include "ui/gfx/animation/tween.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/views/controls/button/button.h"
 #include "ui/views/highlight_border.h"
 
 namespace aura {
@@ -236,6 +237,13 @@
     const gfx::Rect& partial_region_bounds,
     aura::Window* root_window);
 
+// TODO(http://b/368674223): Add some type of ordering mechanism to the API.
+// Adds a new action button to a sunfish capture session if the session is
+// active.
+ASH_EXPORT void AddActionButton(views::Button::PressedCallback callback,
+                                std::u16string text,
+                                const gfx::VectorIcon* icon);
+
 }  // namespace capture_mode_util
 
 }  // namespace ash
diff --git a/ash/capture_mode/null_capture_mode_session.cc b/ash/capture_mode/null_capture_mode_session.cc
index a28b735f..cebc847 100644
--- a/ash/capture_mode/null_capture_mode_session.cc
+++ b/ash/capture_mode/null_capture_mode_session.cc
@@ -116,6 +116,11 @@
 void NullCaptureModeSession::ShowSearchResultsPanel(
     const gfx::ImageSkia& image) {}
 
+void NullCaptureModeSession::AddActionButton(
+    views::Button::PressedCallback callback,
+    std::u16string text,
+    const gfx::VectorIcon* icon) {}
+
 void NullCaptureModeSession::InitInternal() {
   layer()->SetName("NullCaptureModeSession");
 }
diff --git a/ash/capture_mode/null_capture_mode_session.h b/ash/capture_mode/null_capture_mode_session.h
index de1e67b..42b4120 100644
--- a/ash/capture_mode/null_capture_mode_session.h
+++ b/ash/capture_mode/null_capture_mode_session.h
@@ -49,6 +49,9 @@
                        bool root_window_will_shutdown) override;
   std::set<aura::Window*> GetWindowsToIgnoreFromWidgets() override;
   void ShowSearchResultsPanel(const gfx::ImageSkia& image) override;
+  void AddActionButton(views::Button::PressedCallback callback,
+                       std::u16string text,
+                       const gfx::VectorIcon* icon) override;
 
  private:
   // CaptureModeSession:
diff --git a/ash/capture_mode/sunfish_unittest.cc b/ash/capture_mode/sunfish_unittest.cc
index 105217a9e..66b0c63 100644
--- a/ash/capture_mode/sunfish_unittest.cc
+++ b/ash/capture_mode/sunfish_unittest.cc
@@ -10,16 +10,20 @@
 #include "ash/capture_mode/capture_mode_session.h"
 #include "ash/capture_mode/capture_mode_session_test_api.h"
 #include "ash/capture_mode/capture_mode_test_util.h"
+#include "ash/capture_mode/capture_mode_util.h"
 #include "ash/capture_mode/search_results_panel.h"
 #include "ash/capture_mode/sunfish_capture_bar_view.h"
 #include "ash/capture_mode/test_capture_mode_delegate.h"
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_switches.h"
 #include "ash/public/cpp/capture_mode/capture_mode_test_api.h"
+#include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/scanner/scanner_controller.h"
 #include "ash/shell.h"
 #include "ash/style/icon_button.h"
+#include "ash/style/pill_button.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/test/ash_test_util.h"
 #include "ash/test/test_ash_web_view_factory.h"
 #include "base/auto_reset.h"
 #include "base/test/bind.h"
@@ -108,7 +112,7 @@
   // Immediately upon region selection, `PerformImageSearch()` and
   // `OnCaptureImageAttempted()` will be called once.
   SelectCaptureModeRegion(GetEventGenerator(), gfx::Rect(100, 100, 600, 500),
-                          /*release_mouse=*/true, /*proceed=*/true);
+                          /*release_mouse=*/true, /*verify_region=*/true);
   ASSERT_FALSE(capture_button->GetVisible());
   ASSERT_FALSE(capture_label->GetVisible());
   auto* test_delegate =
@@ -131,7 +135,7 @@
   CaptureModeSessionTestApi test_api(session);
 
   SelectCaptureModeRegion(GetEventGenerator(), gfx::Rect(100, 100, 600, 500),
-                          /*release_mouse=*/true, /*proceed=*/true);
+                          /*release_mouse=*/true, /*verify_region=*/true);
   WaitForImageCapturedForSearch();
   EXPECT_TRUE(session->search_results_panel_widget());
 
@@ -192,7 +196,7 @@
   // Tests after selecting a region, the session is ended.
   auto* event_generator = GetEventGenerator();
   SelectCaptureModeRegion(event_generator, gfx::Rect(100, 100, 600, 500),
-                          /*release_mouse=*/true, /*proceed=*/false);
+                          /*release_mouse=*/true, /*verify_region=*/false);
   EXPECT_FALSE(controller->IsActive());
   test_delegate->set_is_allowed_by_dlp(true);
 }
@@ -434,7 +438,7 @@
 
   // Test we can select a region and show the search results panel.
   SelectCaptureModeRegion(GetEventGenerator(), gfx::Rect(100, 100, 600, 500),
-                          /*release_mouse=*/true, /*proceed=*/true);
+                          /*release_mouse=*/true, /*verify_region=*/true);
   WaitForImageCapturedForSearch();
   EXPECT_TRUE(session->search_results_panel_widget());
 
@@ -467,6 +471,52 @@
   VerifyActiveBehavior(BehaviorType::kSunfish);
 }
 
+// Tests that while a sunfish session has a region selected, calling the API
+// will successfully create a new action button.
+TEST_F(SunfishTest, AddActionButton) {
+  auto* controller = CaptureModeController::Get();
+  controller->StartSunfishSession();
+  ASSERT_TRUE(controller->IsActive());
+  auto* session =
+      static_cast<CaptureModeSession*>(controller->capture_mode_session());
+
+  CaptureModeSessionTestApi session_test_api(
+      controller->capture_mode_session());
+  EXPECT_EQ(session_test_api.GetActionButtons().size(), 0u);
+
+  // Attempt to add a new action button using the API.
+  capture_mode_util::AddActionButton(views::Button::PressedCallback(),
+                                     u"Do not show", &kCaptureModeImageIcon);
+
+  // The region has not been selected yet, so attempting to add a button should
+  // do nothing.
+  EXPECT_EQ(session_test_api.GetActionButtons().size(), 0u);
+
+  // Select a region on the far left of the screen so we have space for the
+  // button between it and the search results panel.
+  SelectCaptureModeRegion(GetEventGenerator(), gfx::Rect(0, 0, 50, 200),
+                          /*release_mouse=*/true, /*verify_region=*/true);
+  WaitForImageCapturedForSearch();
+  EXPECT_TRUE(session->search_results_panel_widget());
+
+  // Create another action button that, when clicked, will change the value of a
+  // bool that can be verified later.
+  bool pressed = false;
+  capture_mode_util::AddActionButton(
+      base::BindLambdaForTesting([&]() { pressed = true; }), u"Test",
+      &kCaptureModeImageIcon);
+
+  // There should only be one valid button in the session.
+  const std::vector<PillButton*> action_buttons =
+      session_test_api.GetActionButtons();
+  EXPECT_EQ(action_buttons.size(), 1u);
+
+  // Clicking the button should successfully run the callback, and change the
+  // value of the bool.
+  LeftClickOn(action_buttons[0]);
+  ASSERT_TRUE(pressed);
+}
+
 class SunfishWithScannerTest : public SunfishTest {
  public:
   SunfishWithScannerTest() = default;
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 0804a09..49763b4 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -381,10 +381,8 @@
 // shortcut and feature tile.
 BASE_FEATURE(kCaptureModeEducation,
              "CaptureModeEducation",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             base::FEATURE_DISABLED_BY_DEFAULT);
 
-// TODO(hewer): Remove the unused paths after at least one milestone after
-// Capture Mode Education has been enabled by default.
 // Determines how we educate the user to the screen capture entry points.
 constexpr base::FeatureParam<CaptureModeEducationParam>::Option
     capture_mode_education_type_options[] = {
diff --git a/ash/webui/common/resources/BUILD.gn b/ash/webui/common/resources/BUILD.gn
index 45342ff8..1461c2f0 100644
--- a/ash/webui/common/resources/BUILD.gn
+++ b/ash/webui/common/resources/BUILD.gn
@@ -47,7 +47,6 @@
   "network/network_property_list_mojo.js",
   "network/network_proxy_exclusions.js",
   "network/network_proxy_input.js",
-  "network/network_proxy.js",
   "network/network_select.js",
   "network/sim_lock_dialogs.js",
   "network/apn_detail_dialog.js",
@@ -113,6 +112,7 @@
   "bluetooth/bluetooth_pairing_ui.ts",
   "bluetooth/bluetooth_spinner_page.ts",
   "network/network_nameservers.ts",
+  "network/network_proxy.ts",
   "network/network_siminfo.ts",
   "network_health/network_diagnostics.ts",
   "network_health/network_health_container.ts",
@@ -454,6 +454,7 @@
   "network/network_config_element_behavior.js",
   "network/network_listener_behavior.js",
   "network/network_nameservers.html.js",
+  "network/network_proxy.html.js",
   "network/network_proxy_exclusions.html.js",
   "network/network_proxy_exclusions.js",
   "network/network_proxy_input.html.js",
@@ -546,7 +547,6 @@
   "network/network_list_types.d.ts",
   "network/network_password_input.d.ts",
   "network/network_property_list_mojo.d.ts",
-  "network/network_proxy.d.ts",
   "network/network_select.d.ts",
   "network/sim_lock_dialogs.d.ts",
 
diff --git a/ash/webui/common/resources/network/BUILD.gn b/ash/webui/common/resources/network/BUILD.gn
index 7ffe5e5..64cd0322 100644
--- a/ash/webui/common/resources/network/BUILD.gn
+++ b/ash/webui/common/resources/network/BUILD.gn
@@ -35,7 +35,6 @@
     ":network_listener_behavior",
     ":network_password_input",
     ":network_property_list_mojo",
-    ":network_proxy",
     ":network_proxy_exclusions",
     ":network_proxy_input",
     ":network_select",
@@ -312,24 +311,6 @@
   ]
 }
 
-js_library("network_proxy") {
-  deps = [
-    ":cr_policy_network_behavior_mojo",
-    ":network_proxy_exclusions",
-    ":network_proxy_input",
-    ":onc_mojo",
-    "//ash/webui/common/resources:assert",
-    "//ash/webui/common/resources:i18n_behavior",
-    "//third_party/polymer/v3_0/components-chromium/iron-flex-layout:iron-flex-layout-classes",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-  ]
-  externs_list = [
-    "//ash/webui/common/resources/cr_elements/cr_button/cr_button_externs.js",
-    "//ash/webui/common/resources/cr_elements/cr_input/cr_input_externs.js",
-    "//ash/webui/common/resources/cr_elements/cr_toggle/cr_toggle_externs.js",
-  ]
-}
-
 js_library("network_proxy_exclusions") {
   deps = [
     "//ash/webui/common/resources:i18n_behavior",
diff --git a/ash/webui/common/resources/network/network_proxy.d.ts b/ash/webui/common/resources/network/network_proxy.d.ts
deleted file mode 100644
index c567313..0000000
--- a/ash/webui/common/resources/network/network_proxy.d.ts
+++ /dev/null
@@ -1,10 +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.
-
-import {LegacyElementMixin} from '//resources/polymer/v3_0/polymer/lib/legacy/legacy-element-mixin.js';
-interface NetworkProxyElement extends LegacyElementMixin, HTMLElement {
-  editable: boolean;
-  reset(): void;
-}
-export {NetworkProxyElement};
diff --git a/ash/webui/common/resources/network/network_proxy.html b/ash/webui/common/resources/network/network_proxy.html
index 10f636c0..121a8ed 100644
--- a/ash/webui/common/resources/network/network_proxy.html
+++ b/ash/webui/common/resources/network/network_proxy.html
@@ -40,7 +40,7 @@
     hidden$="[[!matches_(proxy_.type.activeValue, 'PAC')]]">
   <cr-input id="pacInput" class="flex"
       label="[[i18n('networkProxyAutoConfig')]]"
-      value="{{proxy_.pac.activeValue}}" on-change="onPACChange_"
+      value="{{proxy_.pac.activeValue}}" on-change="onPacChange_"
       disabled="[[!isEditable_('pac', managedProperties, editable,
           useSharedProxies)]]">
   </cr-input>
@@ -120,7 +120,7 @@
           aria-label="[[i18n('networkProxyExceptionInputA11yLabel')]]"
           on-keypress="onAddProxyExclusionKeypress_">
         <cr-button id="proxyExclusionButton"
-            on-click="onAddProxyExclusionTap_"
+            on-click="onAddProxyExclusionClicked_"
             slot="suffix"
             disabled="[[shouldProxyExclusionButtonBeDisabled_(
                 proxyExclusionInputValue_)]]">
@@ -131,7 +131,7 @@
   </div>
 
   <cr-button id="saveManualProxy"
-      on-click="onSaveProxyTap_" class="action-button"
+      on-click="onSaveProxyClicked_" class="action-button"
       disabled="[[!isSaveManualProxyEnabled_(managedProperties,
           proxyIsUserModified_, proxy_.*)]]">
     [[i18n('save')]]
diff --git a/ash/webui/common/resources/network/network_proxy.js b/ash/webui/common/resources/network/network_proxy.ts
similarity index 66%
rename from ash/webui/common/resources/network/network_proxy.js
rename to ash/webui/common/resources/network/network_proxy.ts
index b37a9a7f..432f8e70 100644
--- a/ash/webui/common/resources/network/network_proxy.js
+++ b/ash/webui/common/resources/network/network_proxy.ts
@@ -17,30 +17,35 @@
 import './network_proxy_input.js';
 import './network_shared.css.js';
 
-import {assert} from '//resources/ash/common/assert.js';
-import {I18nBehavior, I18nBehaviorInterface} from '//resources/ash/common/i18n_behavior.js';
-import {ManagedManualProxySettings, ManagedProperties, ManagedProxyLocation, ManagedProxySettings, ManagedStringList, ProxyLocation, ProxySettings} from '//resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
-import {IPConfigType, OncSource, PolicySource} from '//resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
-import {mixinBehaviors, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {microTask} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {I18nMixin, I18nMixinInterface} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
+import {assert} from 'chrome://resources/js/assert.js';
+import {ManagedManualProxySettings, ManagedProperties, ManagedProxyLocation, ManagedProxySettings, ManagedStringList, ManualProxySettings, ProxyLocation, ProxySettings} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
+import {IPConfigType, OncSource, PolicySource} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
+import {microTask, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {CrPolicyNetworkBehaviorMojo, CrPolicyNetworkBehaviorMojoInterface} from './cr_policy_network_behavior_mojo.js';
 import {getTemplate} from './network_proxy.html.js';
 import {OncMojo} from './onc_mojo.js';
 
-/**
- * @constructor
- * @extends {PolymerElement}
- * @implements {I18nBehaviorInterface}
- * @implements {CrPolicyNetworkBehaviorMojoInterface}
- */
-const NetworkProxyElementBase =
-    mixinBehaviors([CrPolicyNetworkBehaviorMojo, I18nBehavior], PolymerElement);
+function createDefaultProxySettings(): ManagedProxySettings {
+  return {
+    type: OncMojo.createManagedString('Direct'),
+    manual: undefined,
+    excludeDomains: undefined,
+    pac: undefined,
+  };
+}
 
-/** @polymer */
-class NetworkProxyElement extends NetworkProxyElementBase {
+type Constructor<T> = new (...args: any[]) => T;
+
+const NetworkProxyElementBase =
+    mixinBehaviors([CrPolicyNetworkBehaviorMojo], I18nMixin(PolymerElement)) as
+    Constructor<PolymerElement&I18nMixinInterface&
+                CrPolicyNetworkBehaviorMojoInterface>;
+
+export class NetworkProxyElement extends NetworkProxyElementBase {
   static get is() {
-    return 'network-proxy';
+    return 'network-proxy' as const;
   }
 
   static get template() {
@@ -55,7 +60,6 @@
         value: false,
       },
 
-      /** @type {!ManagedProperties|undefined} */
       managedProperties: {
         type: Object,
         observer: 'managedPropertiesChanged_',
@@ -70,18 +74,16 @@
 
       /**
        * UI visible / edited proxy configuration.
-       * @private {!ManagedProxySettings}
        */
       proxy_: {
         type: Object,
         value() {
-          return this.createDefaultProxySettings_();
+          return createDefaultProxySettings();
         },
       },
 
       /**
        * The Web Proxy Auto Discovery URL extracted from managedProperties.
-       * @private
        */
       wpad_: {
         type: String,
@@ -90,7 +92,6 @@
 
       /**
        * Whether or not to use the same manual proxy for all protocols.
-       * @private
        */
       useSameProxy_: {
         type: Boolean,
@@ -100,8 +101,6 @@
 
       /**
        * Array of proxy configuration types.
-       * @private {!Array<string>}
-       * @const
        */
       proxyTypes_: {
         type: Array,
@@ -111,7 +110,6 @@
 
       /**
        * The current value of the proxy exclusion input.
-       * @private
        */
       proxyExclusionInputValue_: {
         type: String,
@@ -121,7 +119,6 @@
       /**
        * Set to true while modifying proxy values so that an update does not
        * override the edited values.
-       * @private {boolean}
        */
       proxyIsUserModified_: {
         type: Boolean,
@@ -131,23 +128,30 @@
     };
   }
 
-  /** @override */
-  connectedCallback() {
+  editable: boolean;
+  managedProperties: ManagedProperties|undefined;
+  useSharedProxies: boolean;
+  private proxy_: ManagedProxySettings;
+  private wpad_: string;
+  private useSameProxy_: boolean;
+  private proxyTypes_: [];
+  private proxyExclusionInputValue_: string;
+  private proxyIsUserModified_: boolean;
+
+  /**
+   * Saved ExcludeDomains properties so that switching to a non-Manual type
+   * does not loose any set exclusions while the UI is open.
+   */
+  private savedManual_: ManagedManualProxySettings|undefined = undefined;
+
+  /**
+   * Saved Manual properties so that switching to another type does not loose
+   * any set properties while the UI is open.
+   */
+  private savedExcludeDomains_: ManagedStringList|undefined = undefined;
+
+  override connectedCallback() {
     super.connectedCallback();
-
-    /**
-     * Saved ExcludeDomains properties so that switching to a non-Manual type
-     * does not loose any set exclusions while the UI is open.
-     * @private {!ManagedManualProxySettings|undefined}
-     */
-    this.savedManual_ = undefined;
-
-    /**
-     * Saved Manual properties so that switching to another type does not loose
-     * any set properties while the UI is open.
-     * @private {!ManagedStringList|undefined}
-     */
-    this.savedExcludeDomains_ = undefined;
     this.reset();
   }
 
@@ -160,12 +164,9 @@
     this.updateProxy_();
   }
 
-  /**
-   * @param {!ManagedProperties|undefined} newValue
-   * @param {!ManagedProperties|undefined} oldValue
-   * @private
-   */
-  managedPropertiesChanged_(newValue, oldValue) {
+  private managedPropertiesChanged_(
+      newValue: ManagedProperties|undefined,
+      oldValue: ManagedProperties|undefined) {
     if ((newValue && newValue.guid) !== (oldValue && oldValue.guid)) {
       // Clear saved manual properties and exclude domains if we're updating
       // to show a different network.
@@ -181,15 +182,11 @@
     this.updateProxy_();
   }
 
-  /**
-   * @return {boolean} True if any input elements are currently being edited.
-   * @private
-   */
-  isInputEditInProgress_() {
+  private isInputEditInProgress_(): boolean {
     if (!this.editable) {
       return false;
     }
-    const activeElement = this.shadowRoot.activeElement;
+    const activeElement = this.shadowRoot!.activeElement;
     if (!activeElement) {
       return false;
     }
@@ -219,23 +216,14 @@
     return this.isEditable_(property);
   }
 
-  /**
-   * @param {?ManagedProxyLocation|undefined} a
-   * @param {?ManagedProxyLocation|undefined} b
-   * @return {boolean}
-   * @private
-   */
-  proxyMatches_(a, b) {
+  private proxyMatches_(
+      a: ManagedProxyLocation|undefined|null,
+      b: ManagedProxyLocation|undefined|null): boolean {
     return !!a && !!b && a.host.activeValue === b.host.activeValue &&
         a.port.activeValue === b.port.activeValue;
   }
 
-  /**
-   * @param {number} port
-   * @return {!ManagedProxyLocation}
-   * @private
-   */
-  createDefaultProxyLocation_(port) {
+  private createDefaultProxyLocation_(port: number): ManagedProxyLocation {
     return {
       host: OncMojo.createManagedString(''),
       port: OncMojo.createManagedInt(port),
@@ -244,20 +232,19 @@
 
   /**
    * Returns a copy of |inputProxy| with all required properties set correctly.
-   * @param {!ManagedProxySettings} inputProxy
-   * @return {!ManagedProxySettings}
-   * @private
    */
-  validateProxy_(inputProxy) {
-    const proxy =
-        /** @type {!ManagedProxySettings} */ (Object.assign({}, inputProxy));
+  private validateProxy_(inputProxy: ManagedProxySettings):
+      ManagedProxySettings {
+    const proxy = {...inputProxy};
     const type = proxy.type.activeValue;
     if (type === 'PAC') {
       if (!proxy.pac) {
         proxy.pac = OncMojo.createManagedString('');
       }
     } else if (type === 'Manual') {
-      proxy.manual = proxy.manual || this.savedManual_ || {};
+      proxy.manual =
+          proxy.manual || this.savedManual_ || new ManagedManualProxySettings();
+      assert(proxy.manual);
       if (!proxy.manual.httpProxy) {
         proxy.manual.httpProxy = this.createDefaultProxyLocation_(80);
       }
@@ -271,13 +258,13 @@
           proxy.excludeDomains || this.savedExcludeDomains_ || {
             activeValue: [],
             policySource: PolicySource.kNone,
+            policyValue: undefined,
           };
     }
     return proxy;
   }
 
-  /** @private */
-  updateProxy_() {
+  private updateProxy_(): void {
     if (!this.managedProperties) {
       return;
     }
@@ -288,11 +275,11 @@
     // settings and use the default value.
     if (this.isShared_() && proxySettings &&
         !this.isControlled(proxySettings.type) && !this.useSharedProxies) {
-      proxySettings = null;  // Ignore proxy settings.
+      proxySettings = undefined;  // Ignore proxy settings.
     }
 
     const proxy = proxySettings ? this.validateProxy_(proxySettings) :
-                                  this.createDefaultProxySettings_();
+                                  createDefaultProxySettings();
 
     if (proxy.type.activeValue === 'WPAD') {
       // Set the Web Proxy Auto Discovery URL for display purposes.
@@ -308,11 +295,7 @@
     microTask.run(() => this.setProxy_(proxy));
   }
 
-  /**
-   * @param {!ManagedProxySettings} proxy
-   * @private
-   */
-  setProxy_(proxy) {
+  private setProxy_(proxy: ManagedProxySettings): void {
     this.proxy_ = proxy;
     if (proxy.manual) {
       const manual = proxy.manual;
@@ -322,8 +305,8 @@
         // If all four proxies match, enable the 'use same proxy' toggle.
         this.useSameProxy_ = true;
       } else if (
-          !manual.secureHttpProxy.host.activeValue &&
-          !manual.socks.host.activeValue) {
+          !manual.secureHttpProxy?.host?.activeValue &&
+          !manual.socks?.host?.activeValue) {
         // Otherwise if no proxies other than http have a host value, also
         // enable the 'use same proxy' toggle.
         this.useSameProxy_ = true;
@@ -332,28 +315,12 @@
     this.proxyIsUserModified_ = false;
   }
 
-  /** @private */
-  useSameProxyChanged_() {
+  private useSameProxyChanged_(): void {
     this.proxyIsUserModified_ = true;
   }
 
-  /**
-   * @return {!ManagedProxySettings}
-   * @private
-   */
-  createDefaultProxySettings_() {
-    return {
-      type: OncMojo.createManagedString('Direct'),
-    };
-  }
-
-  /**
-   * @param {?ManagedProxyLocation|undefined}
-   *     location
-   * @return {!ProxyLocation|undefined}
-   * @private
-   */
-  getProxyLocation_(location) {
+  private getProxyLocation_(location: ManagedProxyLocation|undefined|
+                            null): ProxyLocation|undefined {
     if (!location) {
       return undefined;
     }
@@ -365,43 +332,38 @@
 
   /**
    * Called when the proxy changes in the UI.
-   * @private
    */
-  sendProxyChange_() {
+  private sendProxyChange_(): void {
     const proxyType = OncMojo.getActiveString(this.proxy_.type);
     if (!proxyType || (proxyType === 'PAC' && !this.proxy_.pac)) {
       return;
     }
 
-    const proxy = /** @type {!ProxySettings} */ ({
-      type: proxyType,
-      excludeDomains: OncMojo.getActiveValue(this.proxy_.excludeDomains),
-    });
+    const proxy = new ProxySettings();
+    proxy.type = proxyType;
+    proxy.excludeDomains =
+        OncMojo.getActiveValue(this.proxy_.excludeDomains) as string[] |
+        undefined;
 
     if (proxyType === 'Manual') {
-      let manual = {};
+      let manual = new ManualProxySettings();
       if (this.proxy_.manual) {
-        this.savedManual_ =
-            /** @type{!ManagedManualProxySettings}*/ (
-                Object.assign({}, this.proxy_.manual));
+        this.savedManual_ = {...this.proxy_.manual};
         manual = {
           httpProxy: this.getProxyLocation_(this.proxy_.manual.httpProxy),
           secureHttpProxy:
               this.getProxyLocation_(this.proxy_.manual.secureHttpProxy),
+          ftpProxy: undefined,
           socks: this.getProxyLocation_(this.proxy_.manual.socks),
         };
       }
       if (this.proxy_.excludeDomains) {
-        this.savedExcludeDomains_ =
-            /** @type{!ManagedStringList}*/ (
-                Object.assign({}, this.proxy_.excludeDomains));
+        this.savedExcludeDomains_ = {...this.proxy_.excludeDomains};
       }
       const defaultProxy = manual.httpProxy || {host: '', port: 80};
       if (this.useSameProxy_) {
-        manual.secureHttpProxy =
-            /** @type {!ProxyLocation} */ (Object.assign({}, defaultProxy));
-        manual.socks =
-            /** @type {!ProxyLocation} */ (Object.assign({}, defaultProxy));
+        manual.secureHttpProxy = {...defaultProxy};
+        manual.socks = {...defaultProxy};
       } else {
         // Remove properties with empty hosts to unset them.
         if (manual.httpProxy && !manual.httpProxy.host) {
@@ -428,14 +390,12 @@
 
   /**
    * Event triggered when the selected proxy type changes.
-   * @param {!Event} event
-   * @private
    */
-  onTypeChange_(event) {
+  private onTypeChange_(event: Event): void {
     if (!this.proxy_ || !this.proxy_.type) {
       return;
     }
-    const target = /** @type {!HTMLSelectElement} */ (event.target);
+    const target = event.target as HTMLSelectElement;
     const type = target.value;
     this.proxy_.type.activeValue = type;
     this.set('proxy_', this.validateProxy_(this.proxy_));
@@ -448,7 +408,7 @@
         proxyTypeChangeIsReady = true;
         break;
       case 'PAC':
-        elementToFocus = this.shadowRoot.querySelector('#pacInput');
+        elementToFocus = this.shadowRoot!.querySelector('#pacInput');
         // If a PAC is already set, send the type change now, otherwise wait
         // until the user provides a PAC value.
         proxyTypeChangeIsReady = !!OncMojo.getActiveString(this.proxy_.pac);
@@ -458,7 +418,7 @@
         // until the 'send' button is clicked.
         proxyTypeChangeIsReady = false;
         elementToFocus =
-            this.shadowRoot.querySelector('#manualProxy network-proxy-input');
+            this.shadowRoot!.querySelector('#manualProxy network-proxy-input');
         break;
     }
 
@@ -476,18 +436,15 @@
     }
   }
 
-  /** @private */
-  onPACChange_() {
+  private onPacChange_(): void {
     this.sendProxyChange_();
   }
 
-  /** @private */
-  onProxyInputChange_() {
+  private onProxyInputChange_(): void {
     this.proxyIsUserModified_ = true;
   }
 
-  /** @private */
-  onAddProxyExclusionTap_() {
+  private onAddProxyExclusionClicked_(): void {
     assert(this.proxyExclusionInputValue_);
     this.push(
         'proxy_.excludeDomains.activeValue', this.proxyExclusionInputValue_);
@@ -496,47 +453,31 @@
     this.proxyIsUserModified_ = true;
   }
 
-  /**
-   * @param {!Event} event
-   * @private
-   */
-  onAddProxyExclusionKeypress_(event) {
+  private onAddProxyExclusionKeypress_(event: KeyboardEvent): void {
     if (event.key !== 'Enter') {
       return;
     }
     event.stopPropagation();
-    this.onAddProxyExclusionTap_();
+    this.onAddProxyExclusionClicked_();
   }
 
-  /**
-   * @param {string} proxyExclusionInputValue
-   * @return {boolean}
-   * @private
-   */
-  shouldProxyExclusionButtonBeDisabled_(proxyExclusionInputValue) {
+  private shouldProxyExclusionButtonBeDisabled_(proxyExclusionInputValue:
+                                                    string): boolean {
     return !proxyExclusionInputValue;
   }
 
   /**
    * Event triggered when the proxy exclusion list changes.
-   * @param {!Event} event The remove proxy exclusions change event.
-   * @private
    */
-  onProxyExclusionsChange_(event) {
+  private onProxyExclusionsChange_(): void {
     this.proxyIsUserModified_ = true;
   }
 
-  /** @private */
-  onSaveProxyTap_() {
+  private onSaveProxyClicked_(): void {
     this.sendProxyChange_();
   }
 
-  /**
-   * @param {string} proxyType The proxy type.
-   * @return {string} The description for |proxyType|.
-   * @private
-   */
-  getProxyTypeDesc_(proxyType) {
+  private getProxyTypeDesc_(proxyType: string): string {
     if (proxyType === 'Manual') {
       return this.i18n('networkProxyTypeManual');
     }
@@ -549,38 +490,25 @@
     return this.i18n('networkProxyTypeDirect');
   }
 
-  /**
-   * @param {string} propertyName
-   * @return {boolean} Whether the named property setting is editable.
-   * @private
-   */
-  isEditable_(propertyName) {
+  private isEditable_(propertyName: string): boolean {
     if (!this.editable || (this.isShared_() && !this.useSharedProxies)) {
       return false;
     }
-    const property = /** @type {!OncMojo.ManagedProperty|undefined} */ (
-        this.get('proxySettings.' + propertyName, this.managedProperties));
+    const property =
+        this.get('proxySettings.' + propertyName, this.managedProperties);
     if (!property) {
       return true;  // Default to editable if property is not defined.
     }
     return this.isPropertyEditable_(property);
   }
 
-  /**
-   * @param {!OncMojo.ManagedProperty|undefined} property
-   * @return {boolean} Whether |property| is editable.
-   * @private
-   */
-  isPropertyEditable_(property) {
+  private isPropertyEditable_(property: OncMojo.ManagedProperty|
+                              undefined): boolean {
     return !!property && !this.isNetworkPolicyEnforced(property) &&
         !this.isExtensionControlled(property);
   }
 
-  /**
-   * @return {boolean}
-   * @private
-   */
-  isShared_() {
+  private isShared_(): boolean {
     if (!this.managedProperties) {
       return false;
     }
@@ -588,11 +516,7 @@
     return source === OncSource.kDevice || source === OncSource.kDevicePolicy;
   }
 
-  /**
-   * @return {boolean}
-   * @private
-   */
-  isSaveManualProxyEnabled_() {
+  private isSaveManualProxyEnabled_(): boolean {
     if (!this.proxyIsUserModified_) {
       return false;
     }
@@ -606,13 +530,7 @@
         !!this.get('socks.host.activeValue', manual);
   }
 
-  /**
-   * @param {string} property The property to test
-   * @param {string} value The value to test against
-   * @return {boolean} True if property === value
-   * @private
-   */
-  matches_(property, value) {
+  private matches_(property: string, value: string): boolean {
     return property === value;
   }
 }
diff --git a/ash/webui/demo_mode_app_ui/resources/demo_mode_metrics_service.js b/ash/webui/demo_mode_app_ui/resources/demo_mode_metrics_service.js
index 914b5d2..5eee8a8 100644
--- a/ash/webui/demo_mode_app_ui/resources/demo_mode_metrics_service.js
+++ b/ash/webui/demo_mode_app_ui/resources/demo_mode_metrics_service.js
@@ -81,6 +81,11 @@
 
   // Enum shared between CB & CBX are: BATTERY, GOOGLE_APPS, NEARBY_SHARE,
   // MESSAGING, BUILT_IN_SECURITY,MS_365_APPS, SWITCHING, COMPARISON
+
+  // New in 2024 Cycle 2 refresh
+  // CBX:
+  HELP_ME_READ: 'HelpMeRead',
+  LIVE_TRANSLATE: 'LiveTranslate',
 };
 
 /**
diff --git a/base/allocator/partition_allocator/partition_alloc.gni b/base/allocator/partition_allocator/partition_alloc.gni
index b78f042..423c081 100644
--- a/base/allocator/partition_allocator/partition_alloc.gni
+++ b/base/allocator/partition_allocator/partition_alloc.gni
@@ -286,12 +286,21 @@
 # PartitionAlloc at all. If `use_partition_alloc` is false, we jam all
 # related args to `false`.
 #
+# We also disable PA-Everywhere and PA-based features in two types of
+# toolchains:
+# - Toolchains that disable PA-Everywhere explicitly.
+# - The rust host build tools toochain, which builds DLLs to dlopen into the
+#   compiler for proc macros. We would want any allocations to use the same
+#   paths as the compiler.
+#
 # Do not clear the following, as they can function outside of PartitionAlloc
 # - has_64_bit_pointers
 # - has_memory_tagging
 if (!use_partition_alloc ||
     (defined(toolchain_allows_use_partition_alloc_as_malloc) &&
-     !toolchain_allows_use_partition_alloc_as_malloc)) {
+     !toolchain_allows_use_partition_alloc_as_malloc) ||
+    (defined(toolchain_for_rust_host_build_tools) &&
+     toolchain_for_rust_host_build_tools)) {
   use_partition_alloc_as_malloc = false
   glue_core_pools = false
   enable_backup_ref_ptr_support = false
diff --git a/base/android/javatests/src/org/chromium/base/task/ChainedTasksTest.java b/base/android/javatests/src/org/chromium/base/task/ChainedTasksTest.java
index e60442c..9c0b5df 100644
--- a/base/android/javatests/src/org/chromium/base/task/ChainedTasksTest.java
+++ b/base/android/javatests/src/org/chromium/base/task/ChainedTasksTest.java
@@ -132,7 +132,7 @@
                         secondTaskFinished.notifyCalled();
                         waitForHighPriorityTask.waitForOnly();
                     } catch (TimeoutException e) {
-                        Assert.fail();
+                        throw new RuntimeException(e);
                     }
                 });
         tasks.add(TaskTraits.UI_DEFAULT, new TestRunnable(messages, "Third"));
diff --git a/base/android/junit/src/org/chromium/base/UnownedUserDataKeyTest.java b/base/android/junit/src/org/chromium/base/UnownedUserDataKeyTest.java
index 69431443..66ad3c1 100644
--- a/base/android/junit/src/org/chromium/base/UnownedUserDataKeyTest.java
+++ b/base/android/junit/src/org/chromium/base/UnownedUserDataKeyTest.java
@@ -124,6 +124,7 @@
         mHost2 = null;
     }
 
+    @SuppressWarnings({"SelfAssertion", "JUnitIncompatibleType"})
     @Test
     public void testKeyEquality() {
         assertEquals(Foo.KEY, Foo.KEY);
diff --git a/base/hash/sha1_boringssl.cc b/base/hash/sha1_boringssl.cc
index 957ee47..90eeac7 100644
--- a/base/hash/sha1_boringssl.cc
+++ b/base/hash/sha1_boringssl.cc
@@ -36,7 +36,7 @@
   SHA1_Init(&context);
 }
 
-void SHA1Update(const std::string_view data, SHA1Context& context) {
+void SHA1Update(std::string_view data, SHA1Context& context) {
   SHA1_Update(&context, data.data(), data.size());
 }
 
diff --git a/base/test/launcher/test_launcher.cc b/base/test/launcher/test_launcher.cc
index 633a4875..99cf980 100644
--- a/base/test/launcher/test_launcher.cc
+++ b/base/test/launcher/test_launcher.cc
@@ -962,7 +962,7 @@
 
 // Truncates a snippet in the middle to the given byte limit. byte_limit should
 // be at least 30.
-std::string TruncateSnippet(const std::string_view snippet, size_t byte_limit) {
+std::string TruncateSnippet(std::string_view snippet, size_t byte_limit) {
   if (snippet.length() <= byte_limit) {
     return std::string(snippet);
   }
@@ -2385,7 +2385,7 @@
   return snippet;
 }
 
-std::string TruncateSnippetFocused(const std::string_view snippet,
+std::string TruncateSnippetFocused(std::string_view snippet,
                                    size_t byte_limit) {
   // Find the start of anything that looks like a fatal log message.
   // We want to preferentially preserve these from truncation as we
diff --git a/base/test/launcher/test_launcher.h b/base/test/launcher/test_launcher.h
index 3d71a6f..ffae4e7 100644
--- a/base/test/launcher/test_launcher.h
+++ b/base/test/launcher/test_launcher.h
@@ -393,8 +393,7 @@
 
 // Truncates a snippet to approximately the allowed length, while trying to
 // retain fatal messages. Exposed for testing only.
-std::string TruncateSnippetFocused(const std::string_view snippet,
-                                   size_t byte_limit);
+std::string TruncateSnippetFocused(std::string_view snippet, size_t byte_limit);
 
 }  // namespace base
 
diff --git a/base/test/scoped_feature_list_unittest.cc b/base/test/scoped_feature_list_unittest.cc
index b98223b..60fe222 100644
--- a/base/test/scoped_feature_list_unittest.cc
+++ b/base/test/scoped_feature_list_unittest.cc
@@ -39,7 +39,7 @@
   EXPECT_EQ(disabled_features, actual_disabled_features);
 }
 
-std::string GetActiveFieldTrialGroupName(const std::string trial_name) {
+std::string GetActiveFieldTrialGroupName(const std::string& trial_name) {
   FieldTrial::ActiveGroups groups;
   FieldTrialList::GetActiveFieldTrialGroups(&groups);
   for (const auto& group : groups) {
diff --git a/base/test/test_proto_loader.cc b/base/test/test_proto_loader.cc
index 8ee91dfd..01acd65a 100644
--- a/base/test/test_proto_loader.cc
+++ b/base/test/test_proto_loader.cc
@@ -63,7 +63,7 @@
 TestProtoSetLoader::~TestProtoSetLoader() = default;
 
 std::string TestProtoSetLoader::ParseFromText(
-    const std::string_view type_name,
+    std::string_view type_name,
     const std::string& proto_text) const {
   // Create a message of the given type, parse, and return.
   std::unique_ptr<google::protobuf::Message> message =
@@ -74,7 +74,7 @@
 }
 
 std::string TestProtoSetLoader::PrintToText(
-    const std::string_view type_name,
+    std::string_view type_name,
     const std::string& serialized_message) const {
   // Create a message of the given type, read the serialized message, and
   // print to text format.
diff --git a/base/trace_event/memory_dump_manager_unittest.cc b/base/trace_event/memory_dump_manager_unittest.cc
index da4cd52a2..6b9f562 100644
--- a/base/trace_event/memory_dump_manager_unittest.cc
+++ b/base/trace_event/memory_dump_manager_unittest.cc
@@ -241,7 +241,7 @@
     mdm_->SetupForTracing(TraceConfig::MemoryDumpConfig());
   }
 
-  void EnableForTracingWithTraceConfig(const std::string trace_config_string) {
+  void EnableForTracingWithTraceConfig(const std::string& trace_config_string) {
     TraceConfig trace_config(trace_config_string);
     mdm_->SetupForTracing(trace_config.memory_dump_config());
   }
diff --git a/third_party/crubit/BUILD.gn b/build/rust/crubit/BUILD.gn
similarity index 90%
rename from third_party/crubit/BUILD.gn
rename to build/rust/crubit/BUILD.gn
index a14822b..2bc6eb0 100644
--- a/third_party/crubit/BUILD.gn
+++ b/build/rust/crubit/BUILD.gn
@@ -2,11 +2,12 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/rust.gni")
 import("//build/rust/rust_macro.gni")
 import("//build/rust/rust_static_library.gni")
 
 # Common constants.
-_support_dir = "src/rs_bindings_from_cc/support"
+_support_dir = "{rust_sysroot}/lib/crubit"
 
 # Dependencies of ..._rs_api.rs files generated by Crubit's
 # `bin/rs_bindings_from_cc` tool.  See also `deps_for_generated_rs_file` in
@@ -31,6 +32,8 @@
   ]
 }
 
+# TODO: crbug.com/40226863 - Crubit should publish a Cargo.toml for these
+# crates, then we can generate rules for them dynamically with gnrt.
 rust_static_library("ctor") {
   crate_root = "${_support_dir}/ctor.rs"
   sources = [ "${_support_dir}/ctor.rs" ]
diff --git a/build/rust/rustc_wrapper.py b/build/rust/rustc_wrapper.py
index b8e490d..8f2096dfe 100755
--- a/build/rust/rustc_wrapper.py
+++ b/build/rust/rustc_wrapper.py
@@ -177,6 +177,7 @@
     # Work around for "-l<foo>.lib", where ".lib" suffix is undesirable.
     # Full fix will come from https://gn-review.googlesource.com/c/gn/+/12480
     rsp_args = [remove_lib_suffix_from_l_args(arg) for arg in rsp_args]
+    rustc_args = [remove_lib_suffix_from_l_args(arg) for arg in rustc_args]
   out_rsp = str(args.rsp) + ".rsp"
   with open(out_rsp, 'w') as rspfile:
     # rustc needs the rsp file to be separated by newlines. Note that GN
diff --git a/cc/slim/frame_sink.cc b/cc/slim/frame_sink.cc
index d780a9d..1287001 100644
--- a/cc/slim/frame_sink.cc
+++ b/cc/slim/frame_sink.cc
@@ -6,9 +6,12 @@
 
 #include <utility>
 
+#include "base/feature_list.h"
 #include "base/memory/ptr_util.h"
 #include "cc/slim/delayed_scheduler.h"
 #include "cc/slim/frame_sink_impl.h"
+#include "cc/slim/simple_scheduler.h"
+#include "components/viz/common/features.h"
 
 namespace cc::slim {
 
@@ -23,6 +26,15 @@
     // Parameters below only used when wrapping cc.
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
     base::PlatformThreadId io_thread_id) {
+#if BUILDFLAG(IS_ANDROID)
+  if (base::FeatureList::IsEnabled(features::kAndroidBcivWithSimpleScheduler)) {
+    return base::WrapUnique<FrameSink>(new FrameSinkImpl(
+        std::move(task_runner),
+        std::move(compositor_frame_sink_associated_remote),
+        std::move(client_receiver), std::move(context_provider), io_thread_id,
+        std::make_unique<SimpleScheduler>()));
+  }
+#endif
   return base::WrapUnique<FrameSink>(
       new FrameSinkImpl(std::move(task_runner),
                         std::move(compositor_frame_sink_associated_remote),
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 33c1c45..f0559ab 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -293,7 +293,6 @@
   java_group("delegate_public_impl_java") {
     deps = [
       "//chrome/android/modules/readaloud/public:provider_public_java",
-      "//chrome/browser/lens:delegate_public_impl_java",
       "//chrome/browser/locale:delegate_public_impl_java",
       "//chrome/browser/supervised_user:parent_auth_delegate_impl_java",
       "//chrome/browser/touch_to_fill/password_manager/android/internal:resource_provider_public_impl_java",
@@ -1067,7 +1066,6 @@
       "//chrome/browser/incognito:java",
       "//chrome/browser/language/android:java",
       "//chrome/browser/language/android:junit",
-      "//chrome/browser/lens:delegate_public_impl_java",
       "//chrome/browser/lens:java",
       "//chrome/browser/loading_modal/android:junit",
       "//chrome/browser/locale:java",
@@ -2592,6 +2590,7 @@
       "//base:base_java",
       "//base:base_java_test_support",
       "//build/android:build_java",
+      "//chrome/browser/autofill/test:test_support_java",
       "//chrome/browser/download/android:java",
       "//chrome/browser/prefetch/android:java",
       "//chrome/browser/profiles/android:java",
@@ -2624,6 +2623,7 @@
       ":test_support_jni_headers",
       "//chrome/browser",
       "//chrome/browser:browser_process",
+      "//chrome/browser/autofill/test:test_support",
       "//chrome/browser/sync",
       "//chrome/browser/thumbnail:test_support",
       "//components/offline_pages/core/background:test_support",
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
index 1549000..a2bc9ba 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
@@ -41,7 +41,6 @@
 import org.chromium.chrome.browser.tab.TabSelectionType;
 import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncFeatures;
 import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncServiceFactory;
-import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncUtils;
 import org.chromium.chrome.browser.tab_ui.RecyclerViewPosition;
 import org.chromium.chrome.browser.tab_ui.TabUiThemeUtils;
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
@@ -69,7 +68,6 @@
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler;
 import org.chromium.components.data_sharing.DataSharingService;
 import org.chromium.components.signin.identitymanager.IdentityManager;
-import org.chromium.components.tab_group_sync.LocalTabGroupId;
 import org.chromium.components.tab_group_sync.TabGroupSyncService;
 import org.chromium.components.tab_groups.TabGroupColorId;
 import org.chromium.ui.KeyboardVisibilityDelegate;
@@ -989,10 +987,7 @@
         mModel.set(TabGridDialogProperties.IS_SHARE_SHEET_VISIBLE, true);
 
         String tabGroupDisplayName = mModel.get(TabGridDialogProperties.HEADER_TITLE);
-
         TabGroupModelFilter filter = (TabGroupModelFilter) mCurrentTabModelFilterSupplier.get();
-        Tab tab = filter.getTabModel().getTabById(mCurrentTabId);
-        LocalTabGroupId localTabGroupId = TabGroupSyncUtils.getLocalTabGroupId(tab);
 
         TabUiUtils.startShareTabGroupFlow(
                 mActivity,
@@ -1082,7 +1077,8 @@
             String originalTitle = TabGroupTitleEditor.getDefaultTitle(mActivity, tabsCount);
             mModel.set(
                     TabGridDialogProperties.COLLAPSE_BUTTON_CONTENT_DESCRIPTION,
-                    mActivity.getResources()
+                    mActivity
+                            .getResources()
                             .getQuantityString(
                                     R.plurals.accessibility_dialog_back_button,
                                     tabsCount,
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
index d79192e6..bdf5572 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
@@ -481,14 +481,14 @@
         mRecyclerView.setRecyclerViewPosition(recyclerViewPosition);
     }
 
-    void initWithNative(@NonNull Profile profile) {
+    void initWithNative(@NonNull Profile originalProfile) {
         if (mIsInitialized) return;
 
         try (TraceEvent e = TraceEvent.scoped("TabListCoordinator.initWithNative")) {
             mIsInitialized = true;
 
-            assert !profile.isOffTheRecord() : "Expecting a non-incognito profile.";
-            mMediator.initWithNative(profile);
+            assert !originalProfile.isOffTheRecord() : "Expecting a non-incognito profile.";
+            mMediator.initWithNative(originalProfile);
         }
     }
 
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 aa6c4cc..38e61ae 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
@@ -363,7 +363,7 @@
     private boolean mActionsOnAllRelatedTabs;
     private String mComponentName;
     private @TabActionState int mTabActionState;
-    private @Nullable Profile mProfile;
+    private @Nullable Profile mOriginalProfile;
     private TabListGroupMenuCoordinator mTabListGroupMenuCoordinator;
     private Size mDefaultGridCardSize;
     private ComponentCallbacks mComponentCallbacks;
@@ -912,7 +912,6 @@
         mActionConfirmationManager = actionConfirmationManager;
         mOnTabGroupCreation = onTabGroupCreation;
 
-        mProfile = mCurrentTabModelFilterSupplier.get().getTabModel().getProfile();
         mTabModelObserver =
                 new TabModelObserver() {
                     @Override
@@ -1282,10 +1281,10 @@
         }
     }
 
-    public void initWithNative(Profile profile) {
-        assert !profile.isOffTheRecord() : "Expecting a non-incognito profile.";
-        mProfile = profile;
-        mTabListFaviconProvider.initWithNative(profile);
+    public void initWithNative(Profile originalProfile) {
+        assert !originalProfile.isOffTheRecord() : "Expecting a non-incognito profile.";
+        mOriginalProfile = originalProfile;
+        mTabListFaviconProvider.initWithNative(originalProfile);
 
         mOnTabModelFilterChanged.onResult(
                 mCurrentTabModelFilterSupplier.addObserver(mOnTabModelFilterChanged));
@@ -1335,7 +1334,7 @@
         // switcher.
         if (mMode == TabListMode.GRID
                 && mTabActionState != TabActionState.SELECTABLE
-                && PriceTrackingFeatures.isPriceTrackingEnabled(profile)) {
+                && PriceTrackingFeatures.isPriceTrackingEnabled(originalProfile)) {
             mListObserver =
                     new ListObserver<Void>() {
                         @Override
@@ -1552,10 +1551,11 @@
 
     void hardCleanup() {
         assert !mShowingTabs;
-        if (mProfile != null
-                && PriceTrackingUtilities.isTrackPricesOnTabsEnabled(mProfile)
-                && (PriceTrackingFeatures.isPriceDropIphEnabled(mProfile)
-                        || PriceTrackingFeatures.isPriceDropBadgeEnabled(mProfile))) {
+        if (!mCurrentTabModelFilterSupplier.get().isIncognitoBranded()
+                && mOriginalProfile != null
+                && PriceTrackingUtilities.isTrackPricesOnTabsEnabled(mOriginalProfile)
+                && (PriceTrackingFeatures.isPriceDropIphEnabled(mOriginalProfile)
+                        || PriceTrackingFeatures.isPriceDropBadgeEnabled(mOriginalProfile))) {
             saveSeenPriceDrops();
         }
         sViewedTabIds.clear();
@@ -1726,11 +1726,12 @@
     void registerOnScrolledListener(RecyclerView recyclerView) {
         // For InstantStart, this can be called before native is initialized, so ensure the Profile
         // is available before proceeding.
-        if (mProfile == null) return;
+        if (mOriginalProfile == null) return;
 
-        if (PriceTrackingUtilities.isTrackPricesOnTabsEnabled(mProfile)
-                && (PriceTrackingFeatures.isPriceDropIphEnabled(mProfile)
-                        || PriceTrackingFeatures.isPriceDropBadgeEnabled(mProfile))) {
+        if (!mCurrentTabModelFilterSupplier.get().isIncognitoBranded()
+                && PriceTrackingUtilities.isTrackPricesOnTabsEnabled(mOriginalProfile)
+                && (PriceTrackingFeatures.isPriceDropIphEnabled(mOriginalProfile)
+                        || PriceTrackingFeatures.isPriceDropBadgeEnabled(mOriginalProfile))) {
             mRecyclerView = recyclerView;
             mOnScrollListener =
                     new OnScrollListener() {
@@ -1882,17 +1883,18 @@
 
     private TabListMediator.TabActionListener getTabGroupOverflowMenuClickListener() {
         if (mTabListGroupMenuCoordinator == null) {
-            boolean isTabGroupSyncEnabled = TabGroupSyncFeatures.isTabGroupSyncEnabled(mProfile);
-            TabModel tabModel = mCurrentTabModelFilterSupplier.get().getTabModel();
-            Profile profile = tabModel.getProfile().getOriginalProfile();
+            boolean isTabGroupSyncEnabled =
+                    !mCurrentTabModelFilterSupplier.get().isIncognitoBranded()
+                            && TabGroupSyncFeatures.isTabGroupSyncEnabled(mOriginalProfile);
             IdentityManager identityManager = null;
             TabGroupSyncService tabGroupSyncService = null;
             DataSharingService dataSharingService = null;
             if (isTabGroupSyncEnabled
                     && ChromeFeatureList.isEnabled(ChromeFeatureList.DATA_SHARING)) {
-                identityManager = IdentityServicesProvider.get().getIdentityManager(profile);
-                tabGroupSyncService = TabGroupSyncServiceFactory.getForProfile(profile);
-                dataSharingService = DataSharingServiceFactory.getForProfile(profile);
+                identityManager =
+                        IdentityServicesProvider.get().getIdentityManager(mOriginalProfile);
+                tabGroupSyncService = TabGroupSyncServiceFactory.getForProfile(mOriginalProfile);
+                dataSharingService = DataSharingServiceFactory.getForProfile(mOriginalProfile);
             }
             mTabListGroupMenuCoordinator =
                     new TabListGroupMenuCoordinator(
@@ -2230,8 +2232,8 @@
     private void setupPersistedTabDataFetcherForTab(Tab tab, int index) {
         PropertyModel model = mModel.get(index).model;
         if (mMode == TabListMode.GRID && !tab.isIncognito()) {
-            assert mProfile != null;
-            if (PriceTrackingUtilities.isTrackPricesOnTabsEnabled(mProfile)
+            assert mOriginalProfile != null;
+            if (PriceTrackingUtilities.isTrackPricesOnTabsEnabled(mOriginalProfile)
                     && !isTabInTabGroup(tab)) {
                 model.set(
                         TabProperties.SHOPPING_PERSISTED_TAB_DATA_FETCHER,
@@ -2386,8 +2388,9 @@
     void updateLayout() {
         // Right now we need to update layout only if there is a price welcome message card in tab
         // switcher.
-        if (mProfile == null
-                || !PriceTrackingUtilities.isPriceWelcomeMessageCardEnabled(mProfile)) {
+        if (mOriginalProfile == null
+                || !PriceTrackingUtilities.isPriceWelcomeMessageCardEnabled(mOriginalProfile)
+                || mCurrentTabModelFilterSupplier.get().isIncognitoBranded()) {
             return;
         }
         assert mGridLayoutManager != null;
@@ -2424,9 +2427,10 @@
     @VisibleForTesting
     void recordPriceAnnotationsEnabledMetrics() {
         if (mMode != TabListMode.GRID
+                || mCurrentTabModelFilterSupplier.get().isIncognitoBranded()
                 || !mActionsOnAllRelatedTabs
-                || mProfile == null
-                || !PriceTrackingFeatures.isPriceTrackingEligible(mProfile)) {
+                || mOriginalProfile == null
+                || !PriceTrackingFeatures.isPriceTrackingEligible(mOriginalProfile)) {
             return;
         }
         SharedPreferencesManager preferencesManager = ChromeSharedPreferences.getInstance();
@@ -2438,7 +2442,7 @@
                 >= PriceTrackingFeatures.getAnnotationsEnabledMetricsWindowDurationMilliSeconds()) {
             RecordHistogram.recordBooleanHistogram(
                     "Commerce.PriceDrop.AnnotationsEnabled",
-                    PriceTrackingUtilities.isTrackPricesOnTabsEnabled(mProfile));
+                    PriceTrackingUtilities.isTrackPricesOnTabsEnabled(mOriginalProfile));
             preferencesManager.writeLong(
                     ChromePreferenceKeys.PRICE_TRACKING_ANNOTATIONS_ENABLED_METRICS_TIMESTAMP,
                     System.currentTimeMillis());
@@ -2768,7 +2772,9 @@
 
     @VisibleForTesting
     void onMenuItemClicked(@IdRes int menuId, int tabId, @Nullable String collaborationId) {
-        boolean isSyncEnabled = TabGroupSyncFeatures.isTabGroupSyncEnabled(mProfile);
+        boolean isSyncEnabled =
+                !mCurrentTabModelFilterSupplier.get().isIncognitoBranded()
+                        && TabGroupSyncFeatures.isTabGroupSyncEnabled(mOriginalProfile);
         if (menuId == R.id.close_tab || menuId == R.id.delete_tab) {
             boolean hideTabGroups = menuId == R.id.close_tab;
             if (hideTabGroups) {
@@ -2949,8 +2955,7 @@
 
         @Nullable TabGroupSyncService tabGroupSyncService = null;
         if (TabGroupSyncFeatures.isTabGroupSyncEnabled(tab.getProfile())) {
-            tabGroupSyncService =
-                    TabGroupSyncServiceFactory.getForProfile(tab.getProfile().getOriginalProfile());
+            tabGroupSyncService = TabGroupSyncServiceFactory.getForProfile(mOriginalProfile);
         }
         @Nullable
         String collaborationId =
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewBinder.java
index 0d67b13..370a260 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewBinder.java
@@ -272,7 +272,7 @@
     }
 
     private static void setTabGroupColorIcon(ViewGroup view, PropertyModel model) {
-        ImageView colorIconView = view.findViewById(R.id.icon);
+        ImageView colorIconView = view.findViewById(R.id.before_title_icon);
 
         if (ChromeFeatureList.sTabGroupParityAndroid.isEnabled()) {
             colorIconView.setVisibility(View.VISIBLE);
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogViewBinderTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogViewBinderTest.java
index 005cd26..d45be3a 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogViewBinderTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogViewBinderTest.java
@@ -192,9 +192,9 @@
         assertNull(mTabGridDialogView.getBindingToken());
 
         String title = "1024 tabs";
-        assertNotEquals(title, mTitleTextView.getText());
+        assertNotEquals(title, mTitleTextView.getText().toString());
         mModel.set(TabGridDialogProperties.HEADER_TITLE, title);
-        assertNotEquals(title, mTitleTextView.getText());
+        assertNotEquals(title, mTitleTextView.getText().toString());
 
         mModel.set(TabGridDialogProperties.BINDING_TOKEN, 4);
         assertEquals(mTabGridDialogView.getBindingToken().intValue(), 4);
@@ -240,7 +240,7 @@
     @UiThreadTest
     public void testSetHeaderTitle() {
         String title = "1024 tabs";
-        assertNotEquals(title, mTitleTextView.getText());
+        assertNotEquals(title, mTitleTextView.getText().toString());
 
         mModel.set(TabGridDialogProperties.HEADER_TITLE, title);
 
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
index 1d02880..ac8d0c15 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
@@ -1027,7 +1027,7 @@
         final int colorId = TabGroupColorId.BLUE;
         final int colorLayer = 1;
 
-        ImageView colorIconView = mTabListView.findViewById(R.id.icon);
+        ImageView colorIconView = mTabListView.findViewById(R.id.before_title_icon);
         assertNull(colorIconView.getBackground());
 
         mGridModel.set(TabProperties.TAB_GROUP_COLOR_ID, colorId);
@@ -1059,7 +1059,7 @@
 
         mGridModel.set(TabProperties.TAB_GROUP_COLOR_ID, colorId1);
 
-        ImageView colorIconView = mTabListView.findViewById(R.id.icon);
+        ImageView colorIconView = mTabListView.findViewById(R.id.before_title_icon);
         assertEquals(colorIconView.getVisibility(), View.VISIBLE);
         assertNotNull(colorIconView.getBackground());
 
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 ddf89a8..8a6a654c 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
@@ -77,8 +77,9 @@
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
 import org.mockito.invocation.InvocationOnMock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 import org.mockito.stubbing.Answer;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
@@ -201,7 +202,7 @@
 @LooperMode(LooperMode.Mode.LEGACY)
 @DisableFeatures(ChromeFeatureList.DATA_SHARING)
 public class TabListMediatorUnitTest {
-
+    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Rule public JniMocker mMocker = new JniMocker();
 
     private static final String TAB1_TITLE = "Tab1";
@@ -362,7 +363,6 @@
 
     @Before
     public void setUp() {
-        MockitoAnnotations.initMocks(this);
         mMocker.mock(UrlUtilitiesJni.TEST_HOOKS, mUrlUtilitiesJniMock);
         mMocker.mock(
                 OptimizationGuideBridgeFactoryJni.TEST_HOOKS,
@@ -655,7 +655,7 @@
     }
 
     @Test
-    public void updatesFaviconFetcher_SingleTab_GTS() {
+    public void updatesFaviconFetcher_SingleTab_Gts() {
         mModel.get(0).model.set(TabProperties.FAVICON_FETCHER, null);
         assertNull(mModel.get(0).model.get(TabProperties.FAVICON_FETCHER));
 
@@ -676,7 +676,7 @@
 
     @Test
     @DisableFeatures(ChromeFeatureList.TAB_GROUP_PARITY_ANDROID)
-    public void updatesFaviconFetcher_SingleTabGroup_GTS() {
+    public void updatesFaviconFetcher_SingleTabGroup_Gts() {
         mModel.get(0).model.set(TabProperties.FAVICON_FETCHER, null);
         assertNull(mModel.get(0).model.get(TabProperties.FAVICON_FETCHER));
 
@@ -691,7 +691,7 @@
     }
 
     @Test
-    public void updatesFaviconFetcher_SingleTab_NonGTS() {
+    public void updatesFaviconFetcher_SingleTab_NonGts() {
         mModel.get(0).model.set(TabProperties.FAVICON_FETCHER, null);
         assertNull(mModel.get(0).model.get(TabProperties.FAVICON_FETCHER));
 
@@ -712,7 +712,7 @@
 
     @Test
     @DisableFeatures(ChromeFeatureList.TAB_GROUP_PARITY_ANDROID)
-    public void updatesFaviconFetcher_TabGroup_GTS() {
+    public void updatesFaviconFetcher_TabGroup_Gts() {
         assertNotNull(mModel.get(0).model.get(TabProperties.FAVICON_FETCHER));
         mModel.get(0).model.set(TabProperties.FAVICON_FETCHER, null);
         // Assert that tab1 is in a group.
@@ -730,7 +730,7 @@
     }
 
     @Test
-    public void updatesFaviconFetcher_TabGroup_ListGTS() {
+    public void updatesFaviconFetcher_TabGroup_ListGts() {
         setUpTabListMediator(TabListMediatorType.TAB_SWITCHER, TabListMode.LIST);
 
         assertNotNull(mModel.get(0).model.get(TabProperties.FAVICON_FETCHER));
@@ -748,7 +748,7 @@
     }
 
     @Test
-    public void updatesFaviconFetcher_TabGroup_ListGTS_SingleTab() {
+    public void updatesFaviconFetcher_TabGroup_ListGts_SingleTab() {
         setUpTabListMediator(TabListMediatorType.TAB_SWITCHER, TabListMode.LIST);
 
         assertNotNull(mModel.get(0).model.get(TabProperties.FAVICON_FETCHER));
@@ -1208,7 +1208,7 @@
     }
 
     @Test
-    public void tabAddition_GTS() {
+    public void tabAddition_Gts() {
         Tab newTab = prepareTab(TAB3_ID, TAB3_TITLE, TAB3_URL);
         doReturn(mTab1).when(mTabGroupModelFilter).getTabAt(0);
         doReturn(mTab2).when(mTabGroupModelFilter).getTabAt(1);
@@ -1231,7 +1231,7 @@
     }
 
     @Test
-    public void tabAddition_GTS_delayAdd() {
+    public void tabAddition_Gts_delayAdd() {
         mMediator.setComponentNameForTesting(TabSwitcherPaneCoordinator.COMPONENT_NAME);
         initAndAssertAllProperties();
 
@@ -1285,7 +1285,7 @@
     }
 
     @Test
-    public void tabAddition_GTS_delayAdd_WithUnexpectedUpdate() {
+    public void tabAddition_Gts_delayAdd_WithUnexpectedUpdate() {
         mMediator.setComponentNameForTesting(TabSwitcherPaneCoordinator.COMPONENT_NAME);
         initAndAssertAllProperties();
 
@@ -1345,7 +1345,7 @@
     }
 
     @Test
-    public void tabAddition_GTS_Skip() {
+    public void tabAddition_Gts_Skip() {
         // Add a new tab to the group with mTab2.
         Tab newTab = prepareTab(TAB3_ID, TAB3_TITLE, TAB3_URL);
         doReturn(mTab1).when(mTabGroupModelFilter).getTabAt(0);
@@ -1368,7 +1368,7 @@
     }
 
     @Test
-    public void tabAddition_GTS_Middle() {
+    public void tabAddition_Gts_Middle() {
         Tab newTab = prepareTab(TAB3_ID, TAB3_TITLE, TAB3_URL);
         doReturn(mTab1).when(mTabGroupModelFilter).getTabAt(0);
         doReturn(newTab).when(mTabGroupModelFilter).getTabAt(1);
@@ -1758,7 +1758,7 @@
     }
 
     @Test
-    public void tabMoveOutOfGroup_GTS_Moved_Tab_Selected() {
+    public void tabMoveOutOfGroup_Gts_Moved_Tab_Selected() {
         // Assume that two tabs are in the same group before ungroup.
         List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab2));
         mMediator.resetWithListOfTabs(tabs, false);
@@ -1787,7 +1787,7 @@
     }
 
     @Test
-    public void tabMoveOutOfGroup_GTS_Origin_Tab_Selected() {
+    public void tabMoveOutOfGroup_Gts_Origin_Tab_Selected() {
         // Assume that two tabs are in the same group before ungroup.
         List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1));
         mMediator.resetWithListOfTabs(tabs, false);
@@ -1813,7 +1813,7 @@
     }
 
     @Test
-    public void tabMoveOutOfGroup_GTS_LastTab() {
+    public void tabMoveOutOfGroup_Gts_LastTab() {
         // Assume that tab1 is a single tab group that became a single tab.
         List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1));
         mMediator.resetWithListOfTabs(tabs, false);
@@ -1835,7 +1835,7 @@
     }
 
     @Test
-    public void tabMoveOutOfGroup_GTS_TabAdditionWithSameId() {
+    public void tabMoveOutOfGroup_Gts_TabAdditionWithSameId() {
         // Assume that two tabs are in the same group before ungroup.
         List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1));
         mMediator.resetWithListOfTabs(tabs, false);
@@ -2566,7 +2566,7 @@
     }
 
     @Test
-    public void getLatestTitle_NotGTS() {
+    public void getLatestTitle_NotGts() {
         setUpTabListMediator(TabListMediatorType.TAB_GRID_DIALOG, TabListMode.GRID);
 
         // Mock that we have a stored title stored with reference to root ID of tab1.
@@ -2585,7 +2585,7 @@
     }
 
     @Test
-    public void getLatestTitle_SingleTabGroupSupported_GTS() {
+    public void getLatestTitle_SingleTabGroupSupported_Gts() {
         // Mock that we have a stored title stored with reference to root ID of tab1.
         mTabGroupModelFilter.setTabGroupTitle(mTab1.getRootId(), CUSTOMIZED_DIALOG_TITLE1);
         assertThat(
@@ -2603,7 +2603,7 @@
     }
 
     @Test
-    public void getLatestTitle_SingleTabGroupNotSupported_GTS() {
+    public void getLatestTitle_SingleTabGroupNotSupported_Gts() {
         // Mock that we have a stored title stored with reference to root ID of tab1.
         mTabGroupModelFilter.setTabGroupTitle(mTab1.getRootId(), CUSTOMIZED_DIALOG_TITLE1);
         assertThat(
@@ -2621,7 +2621,7 @@
     }
 
     @Test
-    public void getLatestTitle_Stored_GTS() {
+    public void getLatestTitle_Stored_Gts() {
         // Mock that we have a stored title stored with reference to root ID of tab1.
         mTabGroupModelFilter.setTabGroupTitle(mTab1.getRootId(), CUSTOMIZED_DIALOG_TITLE1);
         assertThat(
@@ -2638,7 +2638,7 @@
     }
 
     @Test
-    public void getLatestTitle_Default_GTS() {
+    public void getLatestTitle_Default_Gts() {
         // Mock that tab1 and tab2 are in the same group and group root id is TAB1_ID.
         List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2));
         createTabGroup(tabs, TAB1_ID, TAB_GROUP_ID);
@@ -2648,7 +2648,7 @@
     }
 
     @Test
-    public void getLatestTitle_NoDefault_GTS() {
+    public void getLatestTitle_NoDefault_Gts() {
         // Mock that tab1 and tab2 are in the same group and group root id is TAB1_ID.
         List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2));
         createTabGroup(tabs, TAB1_ID, TAB_GROUP_ID);
@@ -2658,7 +2658,7 @@
 
     @Test
     @DisableFeatures(ChromeFeatureList.TAB_GROUP_PARITY_ANDROID)
-    public void updateTabGroupTitle_GTS() {
+    public void updateTabGroupTitle_Gts() {
         setUpTabGroupCardDescriptionString();
         String targetString = "Expand tab group with 2 tabs.";
         assertThat(mModel.get(POSITION1).model.get(TabProperties.TITLE), equalTo(TAB1_TITLE));
@@ -2682,7 +2682,7 @@
 
     @Test
     @DisableFeatures(ChromeFeatureList.TAB_GROUP_PARITY_ANDROID)
-    public void updateTabGroupTitle_SingleTab_GTS() {
+    public void updateTabGroupTitle_SingleTab_Gts() {
         setUpTabGroupCardDescriptionString();
         String targetString = "Expand tab group with 1 tab.";
         assertThat(mModel.get(POSITION1).model.get(TabProperties.TITLE), equalTo(TAB1_TITLE));
@@ -2821,7 +2821,7 @@
     }
 
     @Test
-    public void testUrlUpdated_forSingleTab_GTS() {
+    public void testUrlUpdated_forSingleTab_Gts() {
         assertNotEquals(mNewDomain, mModel.get(POSITION1).model.get(TabProperties.URL_DOMAIN));
 
         doReturn(mNewDomain)
@@ -2836,7 +2836,7 @@
     }
 
     @Test
-    public void testUrlUpdated_forGroup_GTS() {
+    public void testUrlUpdated_forGroup_Gts() {
         List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2));
         createTabGroup(tabs, TAB1_ID, TAB_GROUP_ID);
         doReturn(POSITION1).when(mTabGroupModelFilter).indexOf(mTab1);
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 8835864..10aca33a 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
@@ -550,8 +550,7 @@
         mSigninManager.getIdentityManager().addObserver(this);
 
         mSectionHeaderModel.set(SectionHeaderListProperties.MENU_MODEL_LIST_KEY, mFeedMenuModel);
-        mSectionHeaderModel.set(
-                SectionHeaderListProperties.MENU_DELEGATE_KEY, this::onItemSelected);
+        mSectionHeaderModel.set(SectionHeaderListProperties.MENU_DELEGATE_KEY, this);
 
         setUpWebFeedTab();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index f2c63db..7e914be 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -2209,11 +2209,7 @@
                 };
 
         mUndoBarPopupController =
-                new UndoBarController(
-                        this,
-                        mTabModelSelector,
-                        this::getSnackbarManager,
-                        dialogVisibilitySupplier);
+                new UndoBarController(this, mTabModelSelector, this, dialogVisibilitySupplier);
 
         if (ChromeFeatureList.sTabGroupParityAndroid.isEnabled()) {
             TabModelUtils.runOnTabStateInitialized(
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 89a389b..8b5fe2f 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
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.autofill.settings;
 
-import android.os.Build;
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
@@ -46,14 +45,6 @@
             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
         View v = super.onCreateView(inflater, container, savedInstanceState);
 
-        // Do not use autofill for the fields.
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            getActivity()
-                    .getWindow()
-                    .getDecorView()
-                    .setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS);
-        }
-
         // Populate the billing address dropdown.
         ArrayAdapter<AutofillProfile> profilesAdapter =
                 new ArrayAdapter<AutofillProfile>(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillLocalIbanEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillLocalIbanEditor.java
index 87c478f..5d36f48 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillLocalIbanEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillLocalIbanEditor.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.autofill.settings;
 
-import android.os.Build;
 import android.os.Bundle;
 import android.text.Editable;
 import android.view.LayoutInflater;
@@ -60,14 +59,6 @@
             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
         View v = super.onCreateView(inflater, container, savedInstanceState);
 
-        // Do not use autofill for the fields.
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            getActivity()
-                    .getWindow()
-                    .getDecorView()
-                    .setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS);
-        }
-
         PersonalDataManager personalDataManager =
                 PersonalDataManagerFactory.getForProfile(getProfile());
         mIban = personalDataManager.getIban(mGUID);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
index d03a16c..b1b7572 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
@@ -32,7 +32,6 @@
 import androidx.core.view.ViewCompat;
 import androidx.core.view.WindowCompat;
 import androidx.core.view.WindowInsetsCompat;
-import androidx.core.view.accessibility.AccessibilityEventCompat;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 import androidx.customview.widget.ExploreByTouchHelper;
 
@@ -1632,12 +1631,7 @@
 
     @Override
     public void invalidateAccessibilityProvider() {
-        if (mNodeProvider != null) {
-            mNodeProvider.sendEventForVirtualView(
-                    mNodeProvider.getAccessibilityFocusedVirtualViewId(),
-                    AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
-            mNodeProvider.invalidateRoot();
-        }
+        if (mNodeProvider != null) mNodeProvider.invalidateRoot();
     }
 
     // ChromeAccessibilityUtil.Observer
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java
index 45a2635a..2e19bcc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java
@@ -280,7 +280,7 @@
 
     @Override
     public void getVirtualViews(List<VirtualView> views) {
-        if (isCollapsed()) return;
+        if (isCollapsed() || mIsDying) return;
         super.getVirtualViews(views);
         if (mShowingCloseButton) mCloseButton.getVirtualViews(views);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
index 30e7ed70..9ee571a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
@@ -31,7 +31,6 @@
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncFeatures;
 import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncServiceFactory;
-import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncUtils;
 import org.chromium.chrome.browser.tabmodel.TabCreator;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
@@ -49,7 +48,6 @@
 import org.chromium.components.data_sharing.DataSharingService;
 import org.chromium.components.data_sharing.DataSharingService.GroupDataOrFailureOutcome;
 import org.chromium.components.signin.identitymanager.IdentityManager;
-import org.chromium.components.tab_group_sync.LocalTabGroupId;
 import org.chromium.components.tab_group_sync.TabGroupSyncService;
 import org.chromium.components.tab_groups.TabGroupColorId;
 import org.chromium.ui.KeyboardVisibilityDelegate;
@@ -224,9 +222,6 @@
                         tabGroupModelFilter, tabCreator, tabId, TabLaunchType.FROM_TAB_GROUP_UI);
                 recordUserAction("NewTabInGroup");
             } else if (menuId == org.chromium.chrome.R.id.share_group) {
-                LocalTabGroupId localTabGroupId =
-                        TabGroupSyncUtils.getLocalTabGroupId(tabGroupModelFilter, tabId);
-
                 // Get user assigned group title or the default title "N tabs" if no title is
                 // assigned.
                 String tabGroupDisplayName =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/AuthTabIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/AuthTabIntentDataProvider.java
index b74b580..b045a0d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/AuthTabIntentDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/AuthTabIntentDataProvider.java
@@ -80,6 +80,8 @@
         GURL redirectUrl = new GURL(UrlConstants.HTTPS_URL_PREFIX + host + path);
         mRedirectHost = redirectUrl.getHost();
         mRedirectPath = redirectUrl.getPath();
+
+        logFeatureUsage();
     }
 
     @Override
@@ -166,4 +168,25 @@
     public String getAuthRedirectScheme() {
         return mRedirectScheme;
     }
+
+    /**
+     * Logs the usage of Auth Tab features to a large enum histogram in order to track usage by
+     * apps.
+     */
+    private void logFeatureUsage() {
+        if (!CustomTabsFeatureUsage.isEnabled()) return;
+        CustomTabsFeatureUsage featureUsage = new CustomTabsFeatureUsage();
+
+        // Ordering: Log all the features ordered by enum, when they apply.
+        featureUsage.log(CustomTabsFeatureUsage.CustomTabsFeature.EXTRA_LAUNCH_AUTH_TAB);
+        if (mRedirectScheme != null) {
+            featureUsage.log(CustomTabsFeatureUsage.CustomTabsFeature.EXTRA_REDIRECT_SCHEME);
+        }
+        if (mRedirectHost != null) {
+            featureUsage.log(CustomTabsFeatureUsage.CustomTabsFeature.EXTRA_HTTPS_REDIRECT_HOST);
+        }
+        if (mRedirectPath != null) {
+            featureUsage.log(CustomTabsFeatureUsage.CustomTabsFeature.EXTRA_HTTPS_REDIRECT_PATH);
+        }
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsFeatureUsage.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsFeatureUsage.java
index fbf1ae5..11e24b1f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsFeatureUsage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsFeatureUsage.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.customtabs;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -15,6 +16,9 @@
 
 /** Records a histogram that tracks usage of all the CCT features of interest. */
 public class CustomTabsFeatureUsage {
+    @VisibleForTesting
+    public static final String CUSTOM_TABS_FEATURE_USAGE_HISTOGRAM = "CustomTabs.FeatureUsage";
+
     // NOTE: This must be kept in sync with the definition |CustomTabsFeatureUsed|
     // in tools/metrics/histograms/enums.xml.
     @IntDef({
@@ -76,6 +80,10 @@
         CustomTabsFeature.EXTRA_ENABLE_GOOGLE_BOTTOM_BAR,
         CustomTabsFeature.EXTRA_GOOGLE_BOTTOM_BAR_BUTTONS,
         CustomTabsFeature.EXTRA_NETWORK,
+        CustomTabsFeature.EXTRA_LAUNCH_AUTH_TAB,
+        CustomTabsFeature.EXTRA_REDIRECT_SCHEME,
+        CustomTabsFeature.EXTRA_HTTPS_REDIRECT_HOST,
+        CustomTabsFeature.EXTRA_HTTPS_REDIRECT_PATH,
         CustomTabsFeature.COUNT
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -140,9 +148,13 @@
         int EXTRA_ENABLE_GOOGLE_BOTTOM_BAR = 56;
         int EXTRA_GOOGLE_BOTTOM_BAR_BUTTONS = 57;
         int EXTRA_NETWORK = 58;
+        int EXTRA_LAUNCH_AUTH_TAB = 59;
+        int EXTRA_REDIRECT_SCHEME = 60;
+        int EXTRA_HTTPS_REDIRECT_HOST = 61;
+        int EXTRA_HTTPS_REDIRECT_PATH = 62;
 
         /** Total count of entries. */
-        int COUNT = 59;
+        int COUNT = 63;
     }
 
     // Whether flag-enabled or not.
@@ -178,6 +190,6 @@
 
         mUsed.set(feature);
         RecordHistogram.recordEnumeratedHistogram(
-                "CustomTabs.FeatureUsage", feature, CustomTabsFeature.COUNT);
+                CUSTOM_TABS_FEATURE_USAGE_HISTOGRAM, feature, CustomTabsFeature.COUNT);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java
index 00dac51..642383f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java
@@ -82,7 +82,6 @@
                         .getDimensionPixelSize(R.dimen.history_item_remove_button_lateral_padding),
                 getPaddingBottom());
 
-        findViewById(R.id.chip_description).setVisibility(View.VISIBLE);
         mChipView = findViewById(R.id.chip);
         mChipView.getPrimaryTextView().setEllipsize(TextUtils.TruncateAt.END);
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarTest.java
index 509e56a..f1601b3 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarTest.java
@@ -247,7 +247,6 @@
         for (int menuId : applicableMenuIds) {
             boolean isVisible = !hiddenIdSet.contains(menuId);
             MenuItem menuItem = mBookmarkToolbar.getMenu().findItem(menuId);
-            assertNotNull(menuId);
             assertEquals(
                     "Mismatched visibility for menu item " + menuItem,
                     isVisible,
@@ -259,7 +258,6 @@
         for (int menuId : applicableMenuIds) {
             boolean isEnabled = !disabledIds.contains(menuId);
             MenuItem menuItem = mBookmarkToolbar.getMenu().findItem(menuId);
-            assertNotNull(menuId);
             assertEquals(
                     "Mismatched enabled state for menu item " + menuItem,
                     isEnabled,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsTest.java
index dea9caa16..489cbae9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsTest.java
@@ -198,7 +198,7 @@
                     try {
                         Assert.assertNotEquals("null", exec(variable));
                     } catch (TimeoutException e) {
-                        Assert.fail();
+                        throw new RuntimeException(e);
                     }
                 });
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabActivityTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabActivityTestRule.java
index 44be8db..7997a83 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabActivityTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabActivityTestRule.java
@@ -69,7 +69,7 @@
             try {
                 createNewCustomTabSessionForIntent(intent);
             } catch (TimeoutException e) {
-                Assert.fail();
+                throw new RuntimeException(e);
             }
         }
         super.startCustomTabActivityWithIntent(intent);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationTransitionsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationTransitionsTest.java
index 0ccebe21..77118cb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationTransitionsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationTransitionsTest.java
@@ -8,6 +8,9 @@
 
 import android.graphics.Bitmap;
 import android.graphics.Color;
+import android.os.Build;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
 
 import androidx.activity.BackEventCompat;
 import androidx.test.core.app.ApplicationProvider;
@@ -33,6 +36,7 @@
 import org.chromium.base.test.util.CriteriaNotSatisfiedException;
 import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.Features.EnableFeatures;
+import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.chrome.browser.ViewportTestUtils;
 import org.chromium.chrome.browser.back_press.BackPressManager;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
@@ -50,9 +54,13 @@
 import org.chromium.net.test.EmbeddedTestServer;
 import org.chromium.ui.base.BackGestureEventSwipeEdge;
 import org.chromium.ui.base.UiAndroidFeatures;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -67,6 +75,7 @@
 @CommandLineFlags.Add({
     ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
     "enable-features=BackForwardTransitions,BackGestureRefactorAndroid",
+    "force-prefers-no-reduced-motion",
     // Resampling can make scroll offsets non-deterministic so turn it off.
     "disable-features=ResamplingScrollEvents",
     "hide-scrollbars"
@@ -225,7 +234,6 @@
                 () -> {
                     invokeNavigateGesture(edge);
                 });
-        waitForTransitionFinished();
     }
 
     private void waitForTransitionFinished() {
@@ -244,6 +252,50 @@
                 CriteriaHelper.DEFAULT_POLLING_INTERVAL);
     }
 
+    private void waitForModalDialogShown() {
+        CriteriaHelper.pollUiThread(
+                () -> {
+                    try {
+                        Criteria.checkThat(getDialogManager().isShowing(), Matchers.is(true));
+                    } catch (Throwable e) {
+                        throw new CriteriaNotSatisfiedException(e);
+                    }
+                },
+                TEST_TIMEOUT,
+                CriteriaHelper.DEFAULT_POLLING_INTERVAL);
+    }
+
+    private void runJavaScriptOnTab(String script) {
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    getWebContents().evaluateJavaScriptForTests(script, null);
+                });
+    }
+
+    private ModalDialogManager getDialogManager() {
+        return mActivityTestRule.getActivity().getWindowAndroid().getModalDialogManager();
+    }
+
+    private int numSuspendedDialogs(@ModalDialogType int dialogType) {
+        var dialogs = getDialogManager().getPendingDialogsForTest(dialogType);
+        if (dialogs == null) return 0;
+        return dialogs.size();
+    }
+
+    private void waitForNumSuspendedDialogs(@ModalDialogType int dialogType, int numSuspended) {
+        CriteriaHelper.pollUiThread(
+                () -> {
+                    try {
+                        Criteria.checkThat(
+                                numSuspendedDialogs(dialogType), Matchers.is(numSuspended));
+                    } catch (Throwable e) {
+                        throw new CriteriaNotSatisfiedException(e);
+                    }
+                },
+                TEST_TIMEOUT,
+                CriteriaHelper.DEFAULT_POLLING_INTERVAL);
+    }
+
     /**
      * Basic smoke test of transition back navigation.
      *
@@ -253,6 +305,9 @@
     @Test
     @MediumTest
     public void smokeTest() throws Throwable {
+        if (mTestNavigationMode == NAVIGATION_MODE_GESTURAL
+                && VERSION.SDK_INT < VERSION_CODES.UPSIDE_DOWN_CAKE) return;
+
         // Put "blue.html" and then "green.html" in the session history.
         String url1 = mTestServer.getURL("/chrome/test/data/android/blue.html");
         String url2 = mTestServer.getURL("/chrome/test/data/android/green.html");
@@ -265,6 +320,7 @@
 
         // Perform a back gesture transition from the left edge.
         performNavigationTransition(url2, BackEventCompat.EDGE_LEFT);
+        waitForTransitionFinished();
 
         Assert.assertEquals(url2, getCurrentUrl());
 
@@ -272,9 +328,11 @@
         // button mode this goes forward, in gestural mode this goes back.
         if (mTestNavigationMode == NAVIGATION_MODE_THREE_BUTTON) {
             performNavigationTransition(url3, BackEventCompat.EDGE_RIGHT);
+            waitForTransitionFinished();
             Assert.assertEquals(url3, getCurrentUrl());
         } else {
             performNavigationTransition(url1, BackEventCompat.EDGE_RIGHT);
+            waitForTransitionFinished();
             Assert.assertEquals(url1, getCurrentUrl());
         }
     }
@@ -288,6 +346,9 @@
     @Test
     @MediumTest
     public void testInputAfterBackTransition() throws Throwable {
+        if (mTestNavigationMode == NAVIGATION_MODE_GESTURAL
+                && VERSION.SDK_INT < VERSION_CODES.UPSIDE_DOWN_CAKE) return;
+
         // Put "blue.html" and then "green.html" in the session history.
         String url1 = mTestServer.getURL("/chrome/test/data/android/blue.html");
         String url2 = mTestServer.getURL("/chrome/test/data/android/green.html");
@@ -296,6 +357,7 @@
 
         WebContentsUtils.waitForCopyableViewInWebContents(getWebContents());
         performNavigationTransition(url1, BackEventCompat.EDGE_LEFT);
+        waitForTransitionFinished();
 
         JavaScriptUtils.executeJavaScriptAndWaitForResult(
                 getWebContents(),
@@ -326,6 +388,9 @@
     @MediumTest
     @EnableFeatures({UiAndroidFeatures.MIRROR_BACK_FORWARD_GESTURES_IN_RTL})
     public void testBackNavInRTL() throws Throwable {
+        if (mTestNavigationMode == NAVIGATION_MODE_GESTURAL
+                && VERSION.SDK_INT < VERSION_CODES.UPSIDE_DOWN_CAKE) return;
+
         setRtlForTesting(true);
 
         // Put "blue.html" and then "green.html" in the session history.
@@ -338,6 +403,7 @@
 
         WebContentsUtils.waitForCopyableViewInWebContents(getWebContents());
         performNavigationTransition(url2, BackEventCompat.EDGE_RIGHT);
+        waitForTransitionFinished();
         Assert.assertEquals(url2, getCurrentUrl());
 
         // Perform an edge gesture transition from the left edge (semantically
@@ -345,9 +411,11 @@
         // forward, in gestural mode this goes back (without a transition).
         if (mTestNavigationMode == NAVIGATION_MODE_THREE_BUTTON) {
             performNavigationTransition(url3, BackEventCompat.EDGE_LEFT);
+            waitForTransitionFinished();
             Assert.assertEquals(url3, getCurrentUrl());
         } else {
             performNavigationTransition(url1, BackEventCompat.EDGE_LEFT);
+            waitForTransitionFinished();
             Assert.assertEquals(url1, getCurrentUrl());
         }
     }
@@ -360,6 +428,9 @@
     @Test
     @MediumTest
     public void startBackNavWithTopControlHidden() throws Throwable {
+        if (mTestNavigationMode == NAVIGATION_MODE_GESTURAL
+                && VERSION.SDK_INT < VERSION_CODES.UPSIDE_DOWN_CAKE) return;
+
         // The top control's offset is -top_controls_height when controls are fully hidden, 0 when
         // fully shown.
         final AtomicInteger topControlOffsetDuringGesture = new AtomicInteger(Integer.MAX_VALUE);
@@ -399,6 +470,7 @@
         // Perform a back gesture transition.
         mViewportTestUtils.hideBrowserControls();
         performNavigationTransition(url1, BackEventCompat.EDGE_LEFT);
+        waitForTransitionFinished();
 
         Assert.assertEquals(url1, getCurrentUrl());
 
@@ -406,4 +478,123 @@
                 topControlOffsetDuringGesture.get() > -mViewportTestUtils.getTopControlsHeightPx());
         mViewportTestUtils.waitForBrowserControlsState(/* shown= */ true);
     }
+
+    /**
+     * Test that the modal dialogs are suspended during the transition but resumed after the
+     * transition.
+     */
+    @Test
+    @MediumTest
+    @MinAndroidSdkLevel(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public void alertSuspendedDuringTransition() throws Throwable {
+        // Skip for three button mode because `TouchCommon.performWallClockDrag` blocks (it sleeps
+        // between each touch event). It's okay to skip because the dialog suspension is navigation
+        // mode agnostic.
+        if (mTestNavigationMode == NAVIGATION_MODE_THREE_BUTTON) return;
+
+        // Put "blue.html" and then "green.html" in the session history.
+        String url1 = mTestServer.getURL("/chrome/test/data/android/blue.html");
+        String url2 = mTestServer.getURL("/chrome/test/data/android/green.html");
+        mActivityTestRule.loadUrl(url1);
+        mActivityTestRule.loadUrl(url2);
+
+        WebContentsUtils.waitForCopyableViewInWebContents(getWebContents());
+
+        final AtomicBoolean dialogQueuedToShow = new AtomicBoolean(false);
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    getDialogManager()
+                            .addObserver(
+                                    new ModalDialogManager.ModalDialogManagerObserver() {
+                                        @Override
+                                        public void onDialogAdded(PropertyModel model) {
+                                            dialogQueuedToShow.set(true);
+                                        }
+                                    });
+                });
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    BackPressManager manager =
+                            mActivityTestRule.getActivity().getBackPressManagerForTesting();
+                    var backEvent = new BackEventCompat(0, 0, 0, BackEventCompat.EDGE_LEFT);
+                    manager.getCallback().handleOnBackStarted(backEvent);
+                });
+        runJavaScriptOnTab("window.alert('during transition');");
+        waitForNumSuspendedDialogs(ModalDialogType.TAB, 1);
+        Assert.assertFalse(dialogQueuedToShow.get());
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    BackPressManager manager =
+                            mActivityTestRule.getActivity().getBackPressManagerForTesting();
+                    var backEvent = new BackEventCompat(1, 0, 0.8f, BackEventCompat.EDGE_LEFT);
+                    manager.getCallback().handleOnBackProgressed(backEvent);
+                    manager.getCallback().handleOnBackPressed();
+                });
+
+        waitForTransitionFinished();
+        Assert.assertEquals(0, numSuspendedDialogs(ModalDialogType.TAB));
+        Assert.assertFalse(dialogQueuedToShow.get());
+
+        // After the transition, dialogs are resumed.
+        runJavaScriptOnTab("window.alert('after transition');");
+        waitForModalDialogShown();
+        Assert.assertTrue(dialogQueuedToShow.get());
+    }
+
+    /**
+     * Test that the modal dialogs are suspended during the transition but resumed after the
+     * transition is cancelled.
+     */
+    @Test
+    @MediumTest
+    @MinAndroidSdkLevel(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public void alertResumedAfterGestureCancelled() throws Throwable {
+        // TouchCommon.java doesn't have a "cancel gesture".
+        if (mTestNavigationMode == NAVIGATION_MODE_THREE_BUTTON) return;
+
+        // Put "blue.html" and then "green.html" in the session history.
+        String url1 = mTestServer.getURL("/chrome/test/data/android/blue.html");
+        String url2 = mTestServer.getURL("/chrome/test/data/android/green.html");
+        mActivityTestRule.loadUrl(url1);
+        mActivityTestRule.loadUrl(url2);
+
+        final AtomicBoolean dialogQueuedToShow = new AtomicBoolean(false);
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    getDialogManager()
+                            .addObserver(
+                                    new ModalDialogManager.ModalDialogManagerObserver() {
+                                        @Override
+                                        public void onDialogAdded(PropertyModel model) {
+                                            dialogQueuedToShow.set(true);
+                                        }
+                                    });
+                });
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    BackPressManager manager =
+                            mActivityTestRule.getActivity().getBackPressManagerForTesting();
+                    var backEvent = new BackEventCompat(0, 0, 0, BackEventCompat.EDGE_LEFT);
+                    manager.getCallback().handleOnBackStarted(backEvent);
+                });
+        runJavaScriptOnTab("window.alert('shown after transition');");
+        waitForNumSuspendedDialogs(ModalDialogType.TAB, 1);
+        Assert.assertFalse(dialogQueuedToShow.get());
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    BackPressManager manager =
+                            mActivityTestRule.getActivity().getBackPressManagerForTesting();
+                    var backEvent = new BackEventCompat(1, 0, .8f, BackEventCompat.EDGE_LEFT);
+                    manager.getCallback().handleOnBackProgressed(backEvent);
+                    manager.getCallback().handleOnBackCancelled();
+                });
+        waitForTransitionFinished();
+        Assert.assertEquals(0, numSuspendedDialogs(ModalDialogType.TAB));
+
+        // After the transition is finished, the dialog is resumed.
+        waitForModalDialogShown();
+        Assert.assertTrue(dialogQueuedToShow.get());
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageNavigationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageNavigationTest.java
index 04fa7150..5cd87fe 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageNavigationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageNavigationTest.java
@@ -101,7 +101,7 @@
         Assert.assertEquals(UrlConstants.NTP_URL, url);
 
         // Check that the NTP is actually displayed.
-        Assert.assertNotNull(tab.getNativePage() instanceof NewTabPage);
+        Assert.assertTrue(tab.getNativePage() instanceof NewTabPage);
     }
 
     /** Tests navigating to the tab switcher from the NTP. */
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillDeletePaymentMethodConfirmationDialogTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillDeletePaymentMethodConfirmationDialogTest.java
index c92b8fbf..9d56955 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillDeletePaymentMethodConfirmationDialogTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillDeletePaymentMethodConfirmationDialogTest.java
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+package org.chromium.chrome.browser.autofill.settings;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.verify;
@@ -24,7 +26,6 @@
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.autofill.settings.AutofillDeletePaymentMethodConfirmationDialog;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
 import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
 import org.chromium.ui.modaldialog.ModalDialogProperties;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediatorTest.java
index 1eae022e..39d58b6 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediatorTest.java
@@ -852,7 +852,6 @@
         assertEquals(mBookmarkItem21.getTitle(), model.get(ImprovedBookmarkRowProperties.TITLE));
         assertEquals(EXAMPLE_URL_FORMATTED, model.get(ImprovedBookmarkRowProperties.DESCRIPTION));
         assertNotNull(model.get(ImprovedBookmarkRowProperties.START_ICON_DRAWABLE));
-        assertNotNull(model.get(ImprovedBookmarkRowProperties.START_AREA_BACKGROUND_COLOR));
         assertNull(model.get(ImprovedBookmarkRowProperties.START_ICON_TINT));
         assertNotNull(model.get(ImprovedBookmarkRowProperties.POPUP_LISTENER));
         assertEquals(false, model.get(ImprovedBookmarkRowProperties.SELECTION_ACTIVE));
@@ -927,7 +926,6 @@
         assertEquals(mBookmarkItem21.getTitle(), model.get(ImprovedBookmarkRowProperties.TITLE));
         assertEquals(EXAMPLE_URL_FORMATTED, model.get(ImprovedBookmarkRowProperties.DESCRIPTION));
         assertNotNull(model.get(ImprovedBookmarkRowProperties.START_ICON_DRAWABLE));
-        assertNotNull(model.get(ImprovedBookmarkRowProperties.START_AREA_BACKGROUND_COLOR));
         assertNull(model.get(ImprovedBookmarkRowProperties.START_ICON_TINT));
         assertNotNull(model.get(ImprovedBookmarkRowProperties.START_ICON_DRAWABLE));
         assertNotNull(model.get(ImprovedBookmarkRowProperties.POPUP_LISTENER));
@@ -1013,7 +1011,6 @@
                 ImageVisibility.DRAWABLE,
                 model.get(ImprovedBookmarkRowProperties.START_IMAGE_VISIBILITY));
         assertNotNull(model.get(ImprovedBookmarkRowProperties.START_ICON_DRAWABLE));
-        assertNotNull(model.get(ImprovedBookmarkRowProperties.START_AREA_BACKGROUND_COLOR));
         assertNotNull(model.get(ImprovedBookmarkRowProperties.START_ICON_TINT));
         assertNotNull(model.get(ImprovedBookmarkRowProperties.POPUP_LISTENER));
         assertEquals(false, model.get(ImprovedBookmarkRowProperties.SELECTION_ACTIVE));
@@ -1075,7 +1072,6 @@
         assertEquals(
                 mFolderItem2.getTitle() + " (1)", model.get(ImprovedBookmarkRowProperties.TITLE));
         assertNotNull(model.get(ImprovedBookmarkRowProperties.START_ICON_DRAWABLE));
-        assertNotNull(model.get(ImprovedBookmarkRowProperties.START_AREA_BACKGROUND_COLOR));
         assertNotNull(model.get(ImprovedBookmarkRowProperties.START_ICON_TINT));
         assertNotNull(model.get(ImprovedBookmarkRowProperties.POPUP_LISTENER));
         assertEquals(false, model.get(ImprovedBookmarkRowProperties.SELECTION_ACTIVE));
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkRowCoordinatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkRowCoordinatorTest.java
index 359653a..b3ef4a52 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkRowCoordinatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkRowCoordinatorTest.java
@@ -269,7 +269,6 @@
         PropertyModel model = mCoordinator.createBasePropertyModel(READING_LIST_BOOKMARK_ID);
         assertFalse(mCoordinator.shouldShowImagesForFolder(READING_LIST_BOOKMARK_ID));
 
-        assertNotNull(model.get(ImprovedBookmarkRowProperties.FOLDER_START_AREA_BACKGROUND_COLOR));
         assertNotNull(model.get(ImprovedBookmarkRowProperties.FOLDER_START_ICON_TINT));
         assertNotNull(model.get(ImprovedBookmarkRowProperties.FOLDER_START_ICON_DRAWABLE));
         assertEquals(0, model.get(ImprovedBookmarkRowProperties.FOLDER_CHILD_COUNT));
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 3625b04..2b3122f 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
@@ -45,7 +45,8 @@
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 import org.robolectric.Robolectric;
 import org.robolectric.annotation.Config;
 
@@ -105,6 +106,7 @@
 @DisableFeatures(ChromeFeatureList.TAB_STRIP_INCOGNITO_MIGRATION)
 public class StripLayoutHelperManagerTest {
     @Rule public JniMocker mJniMocker = new JniMocker();
+    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Mock private TabStripSceneLayer.Natives mTabStripSceneMock;
     @Mock private TabStripSceneLayer mTabStripTreeProvider;
     @Mock private LayoutManagerHost mManagerHost;
@@ -150,17 +152,16 @@
 
     @Before
     public void beforeTest() {
-        MockitoAnnotations.initMocks(this);
         mJniMocker.mock(TabStripSceneLayerJni.TEST_HOOKS, mTabStripSceneMock);
         mActivity = Robolectric.buildActivity(Activity.class).setup().get();
         mActivity.setTheme(R.style.Theme_BrowserUI_DayNight);
-        when(mToolbarContainerView.getContext()).thenReturn(mActivity);
-        when(mToolbarManager.getStatusBarColorController()).thenReturn(mStatusBarColorController);
-
         TabStripSceneLayer.setTestFlag(true);
 
+        when(mToolbarContainerView.getContext()).thenReturn(mActivity);
+        when(mToolbarManager.getStatusBarColorController()).thenReturn(mStatusBarColorController);
         when(mDesktopWindowStateProvider.isInUnfocusedDesktopWindow()).thenReturn(false);
         when(mWindowAndroid.getActivity()).thenReturn(new WeakReference<>(mActivity));
+
         initializeTest();
     }
 
@@ -347,7 +348,7 @@
 
     @Test
     @DisableFeatures(ChromeFeatureList.TAB_STRIP_LAYOUT_OPTIMIZATION)
-    public void testModelSelectorButtonXPosition() {
+    public void testModelSelectorButtonDrawX() {
         // Set model selector button position.
         mStripLayoutHelperManager.setModelSelectorButtonVisibleForTesting(true);
         mStripLayoutHelperManager.onSizeChanged(
@@ -364,7 +365,7 @@
 
     @Test
     @DisableFeatures(ChromeFeatureList.TAB_STRIP_LAYOUT_OPTIMIZATION)
-    public void testModelSelectorButtonXPosition_RTL() {
+    public void testModelSelectorButtonDrawX_Rtl() {
         // Set model selector button position.
         LocalizationUtils.setRtlForTesting(true);
         mStripLayoutHelperManager.setModelSelectorButtonVisibleForTesting(true);
@@ -380,7 +381,7 @@
     }
 
     @Test
-    public void testModelSelectorButtonYPosition() {
+    public void testModelSelectorButtonDrawY() {
         // Set model selector button position.
         mStripLayoutHelperManager.onSizeChanged(
                 SCREEN_WIDTH, SCREEN_HEIGHT, VISIBLE_VIEWPORT_Y, ORIENTATION);
@@ -540,7 +541,7 @@
     }
 
     @Test
-    public void testFadeDrawable_Left_RTL_ModelSelectorButtonVisible() {
+    public void testFadeDrawable_Left_Rtl_ModelSelectorButtonVisible() {
         // setup
         mStripLayoutHelperManager.setModelSelectorButtonVisibleForTesting(true);
         LocalizationUtils.setRtlForTesting(true);
@@ -553,7 +554,7 @@
     }
 
     @Test
-    public void testFadeDrawable_Left_RTL() {
+    public void testFadeDrawable_Left_Rtl() {
         // setup
         LocalizationUtils.setRtlForTesting(true);
 
@@ -565,7 +566,7 @@
     }
 
     @Test
-    public void testFadeDrawable_Right_RTL() {
+    public void testFadeDrawable_Right_Rtl() {
         // setup
         mStripLayoutHelperManager.setModelSelectorButtonVisibleForTesting(true);
         LocalizationUtils.setRtlForTesting(true);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
index f743e90..d79b371 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
@@ -3924,13 +3924,16 @@
         // Verify.
         StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
         assertTrue("Should be in reorder mode.", mStripLayoutHelper.getInReorderModeForTesting());
-        assertNotEquals("Should be tab margin after tab 0.", 0, tabs[0].getTrailingMargin());
+        assertEquals(
+                "Should not be tab margin after tab 0.", 0, tabs[0].getTrailingMargin(), EPSILON);
         assertEquals(
                 "Should not be tab margin after tab 1.", 0, tabs[1].getTrailingMargin(), EPSILON);
-        assertNotEquals("Should be tab margin after tab 2.", 0, tabs[2].getTrailingMargin());
+        assertEquals(
+                "Should not be tab margin after tab 2.", 0, tabs[2].getTrailingMargin(), EPSILON);
         assertEquals(
                 "Should not be tab margin after tab 3.", 0, tabs[3].getTrailingMargin(), EPSILON);
-        assertNotEquals("Should be tab margin after tab 4.", 0, tabs[4].getTrailingMargin());
+        assertNotEquals(
+                "Should be tab margin after tab 4.", 0, tabs[4].getTrailingMargin(), EPSILON);
 
         assertEquals(
                 "TouchableRect does not match. Touch size should match the strip during drag.",
@@ -3962,22 +3965,26 @@
         StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
         assertEquals(
                 "Should not be tab margin after tab 0.", 0, tabs[0].getTrailingMargin(), EPSILON);
-        assertNotEquals("Should be tab margin after tab 1.", 0, tabs[1].getTrailingMargin());
+        assertEquals(
+                "Should not be tab margin after tab 1.", 0, tabs[1].getTrailingMargin(), EPSILON);
         assertEquals(
                 "Should not be tab margin after tab 2.", 0, tabs[2].getTrailingMargin(), EPSILON);
-        assertNotEquals("Should be tab margin after tab 3.", 0, tabs[3].getTrailingMargin());
+        assertNotEquals(
+                "Should be tab margin after tab 3.", 0, tabs[3].getTrailingMargin(), EPSILON);
 
         // Now hover between 1st and 2nd tab:
         // tabWidth(265) - overlapWidth(28) = 237
         mStripLayoutHelper.updateReorderPositionForTabDrop(237.f);
 
         // Verify.
-        assertNotEquals("Should be tab margin after tab 0.", 0, tabs[0].getTrailingMargin());
+        assertEquals(
+                "Should not be tab margin after tab 0.", 0, tabs[0].getTrailingMargin(), EPSILON);
         assertEquals(
                 "Should not be tab margin after tab 1.", 0, tabs[1].getTrailingMargin(), EPSILON);
         assertEquals(
                 "Should not be tab margin after tab 2.", 0, tabs[2].getTrailingMargin(), EPSILON);
-        assertNotEquals("Should be tab margin after tab 3.", 0, tabs[3].getTrailingMargin());
+        assertNotEquals(
+                "Should be tab margin after tab 3.", 0, tabs[3].getTrailingMargin(), EPSILON);
     }
 
     @Test
@@ -4007,7 +4014,8 @@
                 "Should not be tab margin after tab 0.", 0, tabs[0].getTrailingMargin(), EPSILON);
         assertEquals(
                 "Should not be tab margin after tab 1.", 0, tabs[1].getTrailingMargin(), EPSILON);
-        assertNotEquals("Should be tab margin after tab 2.", 0, tabs[2].getTrailingMargin());
+        assertNotEquals(
+                "Should be tab margin after tab 2.", 0, tabs[2].getTrailingMargin(), EPSILON);
 
         // Hover in end gap:
         mStripLayoutHelper.updateReorderPositionForTabDrop(1100);
@@ -4017,7 +4025,8 @@
                 "Should not be tab margin after tab 0.", 0, tabs[0].getTrailingMargin(), EPSILON);
         assertEquals(
                 "Should not be tab margin after tab 1.", 0, tabs[1].getTrailingMargin(), EPSILON);
-        assertNotEquals("Should be tab margin after tab 2.", 0, tabs[2].getTrailingMargin());
+        assertNotEquals(
+                "Should be tab margin after tab 2.", 0, tabs[2].getTrailingMargin(), EPSILON);
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/AuthTabIntentDataProviderUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/AuthTabIntentDataProviderUnitTest.java
index 044f65a..6ecfc95 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/AuthTabIntentDataProviderUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/AuthTabIntentDataProviderUnitTest.java
@@ -8,6 +8,11 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import static org.chromium.chrome.browser.customtabs.AuthTabIntentDataProvider.EXTRA_HTTPS_REDIRECT_HOST;
+import static org.chromium.chrome.browser.customtabs.AuthTabIntentDataProvider.EXTRA_HTTPS_REDIRECT_PATH;
+import static org.chromium.chrome.browser.customtabs.CustomTabsFeatureUsage.CUSTOM_TABS_FEATURE_USAGE_HISTOGRAM;
+import static org.chromium.chrome.browser.flags.ChromeFeatureList.CCT_FEATURE_USAGE;
+
 import android.app.Activity;
 import android.content.Intent;
 import android.net.Uri;
@@ -25,6 +30,8 @@
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Batch;
+import org.chromium.base.test.util.Features;
+import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider.CustomTabsUiType;
 import org.chromium.chrome.browser.flags.ActivityType;
@@ -33,6 +40,7 @@
 @RunWith(BaseRobolectricTestRunner.class)
 @Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
+@Features.EnableFeatures(CCT_FEATURE_USAGE)
 public class AuthTabIntentDataProviderUnitTest {
     @Rule
     public ActivityScenarioRule<TestActivity> mActivityScenario =
@@ -41,6 +49,8 @@
     private static final String PACKAGE = "com.example.package.app";
     private static final String URL = "https://www.google.com";
     private static final String SCHEME = "myscheme";
+    private static final String HOST = "www.host.com";
+    private static final String PATH = "/path/auth";
 
     private Activity mActivity;
     private Intent mIntent;
@@ -52,16 +62,15 @@
         mIntent = new Intent(Intent.ACTION_VIEW);
         mIntent.setData(Uri.parse(URL));
         mIntent.putExtra(AuthTabIntent.EXTRA_LAUNCH_AUTH_TAB, true);
-        mIntent.putExtra(AuthTabIntent.EXTRA_REDIRECT_SCHEME, SCHEME);
         mIntent.putExtra(IntentHandler.EXTRA_CALLING_ACTIVITY_PACKAGE, PACKAGE);
         Bundle bundle = new Bundle();
         bundle.putBinder(CustomTabsIntent.EXTRA_SESSION, null);
         mIntent.putExtras(bundle);
-        mIntentDataProvider = new AuthTabIntentDataProvider(mIntent, mActivity);
     }
 
     @Test
     public void testOverriddenDefaults() {
+        mIntentDataProvider = new AuthTabIntentDataProvider(mIntent, mActivity);
         assertEquals(
                 "ActivityType should be AUTH_TAB.",
                 ActivityType.AUTH_TAB,
@@ -86,9 +95,53 @@
 
     @Test
     public void testIntentData() {
+        var histogramWatcher =
+                HistogramWatcher.newBuilder()
+                        .expectIntRecord(
+                                CUSTOM_TABS_FEATURE_USAGE_HISTOGRAM,
+                                CustomTabsFeatureUsage.CustomTabsFeature.EXTRA_LAUNCH_AUTH_TAB)
+                        .allowExtraRecordsForHistogramsAbove()
+                        .build();
+        mIntentDataProvider = new AuthTabIntentDataProvider(mIntent, mActivity);
+
         assertEquals("Intent doesn't match expectation.", mIntent, mIntentDataProvider.getIntent());
         assertEquals("Wrong package name", PACKAGE, mIntentDataProvider.getClientPackageName());
         assertEquals("Wrong URL", URL, mIntentDataProvider.getUrlToLoad());
+        histogramWatcher.assertExpected();
+    }
+
+    @Test
+    public void testIntentData_customScheme() {
+        var histogramWatcher =
+                HistogramWatcher.newBuilder()
+                        .expectIntRecord(
+                                CUSTOM_TABS_FEATURE_USAGE_HISTOGRAM,
+                                CustomTabsFeatureUsage.CustomTabsFeature.EXTRA_REDIRECT_SCHEME)
+                        .allowExtraRecordsForHistogramsAbove()
+                        .build();
+        mIntent.putExtra(AuthTabIntent.EXTRA_REDIRECT_SCHEME, SCHEME);
+        mIntentDataProvider = new AuthTabIntentDataProvider(mIntent, mActivity);
+
         assertEquals("Wrong redirect scheme.", SCHEME, mIntentDataProvider.getAuthRedirectScheme());
+        histogramWatcher.assertExpected();
+    }
+
+    @Test
+    public void testIntentData_https() {
+        var histogramWatcher =
+                HistogramWatcher.newBuilder()
+                        .expectIntRecords(
+                                CUSTOM_TABS_FEATURE_USAGE_HISTOGRAM,
+                                CustomTabsFeatureUsage.CustomTabsFeature.EXTRA_HTTPS_REDIRECT_HOST,
+                                CustomTabsFeatureUsage.CustomTabsFeature.EXTRA_HTTPS_REDIRECT_PATH)
+                        .allowExtraRecordsForHistogramsAbove()
+                        .build();
+        mIntent.putExtra(EXTRA_HTTPS_REDIRECT_HOST, HOST);
+        mIntent.putExtra(EXTRA_HTTPS_REDIRECT_PATH, PATH);
+        mIntentDataProvider = new AuthTabIntentDataProvider(mIntent, mActivity);
+
+        assertEquals("Wrong https redirect host.", HOST, mIntentDataProvider.getAuthRedirectHost());
+        assertEquals("Wrong https redirect path.", PATH, mIntentDataProvider.getAuthRedirectPath());
+        histogramWatcher.assertExpected();
     }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationUnitTest.java
index e54fece..89ce823 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationUnitTest.java
@@ -98,7 +98,7 @@
             activityClass =
                     (Class<? extends Activity>) Class.forName(intent.getComponent().getClassName());
         } catch (ClassNotFoundException e) {
-            Assert.fail();
+            throw new RuntimeException(e);
         }
         createActivity(activityClass, intent);
     }
diff --git a/chrome/android/webapk/libs/runtime_library/javatests/src/org/chromium/webapk/lib/runtime_library/WebApkServiceImplTest.java b/chrome/android/webapk/libs/runtime_library/javatests/src/org/chromium/webapk/lib/runtime_library/WebApkServiceImplTest.java
index ac7c904..475d1ba5 100644
--- a/chrome/android/webapk/libs/runtime_library/javatests/src/org/chromium/webapk/lib/runtime_library/WebApkServiceImplTest.java
+++ b/chrome/android/webapk/libs/runtime_library/javatests/src/org/chromium/webapk/lib/runtime_library/WebApkServiceImplTest.java
@@ -103,9 +103,8 @@
                             context.getPackageName(), PackageManager.GET_META_DATA);
             return appInfo.uid;
         } catch (Exception e) {
-            Assert.fail();
+            throw new RuntimeException(e);
         }
-        return -1;
     }
 
     /**
diff --git a/chrome/android/webapk/shell_apk/javatests/src/org/chromium/webapk/shell_apk/h2o/SplashUtilsTest.java b/chrome/android/webapk/shell_apk/javatests/src/org/chromium/webapk/shell_apk/h2o/SplashUtilsTest.java
index 9b9d27b..f5f16543 100644
--- a/chrome/android/webapk/shell_apk/javatests/src/org/chromium/webapk/shell_apk/h2o/SplashUtilsTest.java
+++ b/chrome/android/webapk/shell_apk/javatests/src/org/chromium/webapk/shell_apk/h2o/SplashUtilsTest.java
@@ -51,7 +51,7 @@
         try {
             bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
         } catch (Exception e) {
-            Assert.fail();
+            throw new RuntimeException(e);
         }
         int firstColor = pixels[0];
         for (int i = 1; i < pixels.length; ++i) {
diff --git a/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/HostBrowserClassLoaderTest.java b/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/HostBrowserClassLoaderTest.java
index 288fa26c..40259bc 100644
--- a/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/HostBrowserClassLoaderTest.java
+++ b/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/HostBrowserClassLoaderTest.java
@@ -23,6 +23,7 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.File;
+import java.io.IOException;
 import java.io.InputStream;
 
 /** Tests HostBrowserClassLoader. */
@@ -47,7 +48,7 @@
     private DexLoader mMockDexLoader;
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         mContext = RuntimeEnvironment.application;
         mPackageManager = mContext.getPackageManager();
         setRemoteVersionCode(REMOTE_VERSION_CODE);
@@ -65,7 +66,7 @@
 
     /** Test upgrading to a new runtime dex version. */
     @Test
-    public void testNewRuntimeDexVersion() {
+    public void testNewRuntimeDexVersion() throws Exception {
         HostBrowserClassLoader.createClassLoader(mContext, mRemoteContext, mMockDexLoader, null);
 
         String expectedDexName = WebApkCommonUtils.getRuntimeDexName(REMOTE_DEX_VERSION);
@@ -139,13 +140,9 @@
      * Sets the version of the current runtime library dex stored in the remote host browser's
      * assets.
      */
-    public void setRemoteDexVersion(int dexVersion) {
-        try {
-            Mockito.when(mRemoteAssetManager.open("webapk_dex_version.txt"))
-                    .thenReturn(createIntInputStream(dexVersion));
-        } catch (Exception e) {
-            Assert.fail();
-        }
+    public void setRemoteDexVersion(int dexVersion) throws IOException {
+        Mockito.when(mRemoteAssetManager.open("webapk_dex_version.txt"))
+                .thenReturn(createIntInputStream(dexVersion));
     }
 
     /**
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 220bbf4..af35406 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
@@ -791,7 +791,7 @@
                         (Class<? extends Activity>)
                                 Class.forName(startedActivityIntent.getComponent().getClassName());
             } catch (ClassNotFoundException e) {
-                Assert.fail();
+                throw new RuntimeException(e);
             }
             buildActivityFully(startedActivityClass, startedActivityIntent);
         }
diff --git a/chrome/app/password_manager_ui_strings.grdp b/chrome/app/password_manager_ui_strings.grdp
index cecc390..472b4689 100644
--- a/chrome/app/password_manager_ui_strings.grdp
+++ b/chrome/app/password_manager_ui_strings.grdp
@@ -3,6 +3,9 @@
   <message name="IDS_PASSWORD_MANAGER_UI_TITLE" desc="Title of the Password Manager page">
     Password Manager
   </message>
+  <message name="IDS_PASSWORD_MANAGER_UI_DESCRIPTION" desc="A description of the application.">
+    Keep track of your passwords and manage them in one place.
+  </message>
   <message name="IDS_PASSWORD_MANAGER_UI_SEARCH_PROMPT" desc="Placeholder in the search field.">
     Search passwords
   </message>
diff --git a/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_DESCRIPTION.png.sha1 b/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..fea542d1
--- /dev/null
+++ b/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+2cf2992540345256c40454c85504ea2c3e6bd17c
\ No newline at end of file
diff --git a/chrome/browser/ash/accessibility/service/automation_client_impl.cc b/chrome/browser/ash/accessibility/service/automation_client_impl.cc
index cd8d8fe..f6db723aa 100644
--- a/chrome/browser/ash/accessibility/service/automation_client_impl.cc
+++ b/chrome/browser/ash/accessibility/service/automation_client_impl.cc
@@ -3,10 +3,12 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/ash/accessibility/service/automation_client_impl.h"
+
 #include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h"
 #include "extensions/browser/api/automation_internal/automation_event_router.h"
 #include "extensions/browser/api/automation_internal/automation_internal_api.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
+#include "ui/accessibility/ax_location_and_scroll_updates.h"
 
 namespace ash {
 
@@ -55,8 +57,8 @@
 }
 
 void AutomationClientImpl::DispatchAccessibilityLocationChange(
-    const ui::AXLocationChanges& details) {
-  ui::AXTreeID tree_id = details.ax_tree_id;
+    const ui::AXTreeID& tree_id,
+    const ui::AXLocationChange& details) {
   if (tree_id == ui::AXTreeIDUnknown())
     return;
   for (auto& remote : automation_remotes_) {
diff --git a/chrome/browser/ash/accessibility/service/automation_client_impl.h b/chrome/browser/ash/accessibility/service/automation_client_impl.h
index 3588d42..74f4b104 100644
--- a/chrome/browser/ash/accessibility/service/automation_client_impl.h
+++ b/chrome/browser/ash/accessibility/service/automation_client_impl.h
@@ -11,6 +11,7 @@
 #include "mojo/public/cpp/bindings/remote_set.h"
 #include "services/accessibility/public/mojom/automation.mojom.h"
 #include "services/accessibility/public/mojom/automation_client.mojom.h"
+#include "ui/accessibility/ax_location_and_scroll_updates.h"
 
 namespace ash {
 
@@ -51,7 +52,8 @@
       const gfx::Point& mouse_location,
       const std::vector<ui::AXEvent>& events) override;
   void DispatchAccessibilityLocationChange(
-      const ui::AXLocationChanges& details) override;
+      const ui::AXTreeID& tree_id,
+      const ui::AXLocationChange& details) override;
   void DispatchTreeDestroyedEvent(ui::AXTreeID tree_id) override;
   void DispatchActionResult(const ui::AXActionData& data,
                             bool result,
diff --git a/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc b/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc
index 733b30a..871e611 100644
--- a/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc
+++ b/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc
@@ -62,7 +62,8 @@
       const std::vector<ui::AXEvent>& events) override {}
 
   void DispatchAccessibilityLocationChange(
-      const ui::AXLocationChanges& details) override {}
+      const ui::AXTreeID& tree_id,
+      const ui::AXLocationChange& details) override {}
 
   void DispatchTreeDestroyedEvent(ui::AXTreeID tree_id) override {}
 
diff --git a/chrome/browser/ash/crosapi/automation_ash.cc b/chrome/browser/ash/crosapi/automation_ash.cc
index 65711581..e1b9ac39 100644
--- a/chrome/browser/ash/crosapi/automation_ash.cc
+++ b/chrome/browser/ash/crosapi/automation_ash.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/ash/crosapi/automation_ash.h"
 
+#include "ui/accessibility/ax_location_and_scroll_updates.h"
+#include "ui/accessibility/ax_tree_id.h"
+
 namespace crosapi {
 
 AutomationAsh::AutomationAsh() {
@@ -63,12 +66,12 @@
     const base::UnguessableToken& tree_id,
     int32_t node_id,
     const ui::AXRelativeBounds& bounds) {
-  ui::AXLocationChanges details;
-  details.ax_tree_id = ui::AXTreeID::FromToken(tree_id);
+  ui::AXLocationChange details;
   details.id = node_id;
   details.new_location = bounds;
+  ui::AXTreeID ui_tree_id = ui::AXTreeID::FromToken(tree_id);
   extensions::AutomationEventRouter::GetInstance()
-      ->DispatchAccessibilityLocationChange(details);
+      ->DispatchAccessibilityLocationChange(ui_tree_id, details);
 }
 
 void AutomationAsh::DispatchTreeDestroyedEvent(
diff --git a/chrome/browser/ash/crostini/crostini_export_import.cc b/chrome/browser/ash/crostini/crostini_export_import.cc
index 45d6cd8..962f33e 100644
--- a/chrome/browser/ash/crostini/crostini_export_import.cc
+++ b/chrome/browser/ash/crostini/crostini_export_import.cc
@@ -367,7 +367,12 @@
                          std::move(callback)));
       break;
     case ExportImportType::IMPORT_DISK_IMAGE:
-      LOG(ERROR) << "Importing disk images is currently unimplemented";
+      crostini::CrostiniManager::GetForProfile(profile_)->StopVm(
+          operation_data->container_id.vm_name,
+          base::BindOnce(&CrostiniExportImport::ImportDiskImage,
+                         weak_ptr_factory_.GetWeakPtr(),
+                         operation_data->container_id, path,
+                         std::move(callback)));
       break;
   }
 }
@@ -399,12 +404,44 @@
 
   CrostiniManager::GetForProfile(profile_)->ExportDiskImage(
       container_id, user->username_hash(), path, /*force=*/false,
-      base::BindOnce(&CrostiniExportImport::AfterExportDiskImage,
+      base::BindOnce(&CrostiniExportImport::AfterDiskImageOperation,
                      weak_ptr_factory_.GetWeakPtr(), container_id,
                      std::move(callback)));
 }
 
-void CrostiniExportImport::AfterExportDiskImage(
+void CrostiniExportImport::ImportDiskImage(
+    const guest_os::GuestId& container_id,
+    const base::FilePath& path,
+    CrostiniManager::CrostiniResultCallback callback,
+    CrostiniResult result) {
+  if (result == CrostiniResult::VM_STOP_FAILED) {
+    LOG(ERROR) << "Unable to stop VM, cannot import disk image";
+    std::move(callback).Run(CrostiniResult::DISK_IMAGE_FAILED);
+    return;
+  }
+
+  ash::ProfileHelper* profile_helper = ash::ProfileHelper::Get();
+  if (!profile_helper) {
+    LOG(ERROR) << "Unable to get profile helper";
+    std::move(callback).Run(CrostiniResult::DISK_IMAGE_FAILED);
+    return;
+  }
+  user_manager::User* user = profile_helper->GetUserByProfile(profile_);
+
+  if (!user) {
+    LOG(ERROR) << "Unable to get user";
+    std::move(callback).Run(CrostiniResult::DISK_IMAGE_FAILED);
+    return;
+  }
+
+  CrostiniManager::GetForProfile(profile_)->ImportDiskImage(
+      container_id, user->username_hash(), path,
+      base::BindOnce(&CrostiniExportImport::AfterDiskImageOperation,
+                     weak_ptr_factory_.GetWeakPtr(), container_id,
+                     std::move(callback)));
+}
+
+void CrostiniExportImport::AfterDiskImageOperation(
     const guest_os::GuestId& container_id,
     CrostiniManager::CrostiniResultCallback callback,
     CrostiniResult result) {
@@ -858,6 +895,7 @@
       manager.CancelImportLxdContainer(std::move(container_id));
       return;
     case ExportImportType::EXPORT_DISK_IMAGE:
+    case ExportImportType::IMPORT_DISK_IMAGE:
       manager.CancelDiskImageOp(std::move(container_id));
       return;
     default:
diff --git a/chrome/browser/ash/crostini/crostini_export_import.h b/chrome/browser/ash/crostini/crostini_export_import.h
index e30a963..be22348 100644
--- a/chrome/browser/ash/crostini/crostini_export_import.h
+++ b/chrome/browser/ash/crostini/crostini_export_import.h
@@ -202,6 +202,11 @@
   FRIEND_TEST_ALL_PREFIXES(CrostiniExportImportTest, TestExportDiskImageFail);
   FRIEND_TEST_ALL_PREFIXES(CrostiniExportImportTest,
                            TestExportDiskImageCancelled);
+  FRIEND_TEST_ALL_PREFIXES(CrostiniExportImportTest,
+                           TestImportDiskImageSuccess);
+  FRIEND_TEST_ALL_PREFIXES(CrostiniExportImportTest, TestImportDiskImageFail);
+  FRIEND_TEST_ALL_PREFIXES(CrostiniExportImportTest,
+                           TestImportDiskImageCancelled);
 
   void FillOperationData(ExportImportType type,
                          guest_os::GuestId id,
@@ -255,9 +260,14 @@
                        CrostiniManager::CrostiniResultCallback callback,
                        CrostiniResult result);
 
-  void AfterExportDiskImage(const guest_os::GuestId& container_id,
-                            CrostiniManager::CrostiniResultCallback callback,
-                            CrostiniResult result);
+  void ImportDiskImage(const guest_os::GuestId& container_id,
+                       const base::FilePath& path,
+                       CrostiniManager::CrostiniResultCallback callback,
+                       CrostiniResult result);
+
+  void AfterDiskImageOperation(const guest_os::GuestId& container_id,
+                               CrostiniManager::CrostiniResultCallback callback,
+                               CrostiniResult result);
 
   void ExportAfterSharing(const guest_os::GuestId& container_id,
                           const base::FilePath& path,
diff --git a/chrome/browser/ash/crostini/crostini_export_import_status_tracker.cc b/chrome/browser/ash/crostini/crostini_export_import_status_tracker.cc
index 9685a77..e300ae7 100644
--- a/chrome/browser/ash/crostini/crostini_export_import_status_tracker.cc
+++ b/chrome/browser/ash/crostini/crostini_export_import_status_tracker.cc
@@ -18,7 +18,8 @@
     base::FilePath path)
     : type_(type), path_(path) {
   DCHECK(type == ExportImportType::EXPORT || type == ExportImportType::IMPORT ||
-         type == ExportImportType::EXPORT_DISK_IMAGE);
+         type == ExportImportType::EXPORT_DISK_IMAGE ||
+         type == ExportImportType::IMPORT_DISK_IMAGE);
 }
 
 CrostiniExportImportStatusTracker::~CrostiniExportImportStatusTracker() =
diff --git a/chrome/browser/ash/crostini/crostini_export_import_unittest.cc b/chrome/browser/ash/crostini/crostini_export_import_unittest.cc
index 550f9329..e65b5a3 100644
--- a/chrome/browser/ash/crostini/crostini_export_import_unittest.cc
+++ b/chrome/browser/ash/crostini/crostini_export_import_unittest.cc
@@ -28,6 +28,7 @@
 #include "chromeos/ash/components/dbus/seneschal/fake_seneschal_client.h"
 #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h"
 #include "chromeos/ash/components/dbus/seneschal/seneschal_service.pb.h"
+#include "chromeos/ash/components/dbus/vm_concierge/concierge_service.pb.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/browser_task_environment.h"
 #include "storage/browser/file_system/external_mount_points.h"
@@ -120,6 +121,12 @@
     ash::FakeConciergeClient::Get()->NotifyDiskImageProgress(signal);
   }
 
+  void SetImportResponse() {
+    vm_tools::concierge::ImportDiskImageResponse response;
+    response.set_status(vm_tools::concierge::DISK_STATUS_IN_PROGRESS);
+    ash::FakeConciergeClient::Get()->set_import_disk_image_response(response);
+  }
+
   void SendImportProgress(
       const guest_os::GuestId& container_id,
       vm_tools::cicerone::ImportLxdContainerProgressSignal_Status status,
@@ -398,6 +405,176 @@
   }
 }
 
+TEST_F(CrostiniExportImportTest, TestImportDiskImageSuccess) {
+  SetImportResponse();
+  crostini_export_import_->FillOperationData(
+      ExportImportType::IMPORT_DISK_IMAGE);
+  base::ScopedTempFile zipfile;
+  EXPECT_TRUE(zipfile.Create());
+  crostini_export_import_->FileSelected(ui::SelectedFileInfo(zipfile.path()),
+                                        0);
+  task_environment_.RunUntilIdle();
+  base::WeakPtr<CrostiniExportImportNotificationController> controller =
+      GetController(default_container_id_);
+  ASSERT_NE(controller, nullptr);
+  EXPECT_EQ(controller->status(),
+            CrostiniExportImportStatusTracker::Status::RUNNING);
+
+  std::string notification_id;
+  {
+    const message_center::Notification& notification =
+        GetNotification(default_container_id_);
+    notification_id = notification.id();
+    EXPECT_EQ(notification.progress(), 0);
+    EXPECT_TRUE(notification.pinned());
+  }
+
+  // 50% done.
+  SendDiskImageProgress(default_container_id_,
+                        vm_tools::concierge::DISK_STATUS_IN_PROGRESS, 50);
+  ASSERT_NE(controller, nullptr);
+  EXPECT_EQ(controller->status(),
+            CrostiniExportImportStatusTracker::Status::RUNNING);
+  {
+    const message_center::Notification& notification =
+        GetNotification(default_container_id_);
+    EXPECT_EQ(notification.id(), notification_id);
+    EXPECT_EQ(notification.progress(), 50);
+    EXPECT_TRUE(notification.pinned());
+  }
+
+  // Close notification and update progress. Should not update notification.
+  controller->get_delegate()->Close(false);
+  SendDiskImageProgress(default_container_id_,
+                        vm_tools::concierge::DISK_STATUS_IN_PROGRESS, 60);
+  ASSERT_NE(controller, nullptr);
+  EXPECT_EQ(controller->status(),
+            CrostiniExportImportStatusTracker::Status::RUNNING);
+  {
+    const message_center::Notification& notification =
+        GetNotification(default_container_id_);
+    EXPECT_EQ(notification.id(), notification_id);
+    EXPECT_EQ(notification.progress(), 50);
+    EXPECT_TRUE(notification.pinned());
+  }
+
+  // Done.
+  SendDiskImageProgress(default_container_id_,
+                        vm_tools::concierge::DISK_STATUS_CREATED, 100);
+  EXPECT_EQ(GetController(default_container_id_), nullptr);
+  EXPECT_EQ(controller, nullptr);
+  {
+    const std::optional<message_center::Notification> ui_notification =
+        notification_display_service_->GetNotification(notification_id);
+    ASSERT_NE(ui_notification, std::nullopt);
+    EXPECT_FALSE(ui_notification->pinned());
+    std::string msg("Linux apps & files have been successfully replaced");
+    EXPECT_EQ(ui_notification->message(), base::UTF8ToUTF16(msg));
+  }
+}
+
+TEST_F(CrostiniExportImportTest, TestImportDiskImageFail) {
+  SetImportResponse();
+  crostini_export_import_->FillOperationData(
+      ExportImportType::IMPORT_DISK_IMAGE);
+  base::ScopedTempFile zipfile;
+  EXPECT_TRUE(zipfile.Create());
+  crostini_export_import_->FileSelected(ui::SelectedFileInfo(zipfile.path()),
+                                        0);
+  task_environment_.RunUntilIdle();
+  base::WeakPtr<CrostiniExportImportNotificationController> controller =
+      GetController(default_container_id_);
+  ASSERT_NE(controller, nullptr);
+  EXPECT_EQ(controller->status(),
+            CrostiniExportImportStatusTracker::Status::RUNNING);
+
+  std::string notification_id;
+  {
+    const message_center::Notification& notification =
+        GetNotification(default_container_id_);
+    notification_id = notification.id();
+    EXPECT_EQ(notification.progress(), 0);
+    EXPECT_TRUE(notification.pinned());
+  }
+
+  // Fails.
+  SendDiskImageProgress(default_container_id_,
+                        vm_tools::concierge::DISK_STATUS_FAILED, 0);
+  EXPECT_EQ(GetController(default_container_id_), nullptr);
+  EXPECT_EQ(controller, nullptr);
+  {
+    const std::optional<message_center::Notification> ui_notification =
+        notification_display_service_->GetNotification(notification_id);
+    ASSERT_NE(ui_notification, std::nullopt);
+    EXPECT_FALSE(ui_notification->pinned());
+    std::string msg("Restoring couldn't be completed due to an error");
+    EXPECT_EQ(ui_notification->message(), base::UTF8ToUTF16(msg));
+  }
+}
+
+TEST_F(CrostiniExportImportTest, TestImportDiskImageCancelled) {
+  SetImportResponse();
+  base::ScopedTempFile zipfile;
+  EXPECT_TRUE(zipfile.Create());
+  crostini_export_import_->FillOperationData(
+      ExportImportType::IMPORT_DISK_IMAGE, custom_container_id_);
+  crostini_export_import_->FileSelected(ui::SelectedFileInfo(zipfile.path()),
+                                        0);
+  task_environment_.RunUntilIdle();
+  base::WeakPtr<CrostiniExportImportNotificationController> controller =
+      GetController(custom_container_id_);
+  ASSERT_NE(controller, nullptr);
+  EXPECT_EQ(controller->status(),
+            CrostiniExportImportStatusTracker::Status::RUNNING);
+
+  std::string notification_id;
+  {
+    const message_center::Notification& notification =
+        GetNotification(custom_container_id_);
+    notification_id = notification.id();
+    EXPECT_EQ(notification.progress(), 0);
+    EXPECT_TRUE(notification.pinned());
+  }
+
+  // CANCEL:
+  crostini_export_import_->CancelOperation(ExportImportType::IMPORT_DISK_IMAGE,
+                                           custom_container_id_);
+  ASSERT_NE(controller, nullptr);
+  EXPECT_EQ(controller->status(),
+            CrostiniExportImportStatusTracker::Status::CANCELLING);
+  {
+    const message_center::Notification& notification =
+        GetNotification(custom_container_id_);
+    EXPECT_EQ(notification.id(), notification_id);
+    EXPECT_EQ(notification.progress(), -1);
+    EXPECT_FALSE(notification.pinned());
+  }
+
+  // Should not be displayed as cancel is in progress
+  SendDiskImageProgress(default_container_id_,
+                        vm_tools::concierge::DISK_STATUS_IN_PROGRESS, 7);
+  ASSERT_NE(controller, nullptr);
+  EXPECT_EQ(controller->status(),
+            CrostiniExportImportStatusTracker::Status::CANCELLING);
+  {
+    const message_center::Notification& notification =
+        GetNotification(custom_container_id_);
+    EXPECT_EQ(notification.id(), notification_id);
+    EXPECT_EQ(notification.progress(), -1);
+    EXPECT_FALSE(notification.pinned());
+  }
+
+  // CANCELLED:
+  task_environment_.RunUntilIdle();
+  EXPECT_EQ(GetController(custom_container_id_), nullptr);
+  EXPECT_EQ(controller, nullptr);
+  {
+    const std::optional<message_center::Notification> ui_notification =
+        notification_display_service_->GetNotification(notification_id);
+    EXPECT_EQ(ui_notification, std::nullopt);
+  }
+}
+
 TEST_F(CrostiniExportImportTest, TestExportSuccess) {
   crostini_export_import_->FillOperationData(ExportImportType::EXPORT);
   crostini_export_import_->FileSelected(ui::SelectedFileInfo(tarball_), 0);
diff --git a/chrome/browser/ash/crostini/crostini_manager.cc b/chrome/browser/ash/crostini/crostini_manager.cc
index c3257e6c..8b237708 100644
--- a/chrome/browser/ash/crostini/crostini_manager.cc
+++ b/chrome/browser/ash/crostini/crostini_manager.cc
@@ -1867,13 +1867,13 @@
     return;
   }
   if (user_id_hash.empty()) {
-    LOG(ERROR) << "vm_name is required";
+    LOG(ERROR) << "user_id_hash is required";
     std::move(callback).Run(CrostiniResult::CLIENT_ERROR);
     return;
   }
 
   if (disk_image_callbacks_.find(vm_id) != disk_image_callbacks_.end()) {
-    LOG(ERROR) << "Disk image export currently running for " << vm_id;
+    LOG(ERROR) << "Disk image operation currently running for " << vm_id;
     std::move(callback).Run(CrostiniResult::DISK_IMAGE_FAILED);
   }
   disk_image_callbacks_.emplace(vm_id, std::move(callback));
@@ -1930,6 +1930,76 @@
   disk_image_uuid_to_guest_id_.emplace(response->command_uuid(), vm_id);
 }
 
+void CrostiniManager::ImportDiskImage(guest_os::GuestId vm_id,
+                                      std::string user_id_hash,
+                                      base::FilePath import_path,
+                                      CrostiniResultCallback callback) {
+  if (vm_id.vm_name.empty()) {
+    LOG(ERROR) << "vm_name is required";
+    std::move(callback).Run(CrostiniResult::CLIENT_ERROR);
+    return;
+  }
+  if (user_id_hash.empty()) {
+    LOG(ERROR) << "user_id_hash is required";
+    std::move(callback).Run(CrostiniResult::CLIENT_ERROR);
+    return;
+  }
+
+  if (disk_image_callbacks_.find(vm_id) != disk_image_callbacks_.end()) {
+    LOG(ERROR) << "Disk image operation currently running for " << vm_id;
+    std::move(callback).Run(CrostiniResult::DISK_IMAGE_FAILED);
+  }
+  disk_image_callbacks_.emplace(vm_id, std::move(callback));
+
+  base::File file(import_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
+  if (!file.IsValid()) {
+    LOG(ERROR) << "Failed to open " << import_path;
+    return;
+  }
+
+  vm_tools::concierge::ImportDiskImageRequest request;
+  request.set_vm_name(vm_id.vm_name);
+  request.set_cryptohome_id(user_id_hash);
+  // All vm's are stored in root except pluginvm, which is not supported in this
+  // flow.
+  request.set_storage_location(vm_tools::concierge::STORAGE_CRYPTOHOME_ROOT);
+  request.set_source_size(file.GetLength());
+
+  GetConciergeClient()->ImportDiskImage(
+      base::ScopedFD(file.TakePlatformFile()), std::move(request),
+      base::BindOnce(&CrostiniManager::OnImportDiskImage,
+                     weak_ptr_factory_.GetWeakPtr(), vm_id));
+}
+
+void CrostiniManager::OnImportDiskImage(
+    guest_os::GuestId vm_id,
+    std::optional<vm_tools::concierge::ImportDiskImageResponse> response) {
+  auto it = disk_image_callbacks_.find(vm_id);
+  if (it == disk_image_callbacks_.end()) {
+    LOG(ERROR) << "No import callback for " << vm_id;
+    return;
+  }
+
+  if (!response) {
+    LOG(ERROR) << "Failed to import disk image. Empty response.";
+    std::move(it->second).Run(CrostiniResult::DISK_IMAGE_FAILED);
+    disk_image_callbacks_.erase(it);
+    return;
+  }
+
+  // If import has started, the callback will be invoked when the
+  // DiskImageProgressSignal signal indicates that import is
+  // complete, otherwise this is an error.
+  if (response->status() != vm_tools::concierge::DISK_STATUS_IN_PROGRESS) {
+    LOG(ERROR) << "Failed to import image: status=" << response->status()
+               << ", failure_reason=" << response->failure_reason();
+    std::move(it->second).Run(CrostiniResult::DISK_IMAGE_FAILED);
+    disk_image_callbacks_.erase(it);
+  }
+
+  disk_image_uuid_to_guest_id_.emplace(response->command_uuid(), vm_id);
+}
+
 void CrostiniManager::OnDiskImageProgress(
     const vm_tools::concierge::DiskImageStatusResponse& signal) {
   bool call_observers = false;
diff --git a/chrome/browser/ash/crostini/crostini_manager.h b/chrome/browser/ash/crostini/crostini_manager.h
index 01807b75..b7aaa86 100644
--- a/chrome/browser/ash/crostini/crostini_manager.h
+++ b/chrome/browser/ash/crostini/crostini_manager.h
@@ -335,6 +335,15 @@
                        bool force,
                        CrostiniResultCallback callback);
 
+  // Checks the arguments for exporting a vm disk image via
+  // ConciergeClient::ImportDiskImage. |callback| is called immedaitely if the
+  // arguments are bad, or after the method call finishes.
+  // using DiskImageCallback = base::OnceCallback<void(CrostiniResult result)>;
+  void ImportDiskImage(guest_os::GuestId vm_id,
+                       std::string user_id_hash,
+                       base::FilePath import_path,
+                       CrostiniResultCallback callback);
+
   // Checks the arguments for exporting an Lxd container via
   // CiceroneClient::ExportLxdContainer. |callback| is called immediately if the
   // arguments are bad, or after the method call finishes.
@@ -704,6 +713,12 @@
       guest_os::GuestId vm_id,
       std::optional<vm_tools::concierge::ExportDiskImageResponse> response);
 
+  // Callback for ConciergeClient::ImportDiskImage. Called after the Concierge
+  // service method finishes.
+  void OnImportDiskImage(
+      guest_os::GuestId vm_id,
+      std::optional<vm_tools::concierge::ImportDiskImageResponse> response);
+
   // Callback for CiceroneClient::CreateLxdContainer. May indicate the container
   // is still being created, in which case we will wait for an
   // OnLxdContainerCreated event.
diff --git a/chrome/browser/ash/crostini/crostini_manager_unittest.cc b/chrome/browser/ash/crostini/crostini_manager_unittest.cc
index e7ce53a..336de0eb 100644
--- a/chrome/browser/ash/crostini/crostini_manager_unittest.cc
+++ b/chrome/browser/ash/crostini/crostini_manager_unittest.cc
@@ -2067,6 +2067,80 @@
   EXPECT_EQ(result_future.Get<0>(), CrostiniResult::SUCCESS);
 }
 
+TEST_F(CrostiniManagerTest, ImportDiskImageFailure) {
+  base::ScopedTempFile import_path;
+  EXPECT_TRUE(import_path.Create());
+  TestFuture<CrostiniResult> result_future;
+  EXPECT_EQ(fake_concierge_client_->import_disk_image_call_count(), 0);
+
+  vm_tools::concierge::ImportDiskImageResponse failure_response;
+  failure_response.set_status(vm_tools::concierge::DISK_STATUS_FAILED);
+  fake_concierge_client_->set_import_disk_image_response(failure_response);
+
+  crostini_manager()->ImportDiskImage(container_id(), "my_cool_user_id_hash",
+                                      import_path.path(),
+                                      result_future.GetCallback());
+
+  EXPECT_EQ(fake_concierge_client_->import_disk_image_call_count(), 1);
+  EXPECT_EQ(result_future.Get<0>(), CrostiniResult::DISK_IMAGE_FAILED);
+}
+
+TEST_F(CrostiniManagerTest, ImportDiskImageNoSpaceFailure) {
+  base::ScopedTempFile import_path;
+  EXPECT_TRUE(import_path.Create());
+  TestFuture<CrostiniResult> result_future;
+  EXPECT_EQ(fake_concierge_client_->import_disk_image_call_count(), 0);
+
+  vm_tools::concierge::DiskImageStatusResponse progress_signal;
+  progress_signal.set_status(vm_tools::concierge::DISK_STATUS_IN_PROGRESS);
+  progress_signal.set_progress(50);
+  vm_tools::concierge::DiskImageStatusResponse no_space_signal;
+  no_space_signal.set_status(vm_tools::concierge::DISK_STATUS_NOT_ENOUGH_SPACE);
+  std::vector<vm_tools::concierge::DiskImageStatusResponse> signals;
+  signals.emplace_back(progress_signal);
+  signals.emplace_back(no_space_signal);
+  fake_concierge_client_->set_disk_image_status_signals(signals);
+
+  vm_tools::concierge::ImportDiskImageResponse response;
+  response.set_status(vm_tools::concierge::DISK_STATUS_IN_PROGRESS);
+  ash::FakeConciergeClient::Get()->set_import_disk_image_response(response);
+
+  crostini_manager()->ImportDiskImage(container_id(), "my_cool_user_id_hash",
+                                      import_path.path(),
+                                      result_future.GetCallback());
+
+  EXPECT_EQ(fake_concierge_client_->import_disk_image_call_count(), 1);
+  EXPECT_EQ(result_future.Get<0>(), CrostiniResult::DISK_IMAGE_FAILED_NO_SPACE);
+}
+
+TEST_F(CrostiniManagerTest, ImportDiskImageSuccess) {
+  base::ScopedTempFile import_path;
+  EXPECT_TRUE(import_path.Create());
+  TestFuture<CrostiniResult> result_future;
+  EXPECT_EQ(fake_concierge_client_->import_disk_image_call_count(), 0);
+
+  vm_tools::concierge::DiskImageStatusResponse progress_signal;
+  progress_signal.set_status(vm_tools::concierge::DISK_STATUS_IN_PROGRESS);
+  progress_signal.set_progress(50);
+  vm_tools::concierge::DiskImageStatusResponse done_signal;
+  done_signal.set_status(vm_tools::concierge::DISK_STATUS_CREATED);
+  std::vector<vm_tools::concierge::DiskImageStatusResponse> signals;
+  signals.emplace_back(progress_signal);
+  signals.emplace_back(done_signal);
+  fake_concierge_client_->set_disk_image_status_signals(signals);
+
+  vm_tools::concierge::ImportDiskImageResponse response;
+  response.set_status(vm_tools::concierge::DISK_STATUS_IN_PROGRESS);
+  ash::FakeConciergeClient::Get()->set_import_disk_image_response(response);
+
+  crostini_manager()->ImportDiskImage(container_id(), "my_cool_user_id_hash",
+                                      import_path.path(),
+                                      result_future.GetCallback());
+
+  EXPECT_EQ(fake_concierge_client_->import_disk_image_call_count(), 1);
+  EXPECT_EQ(result_future.Get<0>(), CrostiniResult::SUCCESS);
+}
+
 TEST_F(CrostiniManagerTest, ExportContainerSuccess) {
   uint64_t container_size = 123;
   uint64_t exported_size = 456;
diff --git a/chrome/browser/autofill/android/BUILD.gn b/chrome/browser/autofill/android/BUILD.gn
index d558bd2..144a0ae 100644
--- a/chrome/browser/autofill/android/BUILD.gn
+++ b/chrome/browser/autofill/android/BUILD.gn
@@ -29,6 +29,7 @@
     "java/src/org/chromium/chrome/browser/autofill/editors/AddressEditorMediator.java",
     "java/src/org/chromium/chrome/browser/autofill/editors/DropdownFieldAdapter.java",
     "java/src/org/chromium/chrome/browser/autofill/editors/DropdownFieldView.java",
+    "java/src/org/chromium/chrome/browser/autofill/editors/EditTextNoAutofillView.java",
     "java/src/org/chromium/chrome/browser/autofill/editors/EditorBase.java",
     "java/src/org/chromium/chrome/browser/autofill/editors/EditorDialogToolbar.java",
     "java/src/org/chromium/chrome/browser/autofill/editors/EditorDialogView.java",
diff --git a/chrome/browser/autofill/android/java/res/layout/autofill_local_card_editor.xml b/chrome/browser/autofill/android/java/res/layout/autofill_local_card_editor.xml
index 32f9774..cf09bbd 100644
--- a/chrome/browser/autofill/android/java/res/layout/autofill_local_card_editor.xml
+++ b/chrome/browser/autofill/android/java/res/layout/autofill_local_card_editor.xml
@@ -21,7 +21,7 @@
         app:errorTextAppearance="@style/TextAppearance.ErrorCaption">
 
         <!-- TODO(crbug.com/40600572): Fix and remove lint ignore -->
-        <EditText
+        <org.chromium.chrome.browser.autofill.editors.EditTextNoAutofillView
             tools:ignore="Autofill,LabelFor"
             android:id="@+id/credit_card_number_edit"
             android:layout_width="match_parent"
@@ -115,7 +115,7 @@
                 android:textAppearance="@style/TextAppearance.TextSmall.Secondary"
                 android:text="@string/autofill_credit_card_editor_expiration_date" />
 
-            <EditText
+            <org.chromium.chrome.browser.autofill.editors.EditTextNoAutofillView
                 android:id="@+id/expiration_month_and_year"
                 android:width="@dimen/local_card_expiration_date_editor_width"
                 android:layout_width="match_parent"
@@ -149,7 +149,7 @@
                 android:textAppearance="@style/TextAppearance.TextSmall.Secondary"
                 android:text="@string/autofill_credit_card_editor_security_code" />
 
-            <EditText
+            <org.chromium.chrome.browser.autofill.editors.EditTextNoAutofillView
                 android:id="@+id/cvc"
                 android:width="@dimen/local_card_cvc_editor_width"
                 android:layout_width="match_parent"
@@ -186,7 +186,7 @@
         android:layout_marginBottom="@dimen/pref_autofill_field_bottom_margin">
 
         <!-- TODO(crbug.com/40600572): Fix and remove lint ignore -->
-        <EditText
+        <org.chromium.chrome.browser.autofill.editors.EditTextNoAutofillView
             tools:ignore="Autofill,LabelFor"
             android:id="@+id/credit_card_name_edit"
             android:layout_width="match_parent"
@@ -211,7 +211,7 @@
         android:layout_marginTop="@dimen/pref_autofill_field_extra_large_top_margin"
         android:layout_marginBottom="@dimen/pref_autofill_field_bottom_margin">
 
-        <EditText
+        <org.chromium.chrome.browser.autofill.editors.EditTextNoAutofillView
             tools:ignore="Autofill,LabelFor"
             android:id="@+id/credit_card_nickname_edit"
             android:maxLength="25"
diff --git a/chrome/browser/autofill/android/java/res/layout/autofill_local_iban_editor.xml b/chrome/browser/autofill/android/java/res/layout/autofill_local_iban_editor.xml
index 0585be1e..508b1627 100644
--- a/chrome/browser/autofill/android/java/res/layout/autofill_local_iban_editor.xml
+++ b/chrome/browser/autofill/android/java/res/layout/autofill_local_iban_editor.xml
@@ -20,7 +20,7 @@
         android:layout_marginBottom="@dimen/pref_autofill_field_bottom_margin"
         app:errorTextAppearance="@style/TextAppearance.ErrorCaption">
 
-        <EditText
+        <org.chromium.chrome.browser.autofill.editors.EditTextNoAutofillView
             tools:ignore="Autofill,LabelFor"
             android:id="@+id/iban_value_edit"
             android:layout_width="match_parent"
@@ -44,7 +44,7 @@
         android:layout_marginTop="@dimen/pref_autofill_field_extra_large_top_margin"
         android:layout_marginBottom="@dimen/pref_autofill_field_bottom_margin">
 
-        <EditText
+        <org.chromium.chrome.browser.autofill.editors.EditTextNoAutofillView
             tools:ignore="Autofill,LabelFor"
             android:id="@+id/iban_nickname_edit"
             android:maxLength="25"
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
index d4e7338..e124550 100644
--- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
+++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
@@ -829,26 +829,7 @@
 
     public static String getBasicCardIssuerNetwork(String cardNumber, boolean emptyIfInvalid) {
         ThreadUtils.assertOnUiThread();
-        return PersonalDataManagerJni.get()
-                .getBasicCardIssuerNetwork(
-                        cardNumber,
-                        emptyIfInvalid);
-    }
-
-    public void addServerCreditCardForTest(CreditCard card) {
-        ThreadUtils.assertOnUiThread();
-        assert !card.getIsLocal();
-        PersonalDataManagerJni.get()
-                .addServerCreditCardForTest(mPersonalDataManagerAndroid, card); // IN-TEST
-    }
-
-    public void addServerCreditCardForTestWithAdditionalFields(
-            CreditCard card, String nickname, int cardIssuer) {
-        ThreadUtils.assertOnUiThread();
-        assert !card.getIsLocal();
-        PersonalDataManagerJni.get()
-                .addServerCreditCardForTestWithAdditionalFields(
-                        mPersonalDataManagerAndroid, card, nickname, cardIssuer);
+        return PersonalDataManagerJni.get().getBasicCardIssuerNetwork(cardNumber, emptyIfInvalid);
     }
 
     public void deleteCreditCard(String guid) {
@@ -920,12 +901,6 @@
         return PersonalDataManagerJni.get().getMaskedBankAccounts(mPersonalDataManagerAndroid);
     }
 
-    public void addMaskedBankAccountForTest(BankAccount bankAccount) {
-        ThreadUtils.assertOnUiThread();
-        PersonalDataManagerJni.get()
-                .addMaskedBankAccountForTest(mPersonalDataManagerAndroid, bankAccount);
-    }
-
     /**
      * Records the use of the profile associated with the specified {@code guid}. Effectively
      * increments the use count of the profile and sets its use date to the current time. Also logs
@@ -938,25 +913,6 @@
         PersonalDataManagerJni.get().recordAndLogProfileUse(mPersonalDataManagerAndroid, guid);
     }
 
-    protected void setProfileUseStatsForTesting(String guid, int count, int daysSinceLastUsed) {
-        ThreadUtils.assertOnUiThread();
-        PersonalDataManagerJni.get()
-                .setProfileUseStatsForTesting(
-                        mPersonalDataManagerAndroid, guid, count, daysSinceLastUsed);
-    }
-
-    int getProfileUseCountForTesting(String guid) {
-        ThreadUtils.assertOnUiThread();
-        return PersonalDataManagerJni.get()
-                .getProfileUseCountForTesting(mPersonalDataManagerAndroid, guid); // IN-TEST
-    }
-
-    long getProfileUseDateForTesting(String guid) {
-        ThreadUtils.assertOnUiThread();
-        return PersonalDataManagerJni.get()
-                .getProfileUseDateForTesting(mPersonalDataManagerAndroid, guid); // IN-TEST
-    }
-
     /**
      * Records the use of the credit card associated with the specified {@code guid}. Effectively
      * increments the use count of the credit card and set its use date to the current time. Also
@@ -969,44 +925,6 @@
         PersonalDataManagerJni.get().recordAndLogCreditCardUse(mPersonalDataManagerAndroid, guid);
     }
 
-    protected void setCreditCardUseStatsForTesting(String guid, int count, int daysSinceLastUsed) {
-        ThreadUtils.assertOnUiThread();
-        PersonalDataManagerJni.get()
-                .setCreditCardUseStatsForTesting(
-                        mPersonalDataManagerAndroid, guid, count, daysSinceLastUsed);
-    }
-
-    int getCreditCardUseCountForTesting(String guid) {
-        ThreadUtils.assertOnUiThread();
-        return PersonalDataManagerJni.get()
-                .getCreditCardUseCountForTesting(mPersonalDataManagerAndroid, guid); // IN-TEST
-    }
-
-    long getCreditCardUseDateForTesting(String guid) {
-        ThreadUtils.assertOnUiThread();
-        return PersonalDataManagerJni.get()
-                .getCreditCardUseDateForTesting(mPersonalDataManagerAndroid, guid); // IN-TEST
-    }
-
-    long getCurrentDateForTesting() {
-        ThreadUtils.assertOnUiThread();
-        return PersonalDataManagerJni.get()
-                .getCurrentDateForTesting(mPersonalDataManagerAndroid); // IN-TEST
-    }
-
-    long getDateNDaysAgoForTesting(int days) {
-        ThreadUtils.assertOnUiThread();
-        return PersonalDataManagerJni.get()
-                .getDateNDaysAgoForTesting( // IN-TEST
-                        mPersonalDataManagerAndroid, days);
-    }
-
-    protected void clearServerDataForTesting() {
-        ThreadUtils.assertOnUiThread();
-        PersonalDataManagerJni.get()
-                .clearServerDataForTesting(mPersonalDataManagerAndroid); // IN-TEST
-    }
-
     protected void clearImageDataForTesting() {
         if (mImageFetcher == null) {
             return;
@@ -1189,10 +1107,6 @@
                 .isAutofillCreditCardManaged(mPersonalDataManagerAndroid);
     }
 
-    public void setSyncServiceForTesting() {
-        PersonalDataManagerJni.get().setSyncServiceForTesting(mPersonalDataManagerAndroid);
-    }
-
     private void fetchCreditCardArtImages() {
         mImageFetcher.prefetchImages(
                 getCreditCardsToSuggest().stream()
@@ -1291,55 +1205,16 @@
 
         String setCreditCard(long nativePersonalDataManagerAndroid, CreditCard card);
 
-        long getDateNDaysAgoForTesting(long nativePersonalDataManagerAndroid, int days); // IN-TEST
-
         void updateServerCardBillingAddress(long nativePersonalDataManagerAndroid, CreditCard card);
 
         String getBasicCardIssuerNetwork(String cardNumber, boolean emptyIfInvalid);
 
-        void addServerCreditCardForTest(
-                long nativePersonalDataManagerAndroid, CreditCard card); // IN-TEST
-
-        void addServerCreditCardForTestWithAdditionalFields(
-                long nativePersonalDataManagerAndroid,
-                CreditCard card,
-                String nickname,
-                int cardIssuer);
-
         void removeByGUID(long nativePersonalDataManagerAndroid, String guid);
 
         void recordAndLogProfileUse(long nativePersonalDataManagerAndroid, String guid);
 
-        void setProfileUseStatsForTesting(
-                long nativePersonalDataManagerAndroid,
-                String guid,
-                int count,
-                int daysSinceLastUsed);
-
-        int getProfileUseCountForTesting(
-                long nativePersonalDataManagerAndroid, String guid); // IN-TEST
-
-        long getProfileUseDateForTesting(
-                long nativePersonalDataManagerAndroid, String guid); // IN-TEST
-
         void recordAndLogCreditCardUse(long nativePersonalDataManagerAndroid, String guid);
 
-        void setCreditCardUseStatsForTesting(
-                long nativePersonalDataManagerAndroid,
-                String guid,
-                int count,
-                int daysSinceLastUsed);
-
-        int getCreditCardUseCountForTesting(
-                long nativePersonalDataManagerAndroid, String guid); // IN-TEST
-
-        long getCreditCardUseDateForTesting(
-                long nativePersonalDataManagerAndroid, String guid); // IN-TEST
-
-        long getCurrentDateForTesting(long nativePersonalDataManagerAndroid); // IN-TEST
-
-        void clearServerDataForTesting(long nativePersonalDataManagerAndroid); // IN-TEST
-
         boolean hasProfiles(long nativePersonalDataManagerAndroid);
 
         boolean hasCreditCards(long nativePersonalDataManagerAndroid);
@@ -1354,8 +1229,6 @@
 
         String toCountryCode(String countryName);
 
-        void setSyncServiceForTesting(long nativePersonalDataManagerAndroid);
-
         AutofillImageFetcher getOrCreateJavaImageFetcher(long nativePersonalDataManagerAndroid);
 
         void addServerIbanForTest(long nativePersonalDataManagerAndroid, Iban iban); // IN-TEST
@@ -1369,8 +1242,5 @@
         boolean isValidIban(long nativePersonalDataManagerAndroid, String ibanValue);
 
         BankAccount[] getMaskedBankAccounts(long nativePersonalDataManagerAndroid);
-
-        void addMaskedBankAccountForTest(
-                long nativePersonalDataManagerAndroid, BankAccount bankAccount); // IN-TEST
     }
 }
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/EditTextNoAutofillView.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/EditTextNoAutofillView.java
new file mode 100644
index 0000000..4ff47d7
--- /dev/null
+++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/EditTextNoAutofillView.java
@@ -0,0 +1,24 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.autofill.editors;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.ViewStructure;
+import android.widget.EditText;
+
+/**
+ * A wrapper class around {@link EditText} to stop Android Autofill from suggesting cards. This is
+ * achieved by overriding the {@code onProvideAutofillStructure} method.
+ */
+public class EditTextNoAutofillView extends EditText {
+
+    public EditTextNoAutofillView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void onProvideAutofillStructure(ViewStructure structure, int flags) {}
+}
diff --git a/chrome/browser/autofill/android/personal_data_manager_android.cc b/chrome/browser/autofill/android/personal_data_manager_android.cc
index ccbfda9..bb391bf 100644
--- a/chrome/browser/autofill/android/personal_data_manager_android.cc
+++ b/chrome/browser/autofill/android/personal_data_manager_android.cc
@@ -390,32 +390,6 @@
       {card});
 }
 
-void PersonalDataManagerAndroid::AddServerCreditCardForTest(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jcard) {
-  std::unique_ptr<CreditCard> card = std::make_unique<CreditCard>();
-  PopulateNativeCreditCardFromJava(jcard, env, card.get());
-  card->set_record_type(CreditCard::RecordType::kMaskedServerCard);
-  personal_data_manager_->payments_data_manager().AddServerCreditCardForTest(
-      std::move(card));
-  personal_data_manager_->NotifyPersonalDataObserver();
-}
-
-void PersonalDataManagerAndroid::AddServerCreditCardForTestWithAdditionalFields(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jcard,
-    const base::android::JavaParamRef<jstring>& jnickname,
-    jint jcard_issuer) {
-  std::unique_ptr<CreditCard> card = std::make_unique<CreditCard>();
-  PopulateNativeCreditCardFromJava(jcard, env, card.get());
-  card->set_record_type(CreditCard::RecordType::kMaskedServerCard);
-  card->SetNickname(ConvertJavaStringToUTF16(jnickname));
-  card->set_card_issuer(static_cast<CreditCard::Issuer>(jcard_issuer));
-  personal_data_manager_->payments_data_manager().AddServerCreditCardForTest(
-      std::move(card));
-  personal_data_manager_->NotifyPersonalDataObserver();
-}
-
 void PersonalDataManagerAndroid::RemoveByGUID(
     JNIEnv* env,
     const JavaParamRef<jstring>& jguid) {
@@ -446,39 +420,6 @@
   }
 }
 
-void PersonalDataManagerAndroid::SetProfileUseStatsForTesting(
-    JNIEnv* env,
-    const JavaParamRef<jstring>& jguid,
-    jint count,
-    jint days_since_last_used) {
-  DCHECK(count >= 0 && days_since_last_used >= 0);
-
-  AutofillProfile profile =
-      *personal_data_manager_->address_data_manager().GetProfileByGUID(
-          ConvertJavaStringToUTF8(env, jguid));
-  profile.set_use_count(static_cast<size_t>(count));
-  profile.set_use_date(AutofillClock::Now() - base::Days(days_since_last_used));
-  personal_data_manager_->address_data_manager().UpdateProfile(profile);
-}
-
-jint PersonalDataManagerAndroid::GetProfileUseCountForTesting(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jstring>& jguid) {
-  const AutofillProfile* profile =
-      personal_data_manager_->address_data_manager().GetProfileByGUID(
-          ConvertJavaStringToUTF8(env, jguid));
-  return profile->use_count();
-}
-
-jlong PersonalDataManagerAndroid::GetProfileUseDateForTesting(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jstring>& jguid) {
-  const AutofillProfile* profile =
-      personal_data_manager_->address_data_manager().GetProfileByGUID(
-          ConvertJavaStringToUTF8(env, jguid));
-  return profile->use_date().ToTimeT();
-}
-
 void PersonalDataManagerAndroid::RecordAndLogCreditCardUse(
     JNIEnv* env,
     const JavaParamRef<jstring>& jguid) {
@@ -490,57 +431,6 @@
   }
 }
 
-void PersonalDataManagerAndroid::SetCreditCardUseStatsForTesting(
-    JNIEnv* env,
-    const JavaParamRef<jstring>& jguid,
-    jint count,
-    jint days_since_last_used) {
-  DCHECK(count >= 0 && days_since_last_used >= 0);
-
-  CreditCard* card =
-      personal_data_manager_->payments_data_manager().GetCreditCardByGUID(
-          ConvertJavaStringToUTF8(env, jguid));
-  card->set_use_count(static_cast<size_t>(count));
-  card->set_use_date(AutofillClock::Now() - base::Days(days_since_last_used));
-
-  personal_data_manager_->NotifyPersonalDataObserver();
-}
-
-jint PersonalDataManagerAndroid::GetCreditCardUseCountForTesting(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jstring>& jguid) {
-  const CreditCard* card =
-      personal_data_manager_->payments_data_manager().GetCreditCardByGUID(
-          ConvertJavaStringToUTF8(env, jguid));
-  return card->use_count();
-}
-
-jlong PersonalDataManagerAndroid::GetCreditCardUseDateForTesting(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jstring>& jguid) {
-  const CreditCard* card =
-      personal_data_manager_->payments_data_manager().GetCreditCardByGUID(
-          ConvertJavaStringToUTF8(env, jguid));
-  return card->use_date().ToTimeT();
-}
-
-// TODO(crbug.com/40477114): Use a mock clock for testing.
-jlong PersonalDataManagerAndroid::GetCurrentDateForTesting(JNIEnv* env) {
-  return base::Time::Now().ToTimeT();
-}
-
-jlong PersonalDataManagerAndroid::GetDateNDaysAgoForTesting(
-    JNIEnv* env,
-    jint days) {
-  return (AutofillClock::Now() - base::Days(days)).ToTimeT();
-}
-
-void PersonalDataManagerAndroid::ClearServerDataForTesting(JNIEnv* env) {
-  personal_data_manager_->payments_data_manager()
-      .ClearAllServerDataForTesting();  // IN-TEST
-  personal_data_manager_->NotifyPersonalDataObserver();
-}
-
 jboolean PersonalDataManagerAndroid::HasProfiles(JNIEnv* env) {
   return !personal_data_manager_->address_data_manager().GetProfiles().empty();
 }
@@ -562,11 +452,6 @@
   return IsCreditCardFidoAuthenticationEnabled();
 }
 
-void PersonalDataManagerAndroid::SetSyncServiceForTesting(JNIEnv* env) {
-  personal_data_manager_->payments_data_manager().SetSyncingForTest(
-      true);  // IN-TEST
-}
-
 base::android::ScopedJavaLocalRef<jobject>
 PersonalDataManagerAndroid::GetOrCreateJavaImageFetcher(JNIEnv* env) {
   return static_cast<AutofillImageFetcherImpl*>(
@@ -851,16 +736,6 @@
                                                   type.obj());
 }
 
-void PersonalDataManagerAndroid::AddMaskedBankAccountForTest(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& jbank_account) {
-  BankAccount bank_account =
-      CreateNativeBankAccountFromJava(env, jbank_account);
-  personal_data_manager_->payments_data_manager().AddMaskedBankAccountForTest(
-      bank_account);  // IN-TEST
-  personal_data_manager_->NotifyPersonalDataObserver();
-}
-
 jboolean PersonalDataManagerAndroid::IsAutofillManaged(JNIEnv* env) {
   return prefs::IsAutofillManaged(prefs_);
 }
diff --git a/chrome/browser/autofill/android/personal_data_manager_android.h b/chrome/browser/autofill/android/personal_data_manager_android.h
index 7d61499..10b744c 100644
--- a/chrome/browser/autofill/android/personal_data_manager_android.h
+++ b/chrome/browser/autofill/android/personal_data_manager_android.h
@@ -160,19 +160,6 @@
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& jcard);
 
-  // Adds a server credit card. Used only in tests.
-  void AddServerCreditCardForTest(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& jcard);
-
-  // Adds a server credit card and sets the additional fields, for example,
-  // card_issuer, nickname. Used only in tests.
-  void AddServerCreditCardForTestWithAdditionalFields(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& jcard,
-      const base::android::JavaParamRef<jstring>& jnickname,
-      jint jcard_issuer);
-
   // Removes the profile or credit card represented by |jguid|.
   void RemoveByGUID(JNIEnv* env,
                     const base::android::JavaParamRef<jstring>& jguid);
@@ -193,29 +180,6 @@
       JNIEnv* env,
       const base::android::JavaParamRef<jstring>& jguid);
 
-  // Sets the use count and number of days since last use of the profile
-  // associated to the `jguid`. Both `count` and `days_since_last_used` should
-  // be non-negative. `days_since_last_used` represents the numbers of days
-  // since the profile was last used.
-  void SetProfileUseStatsForTesting(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jstring>& jguid,
-      jint count,
-      jint days_since_last_used);
-
-  // Returns the use count of the profile associated to the |jguid|.
-  jint GetProfileUseCountForTesting(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jstring>& jguid);
-
-  // Returns the use date of the profile associated to the |jguid|. It
-  // represents an absolute point in coordinated universal time (UTC)
-  // represented as microseconds since the Windows epoch. For more details see
-  // the comment header in time.h.
-  jlong GetProfileUseDateForTesting(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jstring>& jguid);
-
   // Records the use and log usage metrics for the credit card associated with
   // the |jguid|. Increments the use count of the credit card and sets its use
   // date to the current time.
@@ -223,44 +187,6 @@
       JNIEnv* env,
       const base::android::JavaParamRef<jstring>& jguid);
 
-  // Sets the use count and number of days since last use of the credit card
-  // associated to the`jguid`. Both `count` and `days_since_last_used` should be
-  // non-negative. `days_since_last_used` represents the numbers of days since
-  // the card was last used.
-  void SetCreditCardUseStatsForTesting(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jstring>& jguid,
-      jint count,
-      jint days_since_last_used);
-
-  // Returns the use count of the credit card associated to the |jguid|.
-  jint GetCreditCardUseCountForTesting(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jstring>& jguid);
-
-  // Returns the use date of the credit card associated to the |jguid|. It
-  // represents an absolute point in coordinated universal time (UTC)
-  // represented as microseconds since the Windows epoch. For more details see
-  // the comment header in time.h.
-  jlong GetCreditCardUseDateForTesting(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jstring>& jguid);
-
-  // Returns the current date represented as an absolute point in coordinated
-  // universal time (UTC) represented as microseconds since the Unix epoch. For
-  // more details see the comment header in time.h
-  jlong GetCurrentDateForTesting(JNIEnv* env);
-
-  // Calculates a point in time `days` days ago from the current
-  // time. Returns the result as an absolute point in coordinated universal time
-  // (UTC) represented as microseconds since the Windows epoch.
-  jlong GetDateNDaysAgoForTesting(
-      JNIEnv* env,
-      jint days);
-
-  // Clears server profiles and cards, to be used in tests only.
-  void ClearServerDataForTesting(JNIEnv* env);
-
   // Checks whether the Autofill PersonalDataManager has profiles.
   jboolean HasProfiles(JNIEnv* env);
 
@@ -270,8 +196,6 @@
   // Checks whether FIDO authentication is available.
   jboolean IsFidoAuthenticationAvailable(JNIEnv* env);
 
-  void SetSyncServiceForTesting(JNIEnv* env);
-
   // Get Java AutofillImageFetcher.
   base::android::ScopedJavaLocalRef<jobject> GetOrCreateJavaImageFetcher(
       JNIEnv* env);
@@ -325,24 +249,18 @@
   base::android::ScopedJavaLocalRef<jobjectArray> GetMaskedBankAccounts(
       JNIEnv* env);
 
-  // Add a BankAccount object to the existing list of BankAccounts stored in
-  // PersonalDataManager.
-  void AddMaskedBankAccountForTest(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& jbank_account);
-
   // Create an object of Java BankAccount from native BankAccount.
   static base::android::ScopedJavaLocalRef<jobject>
   CreateJavaBankAccountFromNative(JNIEnv* env, const BankAccount& bank_account);
 
- private:
-  ~PersonalDataManagerAndroid() override;
-
   // Create an object of native BankAccount from Java BankAccount.
   static BankAccount CreateNativeBankAccountFromJava(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& jbank_account);
 
+ private:
+  ~PersonalDataManagerAndroid() override;
+
   // Returns the GUIDs of the |profiles| passed as parameter.
   base::android::ScopedJavaLocalRef<jobjectArray> GetProfileGUIDs(
       JNIEnv* env,
diff --git a/chrome/browser/autofill/test/BUILD.gn b/chrome/browser/autofill/test/BUILD.gn
index f01aee0..b3f7b40 100644
--- a/chrome/browser/autofill/test/BUILD.gn
+++ b/chrome/browser/autofill/test/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/android/rules.gni")
+import("//third_party/jni_zero/jni_zero.gni")
 
 android_library("test_java") {
   testonly = true
@@ -44,6 +45,7 @@
     "//content/public/android:content_java",
     "//third_party/android_deps:espresso_java",
     "//third_party/hamcrest:hamcrest_java",
+    "//third_party/jni_zero:jni_zero_java",
     "//url:gurl_java",
   ]
 
@@ -53,4 +55,24 @@
     "//components/autofill/android:main_autofill_java",
     "//content/public/test/android:content_java_test_support",
   ]
+
+  srcjar_deps = [ ":jni_headers" ]
+}
+
+generate_jni("jni_headers") {
+  testonly = true
+  visibility = [ ":*" ]
+  sources = [ "android/java/src/org/chromium/chrome/browser/autofill/AutofillTestHelper.java" ]
+}
+
+static_library("test_support") {
+  testonly = true
+  sources = [ "android/autofill_test_helper_android.cc" ]
+  deps = [
+    ":jni_headers",
+    "//base",
+    "//chrome/browser",
+    "//chrome/browser/autofill",
+    "//components/autofill/core/browser",
+  ]
 }
diff --git a/chrome/browser/autofill/test/android/autofill_test_helper_android.cc b/chrome/browser/autofill/test/android/autofill_test_helper_android.cc
new file mode 100644
index 0000000..89ea118
--- /dev/null
+++ b/chrome/browser/autofill/test/android/autofill_test_helper_android.cc
@@ -0,0 +1,192 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <jni.h>
+
+#include <memory>
+
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "chrome/browser/autofill/android/personal_data_manager_android.h"
+#include "chrome/browser/autofill/personal_data_manager_factory.h"
+#include "chrome/browser/autofill/test/jni_headers/AutofillTestHelper_jni.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "components/autofill/core/browser/data_model/bank_account.h"
+#include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/common/autofill_clock.h"
+
+namespace autofill {
+namespace {
+
+using ::base::android::ConvertJavaStringToUTF16;
+using ::base::android::ConvertJavaStringToUTF8;
+using ::base::android::JavaParamRef;
+
+PersonalDataManager* GetPersonalDataManagerForLastUsedProfile() {
+  return PersonalDataManagerFactory::GetForBrowserContext(
+      ProfileManager::GetLastUsedProfile());
+}
+
+}  // anonymous namespace
+
+// static
+jlong JNI_AutofillTestHelper_GetDateNDaysAgo(JNIEnv* env, jint days) {
+  return (AutofillClock::Now() - base::Days(days)).ToTimeT();
+}
+
+// static
+void JNI_AutofillTestHelper_AddServerCreditCard(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& jcard) {
+  std::unique_ptr<CreditCard> card = std::make_unique<CreditCard>();
+  PersonalDataManagerAndroid::PopulateNativeCreditCardFromJava(jcard, env,
+                                                               card.get());
+  card->set_record_type(CreditCard::RecordType::kMaskedServerCard);
+  PersonalDataManager* personal_data_manager =
+      GetPersonalDataManagerForLastUsedProfile();
+  personal_data_manager->payments_data_manager().AddServerCreditCardForTest(
+      std::move(card));
+  personal_data_manager->NotifyPersonalDataObserver();
+}
+
+// static
+void JNI_AutofillTestHelper_AddServerCreditCardWithAdditionalFields(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& jcard,
+    const base::android::JavaParamRef<jstring>& jnickname,
+    jint jcard_issuer) {
+  std::unique_ptr<CreditCard> card = std::make_unique<CreditCard>();
+  PersonalDataManagerAndroid::PopulateNativeCreditCardFromJava(jcard, env,
+                                                               card.get());
+  card->set_record_type(CreditCard::RecordType::kMaskedServerCard);
+  card->SetNickname(ConvertJavaStringToUTF16(jnickname));
+  card->set_card_issuer(static_cast<CreditCard::Issuer>(jcard_issuer));
+  PersonalDataManager* personal_data_manager =
+      GetPersonalDataManagerForLastUsedProfile();
+  personal_data_manager->payments_data_manager().AddServerCreditCardForTest(
+      std::move(card));
+  personal_data_manager->NotifyPersonalDataObserver();
+}
+
+// static
+void JNI_AutofillTestHelper_SetProfileUseStats(
+    JNIEnv* env,
+    const JavaParamRef<jstring>& jguid,
+    jint count,
+    jint days_since_last_used) {
+  DCHECK(count >= 0 && days_since_last_used >= 0);
+
+  PersonalDataManager* personal_data_manager =
+      GetPersonalDataManagerForLastUsedProfile();
+  AutofillProfile profile =
+      *personal_data_manager->address_data_manager().GetProfileByGUID(
+          ConvertJavaStringToUTF8(env, jguid));
+  profile.set_use_count(static_cast<size_t>(count));
+  profile.set_use_date(AutofillClock::Now() - base::Days(days_since_last_used));
+  personal_data_manager->address_data_manager().UpdateProfile(profile);
+}
+
+// static
+jint JNI_AutofillTestHelper_GetProfileUseCount(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jstring>& jguid) {
+  PersonalDataManager* personal_data_manager =
+      GetPersonalDataManagerForLastUsedProfile();
+  const AutofillProfile* profile =
+      personal_data_manager->address_data_manager().GetProfileByGUID(
+          ConvertJavaStringToUTF8(env, jguid));
+  return profile->use_count();
+}
+
+// static
+jlong JNI_AutofillTestHelper_GetProfileUseDate(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jstring>& jguid) {
+  PersonalDataManager* personal_data_manager =
+      GetPersonalDataManagerForLastUsedProfile();
+  const AutofillProfile* profile =
+      personal_data_manager->address_data_manager().GetProfileByGUID(
+          ConvertJavaStringToUTF8(env, jguid));
+  return profile->use_date().ToTimeT();
+}
+
+// static
+void JNI_AutofillTestHelper_SetCreditCardUseStats(
+    JNIEnv* env,
+    const JavaParamRef<jstring>& jguid,
+    jint count,
+    jint days_since_last_used) {
+  DCHECK(count >= 0 && days_since_last_used >= 0);
+
+  PersonalDataManager* personal_data_manager =
+      GetPersonalDataManagerForLastUsedProfile();
+  CreditCard* card =
+      personal_data_manager->payments_data_manager().GetCreditCardByGUID(
+          ConvertJavaStringToUTF8(env, jguid));
+  card->set_use_count(static_cast<size_t>(count));
+  card->set_use_date(AutofillClock::Now() - base::Days(days_since_last_used));
+
+  personal_data_manager->NotifyPersonalDataObserver();
+}
+
+// static
+jint JNI_AutofillTestHelper_GetCreditCardUseCount(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jstring>& jguid) {
+  PersonalDataManager* personal_data_manager =
+      GetPersonalDataManagerForLastUsedProfile();
+  const CreditCard* card =
+      personal_data_manager->payments_data_manager().GetCreditCardByGUID(
+          ConvertJavaStringToUTF8(env, jguid));
+  return card->use_count();
+}
+
+// static
+jlong JNI_AutofillTestHelper_GetCreditCardUseDate(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jstring>& jguid) {
+  PersonalDataManager* personal_data_manager =
+      GetPersonalDataManagerForLastUsedProfile();
+  const CreditCard* card =
+      personal_data_manager->payments_data_manager().GetCreditCardByGUID(
+          ConvertJavaStringToUTF8(env, jguid));
+  return card->use_date().ToTimeT();
+}
+
+// TODO(crbug.com/40477114): Use a mock clock for testing.
+jlong JNI_AutofillTestHelper_GetCurrentDate(JNIEnv* env) {
+  return base::Time::Now().ToTimeT();
+}
+
+// static
+void JNI_AutofillTestHelper_ClearServerData(JNIEnv* env) {
+  PersonalDataManager* personal_data_manager =
+      GetPersonalDataManagerForLastUsedProfile();
+  personal_data_manager->payments_data_manager().ClearAllServerDataForTesting();
+  personal_data_manager->NotifyPersonalDataObserver();
+}
+
+// static
+void JNI_AutofillTestHelper_SetSyncService(JNIEnv* env) {
+  GetPersonalDataManagerForLastUsedProfile()
+      ->payments_data_manager()
+      .SetSyncingForTest(true);
+}
+
+// static
+void JNI_AutofillTestHelper_AddMaskedBankAccount(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& jbank_account) {
+  BankAccount bank_account =
+      PersonalDataManagerAndroid::CreateNativeBankAccountFromJava(
+          env, jbank_account);
+  PersonalDataManager* personal_data_manager =
+      GetPersonalDataManagerForLastUsedProfile();
+  personal_data_manager->payments_data_manager().AddMaskedBankAccountForTest(
+      bank_account);
+  personal_data_manager->NotifyPersonalDataObserver();
+}
+
+}  // namespace autofill
diff --git a/chrome/browser/autofill/test/android/java/src/org/chromium/chrome/browser/autofill/AutofillTestHelper.java b/chrome/browser/autofill/test/android/java/src/org/chromium/chrome/browser/autofill/AutofillTestHelper.java
index 984725c3..1ab3dd6 100644
--- a/chrome/browser/autofill/test/android/java/src/org/chromium/chrome/browser/autofill/AutofillTestHelper.java
+++ b/chrome/browser/autofill/test/android/java/src/org/chromium/chrome/browser/autofill/AutofillTestHelper.java
@@ -19,6 +19,8 @@
 import androidx.test.espresso.util.HumanReadables;
 
 import org.hamcrest.Matcher;
+import org.jni_zero.JNINamespace;
+import org.jni_zero.NativeMethods;
 
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
@@ -39,6 +41,7 @@
 import java.util.concurrent.TimeoutException;
 
 /** Helper class for testing AutofillProfiles. */
+@JNINamespace("autofill")
 public class AutofillTestHelper {
     private final CallbackHelper mOnPersonalDataChangedHelper = new CallbackHelper();
 
@@ -68,8 +71,7 @@
     }
 
     void setSyncServiceForTesting() {
-        runOnUiThreadBlocking(
-                () -> getPersonalDataManagerForLastUsedProfile().setSyncServiceForTesting());
+        runOnUiThreadBlocking(() -> AutofillTestHelperJni.get().setSyncService());
     }
 
     AutofillProfile getProfile(final String guid) {
@@ -146,8 +148,7 @@
 
     public void addServerCreditCard(final CreditCard card) throws TimeoutException {
         int callCount = mOnPersonalDataChangedHelper.getCallCount();
-        runOnUiThreadBlocking(
-                () -> getPersonalDataManagerForLastUsedProfile().addServerCreditCardForTest(card));
+        runOnUiThreadBlocking(() -> AutofillTestHelperJni.get().addServerCreditCard(card));
         mOnPersonalDataChangedHelper.waitForCallback(callCount);
     }
 
@@ -156,8 +157,8 @@
         int callCount = mOnPersonalDataChangedHelper.getCallCount();
         runOnUiThreadBlocking(
                 () ->
-                        getPersonalDataManagerForLastUsedProfile()
-                                .addServerCreditCardForTestWithAdditionalFields(
+                        AutofillTestHelperJni.get()
+                                .addServerCreditCardWithAdditionalFields(
                                         card, nickname, cardIssuer));
         mOnPersonalDataChangedHelper.waitForCallback(callCount);
     }
@@ -197,8 +198,8 @@
         int callCount = mOnPersonalDataChangedHelper.getCallCount();
         runOnUiThreadBlocking(
                 () ->
-                        getPersonalDataManagerForLastUsedProfile()
-                                .setProfileUseStatsForTesting(guid, count, daysSinceLastUsed));
+                        AutofillTestHelperJni.get()
+                                .setProfileUseStats(guid, count, daysSinceLastUsed));
         mOnPersonalDataChangedHelper.waitForCallback(callCount);
     }
 
@@ -209,10 +210,7 @@
      * @return The non-negative use count of the profile.
      */
     public int getProfileUseCountForTesting(final String guid) {
-        return runOnUiThreadBlocking(
-                () ->
-                        getPersonalDataManagerForLastUsedProfile()
-                                .getProfileUseCountForTesting(guid));
+        return runOnUiThreadBlocking(() -> AutofillTestHelperJni.get().getProfileUseCount(guid));
     }
 
     /**
@@ -224,8 +222,7 @@
      *     Windows epoch. For more details see the comment header in time.h.
      */
     public long getProfileUseDateForTesting(final String guid) {
-        return runOnUiThreadBlocking(
-                () -> getPersonalDataManagerForLastUsedProfile().getProfileUseDateForTesting(guid));
+        return runOnUiThreadBlocking(() -> AutofillTestHelperJni.get().getProfileUseDate(guid));
     }
 
     /**
@@ -256,8 +253,8 @@
         int callCount = mOnPersonalDataChangedHelper.getCallCount();
         runOnUiThreadBlocking(
                 () ->
-                        getPersonalDataManagerForLastUsedProfile()
-                                .setCreditCardUseStatsForTesting(guid, count, daysSinceLastUsed));
+                        AutofillTestHelperJni.get()
+                                .setCreditCardUseStats(guid, count, daysSinceLastUsed));
         mOnPersonalDataChangedHelper.waitForCallback(callCount);
     }
 
@@ -268,10 +265,7 @@
      * @return The non-negative use count of the credit card.
      */
     public int getCreditCardUseCountForTesting(final String guid) {
-        return runOnUiThreadBlocking(
-                () ->
-                        getPersonalDataManagerForLastUsedProfile()
-                                .getCreditCardUseCountForTesting(guid));
+        return runOnUiThreadBlocking(() -> AutofillTestHelperJni.get().getCreditCardUseCount(guid));
     }
 
     /**
@@ -283,10 +277,7 @@
      *     the Windows epoch. For more details see the comment header in time.h.
      */
     public long getCreditCardUseDateForTesting(final String guid) {
-        return runOnUiThreadBlocking(
-                () ->
-                        getPersonalDataManagerForLastUsedProfile()
-                                .getCreditCardUseDateForTesting(guid));
+        return runOnUiThreadBlocking(() -> AutofillTestHelperJni.get().getCreditCardUseDate(guid));
     }
 
     /**
@@ -297,8 +288,7 @@
      *     more details see the comment header in time.h.
      */
     public long getCurrentDateForTesting() {
-        return runOnUiThreadBlocking(
-                () -> getPersonalDataManagerForLastUsedProfile().getCurrentDateForTesting());
+        return runOnUiThreadBlocking(() -> AutofillTestHelperJni.get().getCurrentDate());
     }
 
     /**
@@ -310,8 +300,7 @@
      *     For more details see the comment header in time.h.
      */
     public long getDateNDaysAgoForTesting(final int days) {
-        return runOnUiThreadBlocking(
-                () -> getPersonalDataManagerForLastUsedProfile().getDateNDaysAgoForTesting(days));
+        return runOnUiThreadBlocking(() -> AutofillTestHelperJni.get().getDateNDaysAgo(days));
     }
 
     public void addServerIban(final Iban iban) throws TimeoutException {
@@ -347,8 +336,7 @@
      * #addServerCreditCard(CreditCard)}}.
      */
     public void clearAllDataForTesting() throws TimeoutException {
-        runOnUiThreadBlocking(
-                () -> getPersonalDataManagerForLastUsedProfile().clearServerDataForTesting());
+        runOnUiThreadBlocking(() -> AutofillTestHelperJni.get().clearServerData());
         runOnUiThreadBlocking(
                 () -> getPersonalDataManagerForLastUsedProfile().clearImageDataForTesting());
         // Clear remaining local profiles and cards.
@@ -489,10 +477,7 @@
     }
 
     public static void addMaskedBankAccount(BankAccount bankAccount) {
-        runOnUiThreadBlocking(
-                () ->
-                        getPersonalDataManagerForLastUsedProfile()
-                                .addMaskedBankAccountForTest(bankAccount));
+        runOnUiThreadBlocking(() -> AutofillTestHelperJni.get().addMaskedBankAccount(bankAccount));
     }
 
     private void registerDataObserver() {
@@ -588,4 +573,34 @@
                 /* source= */ InputDevice.SOURCE_CLASS_POINTER,
                 /* flags= */ flags);
     }
+
+    @NativeMethods
+    interface Natives {
+        long getDateNDaysAgo(int days);
+
+        void addServerCreditCard(CreditCard card);
+
+        void addServerCreditCardWithAdditionalFields(
+                CreditCard card, String nickname, int cardIssuer);
+
+        void setProfileUseStats(String guid, int count, int daysSinceLastUsed);
+
+        int getProfileUseCount(String guid);
+
+        long getProfileUseDate(String guid);
+
+        void setCreditCardUseStats(String guid, int count, int daysSinceLastUsed);
+
+        int getCreditCardUseCount(String guid);
+
+        long getCreditCardUseDate(String guid);
+
+        long getCurrentDate();
+
+        void clearServerData();
+
+        void setSyncService();
+
+        void addMaskedBankAccount(BankAccount bankAccount);
+    }
 }
diff --git a/chrome/browser/commerce/coupons/android/BUILD.gn b/chrome/browser/commerce/coupons/android/BUILD.gn
index 816ac8c43..9c0efc7 100644
--- a/chrome/browser/commerce/coupons/android/BUILD.gn
+++ b/chrome/browser/commerce/coupons/android/BUILD.gn
@@ -36,8 +36,12 @@
 
 android_resources("java_resources") {
   sources = [
+    "java/res/drawable/discount_code_background.xml",
+    "java/res/drawable/discount_item_container_background.xml",
     "java/res/layout/discount_item_container.xml",
     "java/res/layout/discounts_content_container.xml",
+    "java/res/values/dimens.xml",
+    "java/res/values/styles.xml",
   ]
 
   deps = [
diff --git a/chrome/browser/commerce/coupons/android/java/res/drawable/discount_code_background.xml b/chrome/browser/commerce/coupons/android/java/res/drawable/discount_code_background.xml
new file mode 100644
index 0000000..1c412eb
--- /dev/null
+++ b/chrome/browser/commerce/coupons/android/java/res/drawable/discount_code_background.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2023 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <corners android:radius="@dimen/discount_code_background_radius"/>
+    <solid android:color="@color/price_drop_annotation_bg_color"/>
+</shape>
\ No newline at end of file
diff --git a/chrome/browser/commerce/coupons/android/java/res/drawable/discount_item_container_background.xml b/chrome/browser/commerce/coupons/android/java/res/drawable/discount_item_container_background.xml
new file mode 100644
index 0000000..832b062
--- /dev/null
+++ b/chrome/browser/commerce/coupons/android/java/res/drawable/discount_item_container_background.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2024 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@macro/default_bg_color"/>
+    <corners android:radius="@dimen/discount_item_container_background_radius" />
+</shape>
\ No newline at end of file
diff --git a/chrome/browser/commerce/coupons/android/java/res/layout/discount_item_container.xml b/chrome/browser/commerce/coupons/android/java/res/layout/discount_item_container.xml
index f108f8cf..0a95350 100644
--- a/chrome/browser/commerce/coupons/android/java/res/layout/discount_item_container.xml
+++ b/chrome/browser/commerce/coupons/android/java/res/layout/discount_item_container.xml
@@ -9,7 +9,9 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height= "wrap_content"
-    android:orientation="horizontal">
+    android:orientation="horizontal"
+    android:padding="@dimen/discount_item_container_padding"
+    android:background="@drawable/discount_item_container_background">
 
     <!-- Discount content layout -->
     <LinearLayout
@@ -20,13 +22,20 @@
         <TextView
                 android:id="@+id/discount_code"
                 android:layout_width="wrap_content"
-                android:layout_height="wrap_content"/>
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="@dimen/discount_code_bottom_margin"
+                android:paddingHorizontal="@dimen/discount_code_horizontal_padding"
+                android:paddingVertical="@dimen/discount_code_vertical_padding"
+                android:textAppearance="@style/TextAppearance.DiscountCodeText"
+                android:background="@drawable/discount_code_background"/>
         <TextView
                 android:id="@+id/description_detail"
+                android:textAppearance="@style/TextAppearance.TextMedium.Primary"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"/>
         <TextView
                 android:id="@+id/expiry_time"
+                android:textAppearance="@style/TextAppearance.TextSmall.Secondary"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"/>
     </LinearLayout>
diff --git a/chrome/browser/commerce/coupons/android/java/res/values/dimens.xml b/chrome/browser/commerce/coupons/android/java/res/values/dimens.xml
new file mode 100644
index 0000000..cd95d1a4
--- /dev/null
+++ b/chrome/browser/commerce/coupons/android/java/res/values/dimens.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2024 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <dimen name="discount_item_container_background_radius">16dp</dimen>
+    <dimen name="discount_item_container_padding">12dp</dimen>
+    <dimen name="discount_code_background_radius">4dp</dimen>
+    <dimen name="discount_code_bottom_margin">4dp</dimen>
+    <dimen name="discount_code_horizontal_padding">8dp</dimen>
+    <dimen name="discount_code_vertical_padding">2dp</dimen>
+    <dimen name="discount_item_divider_height">2dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/chrome/browser/commerce/coupons/android/java/res/values/styles.xml b/chrome/browser/commerce/coupons/android/java/res/values/styles.xml
new file mode 100644
index 0000000..1cb3c64e
--- /dev/null
+++ b/chrome/browser/commerce/coupons/android/java/res/values/styles.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2014 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <style name="TextAppearance.DiscountCodeText" parent="TextAppearance.TextSmall.Primary">
+        <item name="android:textColor">@color/price_drop_annotation_text_green</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/chrome/browser/commerce/coupons/android/java/src/org/chromium/chrome/browser/commerce/coupons/DiscountsBottomSheetContentCoordinator.java b/chrome/browser/commerce/coupons/android/java/src/org/chromium/chrome/browser/commerce/coupons/DiscountsBottomSheetContentCoordinator.java
index fa6e826..46b65b0a 100644
--- a/chrome/browser/commerce/coupons/android/java/src/org/chromium/chrome/browser/commerce/coupons/DiscountsBottomSheetContentCoordinator.java
+++ b/chrome/browser/commerce/coupons/android/java/src/org/chromium/chrome/browser/commerce/coupons/DiscountsBottomSheetContentCoordinator.java
@@ -11,11 +11,14 @@
 import static org.chromium.chrome.browser.commerce.CommerceBottomSheetContentProperties.TYPE;
 
 import android.content.Context;
+import android.graphics.Rect;
 import android.view.LayoutInflater;
 import android.view.View;
 
 import androidx.annotation.NonNull;
 import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.ItemDecoration;
+import androidx.recyclerview.widget.RecyclerView.State;
 
 import org.chromium.base.Callback;
 import org.chromium.base.supplier.Supplier;
@@ -51,6 +54,23 @@
         mContentRecyclerView =
                 mDiscountsContentContainer.findViewById(R.id.discounts_content_recycler_view);
         mContentRecyclerView.setAdapter(adapter);
+        mContentRecyclerView.addItemDecoration(
+                new ItemDecoration() {
+                    @Override
+                    public void getItemOffsets(
+                            @NonNull Rect outRect,
+                            @NonNull View view,
+                            @NonNull RecyclerView parent,
+                            @NonNull State state) {
+                        // Avoid adding top padding to the first item in the list.
+                        if (parent.getChildAdapterPosition(view) != 0) {
+                            outRect.top =
+                                    mContext.getResources()
+                                            .getDimensionPixelOffset(
+                                                    R.dimen.discount_item_divider_height);
+                        }
+                    }
+                });
 
         mMediator = new DiscountsBottomSheetContentMediator(context, tabSupplier, mModelList);
     }
diff --git a/chrome/browser/commerce/coupons/android/javatests/src/org/chromium/chrome/browser/commerce/coupons/DiscountsBottomSheetContentRenderTest.java b/chrome/browser/commerce/coupons/android/javatests/src/org/chromium/chrome/browser/commerce/coupons/DiscountsBottomSheetContentRenderTest.java
index 52d2e32f..45fc8d3 100644
--- a/chrome/browser/commerce/coupons/android/javatests/src/org/chromium/chrome/browser/commerce/coupons/DiscountsBottomSheetContentRenderTest.java
+++ b/chrome/browser/commerce/coupons/android/javatests/src/org/chromium/chrome/browser/commerce/coupons/DiscountsBottomSheetContentRenderTest.java
@@ -44,7 +44,10 @@
 public class DiscountsBottomSheetContentRenderTest extends BlankUiTestActivityTestCase {
     @Rule
     public RenderTestRule mRenderTestRule =
-            RenderTestRule.Builder.withPublicCorpus().setBugComponent(UI_BROWSER_SHOPPING).build();
+            RenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(1)
+                    .setBugComponent(UI_BROWSER_SHOPPING)
+                    .build();
 
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
diff --git a/chrome/browser/download/android/download_controller.cc b/chrome/browser/download/android/download_controller.cc
index d4932162..ebeb740 100644
--- a/chrome/browser/download/android/download_controller.cc
+++ b/chrome/browser/download/android/download_controller.cc
@@ -24,7 +24,6 @@
 #include "chrome/browser/android/profile_key_startup_accessor.h"
 #include "chrome/browser/android/profile_key_util.h"
 #include "chrome/browser/android/tab_android.h"
-#include "chrome/browser/browser_process.h"
 #include "chrome/browser/download/android/dangerous_download_infobar_delegate.h"
 #include "chrome/browser/download/android/download_manager_service.h"
 #include "chrome/browser/download/android/download_utils.h"
@@ -37,7 +36,6 @@
 #include "chrome/browser/offline_pages/android/offline_page_bridge.h"
 #include "chrome/browser/permissions/permission_update_message_controller_android.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/ui/android/tab_model/tab_model.h"
 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
 #include "chrome/common/pref_names.h"
@@ -49,9 +47,6 @@
 #include "components/pdf/common/constants.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
-#include "components/safe_browsing/android/safe_browsing_api_handler_bridge.h"
-#include "components/safe_browsing/core/browser/db/database_manager.h"
-#include "components/safe_browsing/core/browser/db/v4_protocol_manager_util.h"
 #include "components/safe_browsing/core/common/features.h"
 #include "components/strings/grit/components_strings.h"
 #include "content/public/browser/browser_context.h"
@@ -190,64 +185,6 @@
          !item->IsMustDownload() && item->IsTransient();
 }
 
-class DownloadBlocklistChecker
-    : public safe_browsing::SafeBrowsingDatabaseManager::Client,
-      public base::RefCounted<DownloadBlocklistChecker> {
- public:
-  explicit DownloadBlocklistChecker(download::DownloadItem* item)
-      : url_chain_(item->GetUrlChain()) {}
-
-  void Start() {
-    scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> database_manager;
-    if (g_browser_process->safe_browsing_service()) {
-      database_manager =
-          g_browser_process->safe_browsing_service()->database_manager();
-    }
-
-    if (!database_manager ||
-        database_manager->CheckDownloadUrl(url_chain_, this)) {
-      Log(safe_browsing::SBThreatType::SB_THREAT_TYPE_SAFE);
-    } else {
-      // Add a reference to this object to prevent it from being destroyed
-      // before url checking result is returned.
-      AddRef();
-    }
-  }
-
- private:
-  friend class base::RefCounted<DownloadBlocklistChecker>;
-
-  ~DownloadBlocklistChecker() override = default;
-
-  void Log(safe_browsing::SBThreatType threat_type) {
-    base::UmaHistogramEnumeration(
-        "SafeBrowsing.AndroidTelemetry.DownloadUrlChainThreatType",
-        threat_type);
-  }
-
-  // SafeBrowsingDatabaseManager::Client:
-  void OnCheckDownloadUrlResult(
-      const std::vector<GURL>& url_chain,
-      safe_browsing::SBThreatType threat_type) override {
-    Log(threat_type);
-    Release();  // Balanced by AddRef in Start.
-  }
-
-  std::vector<GURL> url_chain_;
-};
-
-void RecordDownloadBlocklistState(download::DownloadItem* item) {
-  // Startup in Chrome minimal mode may start a download before
-  // initializing the UI thread.
-  if (!content::BrowserThread::IsThreadInitialized(
-          content::BrowserThread::UI)) {
-    return;
-  }
-
-  auto checker = base::MakeRefCounted<DownloadBlocklistChecker>(item);
-  checker->Start();
-}
-
 void CleanupAppVerificationTimestamps(download::DownloadItem* item) {
   Profile* profile = Profile::FromBrowserContext(
       content::DownloadItemUtils::GetBrowserContext(item));
@@ -505,8 +442,6 @@
 }
 
 void DownloadController::OnDownloadStarted(DownloadItem* download_item) {
-  RecordDownloadBlocklistState(download_item);
-
   // For dangerous downloads, we need to show the dangerous infobar before the
   // download can start.
   if (!download_item->IsDangerous() &&
diff --git a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/interstitial/DownloadInterstitialProperties.java b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/interstitial/DownloadInterstitialProperties.java
index 0e24e53f..b09f4e7 100644
--- a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/interstitial/DownloadInterstitialProperties.java
+++ b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/interstitial/DownloadInterstitialProperties.java
@@ -15,10 +15,14 @@
 import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
 /**
- * Extends the properties defined in {@link ListProperties} to facilitate the logic for an entire
- * UI containing a download ListItem.
+ * Extends the properties defined in {@link ListProperties} to facilitate the logic for an entire UI
+ * containing a download ListItem.
  */
 interface DownloadInterstitialProperties extends ListProperties {
+    /**
+     * Keeps track of the state of the DownloadInterstitial. This may be different to the state of
+     * the offline item displayed within the UI.
+     */
     @IntDef({
         State.UNKNOWN,
         State.IN_PROGRESS,
@@ -27,10 +31,6 @@
         State.PAUSED,
         State.PENDING
     })
-    /**
-     * Keeps track of the state of the DownloadInterstitial. This may be different to the state of
-     * the offline item displayed within the UI.
-     */
     @interface State {
         int UNKNOWN = 0;
         int IN_PROGRESS = 1;
diff --git a/chrome/browser/extensions/api/debugger/debugger_api.cc b/chrome/browser/extensions/api/debugger/debugger_api.cc
index 5266fa3..7c3f912 100644
--- a/chrome/browser/extensions/api/debugger/debugger_api.cc
+++ b/chrome/browser/extensions/api/debugger/debugger_api.cc
@@ -58,6 +58,7 @@
 #include "extensions/common/constants.h"
 #include "extensions/common/error_utils.h"
 #include "extensions/common/extension.h"
+#include "extensions/common/extension_features.h"
 #include "extensions/common/extension_id.h"
 #include "extensions/common/manifest_constants.h"
 #include "extensions/common/permissions/permissions_data.h"
@@ -472,6 +473,11 @@
     return true;
   }
 
+  if (base::FeatureList::IsEnabled(
+          extensions_features::kSilentDebuggerExtensionAPI)) {
+    return true;
+  }
+
   // We allow policy-installed extensions to circumvent the normal
   // infobar warning. See crbug.com/693621.
   if (Manifest::IsPolicyLocation(extension_->location()))
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedMainMenuItemTest.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedMainMenuItemTest.java
index e38397e..489231d 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedMainMenuItemTest.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedMainMenuItemTest.java
@@ -201,12 +201,8 @@
         assertTrue(intent.hasExtra(CreatorIntentConstants.CREATOR_URL));
         assertNotNull(intent.getExtras().getString(CreatorIntentConstants.CREATOR_URL));
         assertTrue(intent.hasExtra(CreatorIntentConstants.CREATOR_ENTRY_POINT));
-        assertNotNull(intent.getExtras().getInt(CreatorIntentConstants.CREATOR_ENTRY_POINT));
         assertTrue(intent.hasExtra(CreatorIntentConstants.CREATOR_FOLLOWING));
-        assertNotNull(
-                intent.getExtras().getBoolean(CreatorIntentConstants.CREATOR_FOLLOWING, false));
         assertTrue(intent.hasExtra(CreatorIntentConstants.CREATOR_TAB_ID));
-        assertNotNull(intent.getExtras().getInt(CreatorIntentConstants.CREATOR_TAB_ID));
     }
 
     @Test
diff --git a/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/DrawableButtonDataUnitTest.java b/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/DrawableButtonDataUnitTest.java
index 919aae4a..56e50fb 100644
--- a/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/DrawableButtonDataUnitTest.java
+++ b/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/DrawableButtonDataUnitTest.java
@@ -60,7 +60,6 @@
         DisplayButtonData buttonData =
                 new DrawableButtonData(
                         R.string.button_new_tab, R.string.button_new_incognito_tab, drawable);
-        assertEquals(buttonData, buttonData);
         assertEquals(
                 buttonData,
                 new DrawableButtonData(
diff --git a/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/ResourceButtonDataUnitTest.java b/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/ResourceButtonDataUnitTest.java
index f13cd58..739d7ad 100644
--- a/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/ResourceButtonDataUnitTest.java
+++ b/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/ResourceButtonDataUnitTest.java
@@ -61,7 +61,6 @@
                         R.string.button_new_tab,
                         R.string.button_new_incognito_tab,
                         R.drawable.ic_add);
-        assertEquals(buttonData, buttonData);
         assertEquals(
                 buttonData,
                 new ResourceButtonData(
diff --git a/chrome/browser/lacros/automation_manager_lacros.cc b/chrome/browser/lacros/automation_manager_lacros.cc
index 5bbbf2ff1..e109ce7 100644
--- a/chrome/browser/lacros/automation_manager_lacros.cc
+++ b/chrome/browser/lacros/automation_manager_lacros.cc
@@ -56,8 +56,8 @@
 }
 
 void AutomationManagerLacros::DispatchAccessibilityLocationChange(
-    const ui::AXLocationChanges& details) {
-  ui::AXTreeID tree_id = details.ax_tree_id;
+    const ui::AXTreeID& tree_id,
+    const ui::AXLocationChange& details) {
   if (!tree_id.token())
     return;
 
diff --git a/chrome/browser/lacros/automation_manager_lacros.h b/chrome/browser/lacros/automation_manager_lacros.h
index 5e2647da..fd8a010 100644
--- a/chrome/browser/lacros/automation_manager_lacros.h
+++ b/chrome/browser/lacros/automation_manager_lacros.h
@@ -31,7 +31,8 @@
       const gfx::Point& mouse_location,
       const std::vector<ui::AXEvent>& events) override;
   void DispatchAccessibilityLocationChange(
-      const ui::AXLocationChanges& details) override;
+      const ui::AXTreeID& tree_id,
+      const ui::AXLocationChange& details) override;
   void DispatchTreeDestroyedEvent(ui::AXTreeID tree_id) override;
   void DispatchActionResult(const ui::AXActionData& data,
                             bool result,
diff --git a/chrome/browser/lens/BUILD.gn b/chrome/browser/lens/BUILD.gn
index d606492..bd138dca 100644
--- a/chrome/browser/lens/BUILD.gn
+++ b/chrome/browser/lens/BUILD.gn
@@ -12,6 +12,7 @@
   deps = [
     ":java_resources",
     "//base:base_java",
+    "//base:service_loader_java",
     "//chrome/browser/contextmenu:java",
     "//chrome/browser/ui/android/strings:ui_strings_grd",
     "//components/embedder_support/android:context_menu_java",
@@ -30,7 +31,6 @@
 android_library("delegate_java") {
   sources = [
     "java/src/org/chromium/chrome/browser/lens/LensControllerDelegate.java",
-    "java/src/org/chromium/chrome/browser/lens/LensControllerDelegateImpl.java",
   ]
 
   deps = [
@@ -44,21 +44,6 @@
     "//ui/android:ui_java",
   ]
   resources_package = "org.chromium.chrome.browser.lens"
-
-  # Add the actual implementation where necessary so that downstream targets
-  # can provide their own implementations.
-  jar_excluded_patterns = [ "*/LensControllerDelegateImpl.class" ]
-}
-
-android_library("delegate_public_impl_java") {
-  sources = [
-    "java/src/org/chromium/chrome/browser/lens/LensControllerDelegateImpl.java",
-  ]
-
-  deps = [
-    ":delegate_java",
-    ":util_java",
-  ]
 }
 
 android_library("util_java") {
diff --git a/chrome/browser/lens/java/src/org/chromium/chrome/browser/lens/LensController.java b/chrome/browser/lens/java/src/org/chromium/chrome/browser/lens/LensController.java
index 51807fb..9572a6a 100644
--- a/chrome/browser/lens/java/src/org/chromium/chrome/browser/lens/LensController.java
+++ b/chrome/browser/lens/java/src/org/chromium/chrome/browser/lens/LensController.java
@@ -6,6 +6,7 @@
 import androidx.annotation.NonNull;
 
 import org.chromium.base.Callback;
+import org.chromium.base.ServiceLoaderUtil;
 import org.chromium.components.embedder_support.contextmenu.ChipRenderParams;
 import org.chromium.ui.base.WindowAndroid;
 
@@ -23,11 +24,17 @@
     }
 
     public LensController() {
-        mDelegate = new LensControllerDelegateImpl();
+        LensControllerDelegate delegate =
+                ServiceLoaderUtil.maybeCreate(LensControllerDelegate.class);
+        if (delegate == null) {
+            delegate = new LensControllerDelegate();
+        }
+        mDelegate = delegate;
     }
 
     /**
      * Whether the Lens SDK is available.
+     *
      * @return Whether the Lens SDK is available.
      */
     public boolean isSdkAvailable() {
diff --git a/chrome/browser/lens/java/src/org/chromium/chrome/browser/lens/LensControllerDelegate.java b/chrome/browser/lens/java/src/org/chromium/chrome/browser/lens/LensControllerDelegate.java
index c137bb4e..a47c838 100644
--- a/chrome/browser/lens/java/src/org/chromium/chrome/browser/lens/LensControllerDelegate.java
+++ b/chrome/browser/lens/java/src/org/chromium/chrome/browser/lens/LensControllerDelegate.java
@@ -18,8 +18,8 @@
 
 /**
  * Base class for defining methods where different behavior is required by downstream targets. The
- * correct version of {@link LensControllerDelegateImpl} will be determined at compile time via
- * build rules.
+ * correct implementation of {@link LensControllerDelegate} will be determined at compile time via
+ * {@link ServiceLoaderUtil}.
  */
 public class LensControllerDelegate {
     /**
diff --git a/chrome/browser/lens/java/src/org/chromium/chrome/browser/lens/LensControllerDelegateImpl.java b/chrome/browser/lens/java/src/org/chromium/chrome/browser/lens/LensControllerDelegateImpl.java
deleted file mode 100644
index d094456..0000000
--- a/chrome/browser/lens/java/src/org/chromium/chrome/browser/lens/LensControllerDelegateImpl.java
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.lens;
-
-/**
- * Instantiable version of {@link LensControllerDelegate}, don't add anything to this class.
- * Downstream targets may provide a different implementation. In GN, we specify that
- * {@link LensControllerDelegate} is compiled separately from its implementation; other
- * projects may specify a different LensControllerDelegate via GN.
- */
-class LensControllerDelegateImpl extends LensControllerDelegate {}
diff --git a/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesCoordinator.java b/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesCoordinator.java
index 072d70f..b8edb39 100644
--- a/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesCoordinator.java
+++ b/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesCoordinator.java
@@ -140,7 +140,7 @@
                         super.onViewRecycled(holder);
                     }
                 };
-        mModuleRegistry.registerAdapter(mAdapter, this::onViewCreated);
+        mModuleRegistry.registerAdapter(mAdapter, this);
         mRecyclerView.setAdapter(mAdapter);
     }
 
diff --git a/chrome/browser/metrics/tab_stats/tab_stats_tracker.cc b/chrome/browser/metrics/tab_stats/tab_stats_tracker.cc
index 5eb32fdf..1a84693e 100644
--- a/chrome/browser/metrics/tab_stats/tab_stats_tracker.cc
+++ b/chrome/browser/metrics/tab_stats/tab_stats_tracker.cc
@@ -626,10 +626,12 @@
 
     base::UmaHistogramCounts100(kTabDuplicateCountSingleWindowHistogramName,
                                 duplicate_data_single_window.duplicate_count);
-    base::UmaHistogramPercentage(
-        kTabDuplicatePercentageSingleWindowHistogramName,
-        duplicate_data_single_window.duplicate_count * 100 /
-            duplicate_data_single_window.tab_count);
+    if (duplicate_data_single_window.tab_count > 0) {
+      base::UmaHistogramPercentage(
+          kTabDuplicatePercentageSingleWindowHistogramName,
+          duplicate_data_single_window.duplicate_count * 100 /
+              duplicate_data_single_window.tab_count);
+    }
   }
   for (const auto& duplicate_data : duplicate_data_per_profile) {
     // Guest mode and incognito should not count for the per-profile metrics
@@ -641,10 +643,12 @@
     base::UmaHistogramCounts100(
         kTabDuplicateCountAllProfileWindowsHistogramName,
         duplicate_data.second.duplicate_count);
-    base::UmaHistogramPercentage(
-        kTabDuplicatePercentageAllProfileWindowsHistogramName,
-        duplicate_data.second.duplicate_count * 100 /
-            duplicate_data.second.tab_count);
+    if (duplicate_data.second.tab_count > 0) {
+      base::UmaHistogramPercentage(
+          kTabDuplicatePercentageAllProfileWindowsHistogramName,
+          duplicate_data.second.duplicate_count * 100 /
+              duplicate_data.second.tab_count);
+    }
   }
 }
 
diff --git a/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler.cc b/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler.cc
index 427c30c..37e88ee 100644
--- a/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler.cc
+++ b/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler.cc
@@ -49,9 +49,9 @@
 using Source = visited_url_ranking::URLVisit::Source;
 
 namespace {
-// Name of preference to track list of dismissed tabs.
-const char kDismissedTabsPrefName[] =
-    "NewTabPage.MostRelevantTabResumption.DismissedTabs";
+// Name of preference to track list of dismissed visits.
+const char kDismissedVisitsPrefName[] =
+    "NewTabPage.MostRelevantTabResumption.DismissedVisits";
 
 std::u16string FormatRelativeTime(const base::Time& time) {
   // Return a time like "1 hour ago", "2 days ago", etc.
@@ -249,17 +249,31 @@
 void MostRelevantTabResumptionPageHandler::DismissModule(
     const std::vector<ntp::most_relevant_tab_resumption::mojom::URLVisitPtr>
         url_visits) {
+  DismissURLVisits(url_visits);
+}
+
+void MostRelevantTabResumptionPageHandler::DismissURLVisit(
+    ntp::most_relevant_tab_resumption::mojom::URLVisitPtr url_visit) {
+  std::vector<ntp::most_relevant_tab_resumption::mojom::URLVisitPtr>
+      url_visits_mojom;
+  url_visits_mojom.push_back(std::move(url_visit));
+  DismissURLVisits(url_visits_mojom);
+}
+
+void MostRelevantTabResumptionPageHandler::DismissURLVisits(
+    const std::vector<ntp::most_relevant_tab_resumption::mojom::URLVisitPtr>&
+        url_visits) {
   RemoveOldDismissedTabs();
-  ScopedListPrefUpdate url_visit_list(profile_->GetPrefs(),
-                                      kDismissedTabsPrefName);
+  ScopedDictPrefUpdate url_visit_dict(profile_->GetPrefs(),
+                                      kDismissedVisitsPrefName);
   auto* visited_url_ranking_service =
       visited_url_ranking::VisitedURLRankingServiceFactory::GetForProfile(
           profile_);
   for (const auto& url_visit : url_visits) {
-    url_visit_list->Append(base::Value(
-        url_visit->url_key + ' ' +
-        base::NumberToString(url_visit->timestamp->ToDeltaSinceWindowsEpoch()
-                                 .InMicroseconds())));
+    url_visit_dict->Set(
+        url_visit->url_key,
+        static_cast<double>(
+            url_visit->timestamp->ToDeltaSinceWindowsEpoch().InMicroseconds()));
     visited_url_ranking_service->RecordAction(
         visited_url_ranking::ScoredURLUserAction::kDismissed,
         url_visit->url_key,
@@ -268,57 +282,40 @@
   }
 }
 
-void MostRelevantTabResumptionPageHandler::DismissURLVisit(
-    const ntp::most_relevant_tab_resumption::mojom::URLVisitPtr url_visit) {
-  RemoveOldDismissedTabs();
-  ScopedListPrefUpdate url_visit_list(profile_->GetPrefs(),
-                                      kDismissedTabsPrefName);
-  url_visit_list->Append(base::Value(
-      url_visit->url_key + ' ' +
-      base::NumberToString(
-          url_visit->timestamp->ToDeltaSinceWindowsEpoch().InMicroseconds())));
-  auto* visited_url_ranking_service =
-      visited_url_ranking::VisitedURLRankingServiceFactory::GetForProfile(
-          profile_);
-  visited_url_ranking_service->RecordAction(
-      visited_url_ranking::ScoredURLUserAction::kDismissed, url_visit->url_key,
-      segmentation_platform::TrainingRequestId(url_visit->training_request_id));
-}
-
 void MostRelevantTabResumptionPageHandler::RestoreModule(
     const std::vector<ntp::most_relevant_tab_resumption::mojom::URLVisitPtr>
         url_visits) {
-  ScopedListPrefUpdate url_visit_list(profile_->GetPrefs(),
-                                      kDismissedTabsPrefName);
-  auto* visited_url_ranking_service =
-      visited_url_ranking::VisitedURLRankingServiceFactory::GetForProfile(
-          profile_);
-  for (const auto& url_visit : url_visits) {
-    url_visit_list->EraseValue(base::Value(
-        url_visit->url_key + ' ' +
-        base::NumberToString(url_visit->timestamp->ToDeltaSinceWindowsEpoch()
-                                 .InMicroseconds())));
-    visited_url_ranking_service->RecordAction(
-        visited_url_ranking::ScoredURLUserAction::kSeen, url_visit->url_key,
-        segmentation_platform::TrainingRequestId(
-            url_visit->training_request_id));
-  }
+  RestoreURLVisits(std::move(url_visits));
 }
 
 void MostRelevantTabResumptionPageHandler::RestoreURLVisit(
     ntp::most_relevant_tab_resumption::mojom::URLVisitPtr url_visit) {
-  ScopedListPrefUpdate url_visit_list(profile_->GetPrefs(),
-                                      kDismissedTabsPrefName);
-  url_visit_list->EraseValue(base::Value(
-      url_visit->url_key + ' ' +
-      base::NumberToString(
-          url_visit->timestamp->ToDeltaSinceWindowsEpoch().InMicroseconds())));
+  std::vector<ntp::most_relevant_tab_resumption::mojom::URLVisitPtr>
+      url_visits_mojom;
+  url_visits_mojom.push_back(std::move(url_visit));
+  RestoreURLVisits(url_visits_mojom);
+}
+
+void MostRelevantTabResumptionPageHandler::RestoreURLVisits(
+    const std::vector<ntp::most_relevant_tab_resumption::mojom::URLVisitPtr>&
+        url_visits) {
+  ScopedDictPrefUpdate url_visit_dict(profile_->GetPrefs(),
+                                      kDismissedVisitsPrefName);
   auto* visited_url_ranking_service =
       visited_url_ranking::VisitedURLRankingServiceFactory::GetForProfile(
           profile_);
-  visited_url_ranking_service->RecordAction(
-      visited_url_ranking::ScoredURLUserAction::kSeen, url_visit->url_key,
-      segmentation_platform::TrainingRequestId(url_visit->training_request_id));
+  for (const auto& url_visit : url_visits) {
+    if (url_visit_dict->Find(url_visit->url_key) &&
+        static_cast<long>(
+            url_visit_dict->Find(url_visit->url_key)->GetDouble()) ==
+            url_visit->timestamp->ToDeltaSinceWindowsEpoch().InMicroseconds()) {
+      url_visit_dict->Remove(url_visit->url_key);
+      visited_url_ranking_service->RecordAction(
+          visited_url_ranking::ScoredURLUserAction::kSeen, url_visit->url_key,
+          segmentation_platform::TrainingRequestId(
+              url_visit->training_request_id));
+    }
+  }
 }
 
 void MostRelevantTabResumptionPageHandler::OnURLVisitAggregatesFetched(
@@ -473,40 +470,31 @@
 // static
 void MostRelevantTabResumptionPageHandler::RegisterProfilePrefs(
     PrefRegistrySimple* registry) {
-  registry->RegisterListPref(kDismissedTabsPrefName, base::Value::List());
+  registry->RegisterDictionaryPref(kDismissedVisitsPrefName,
+                                   base::Value::Dict());
 }
 
 bool MostRelevantTabResumptionPageHandler::IsNewURL(
     ntp::most_relevant_tab_resumption::mojom::URLVisitPtr& url_visit) {
-  const base::Value::List& cached_urls =
-      profile_->GetPrefs()->GetList(kDismissedTabsPrefName);
-  auto it = std::find_if(
-      cached_urls.begin(), cached_urls.end(),
-      [&url_visit](const base::Value& cached_url) {
-        return cached_url.GetString() ==
-               url_visit->url_key + ' ' +
-                   base::NumberToString(
-                       url_visit->timestamp->ToDeltaSinceWindowsEpoch()
-                           .InMicroseconds());
-      });
-  return it == cached_urls.end();
+  const base::Value::Dict& cached_urls =
+      profile_->GetPrefs()->GetDict(kDismissedVisitsPrefName);
+  if (cached_urls.Find(url_visit->url_key) == nullptr) {
+    return true;
+  } else {
+    return static_cast<long>(
+               cached_urls.Find(url_visit->url_key)->GetDouble()) !=
+           url_visit->timestamp->ToDeltaSinceWindowsEpoch().InMicroseconds();
+  }
 }
 
 void MostRelevantTabResumptionPageHandler::RemoveOldDismissedTabs() {
-  ScopedListPrefUpdate tab_list(profile_->GetPrefs(), kDismissedTabsPrefName);
-  for (const auto& entry : tab_list.Get().Clone()) {
-    const std::string dismissed_tab_string = entry.GetString();
-    size_t delimiter_pos = dismissed_tab_string.find(' ');
-    if (delimiter_pos != std::string::npos) {
-      int64_t timestamp_microseconds;
-      base::StringToInt64(dismissed_tab_string.substr(delimiter_pos),
-                          &timestamp_microseconds);
-      base::Time timestamp = base::Time::FromDeltaSinceWindowsEpoch(
-          base::Microseconds(timestamp_microseconds));
-      if (base::Time::Now() - timestamp >
-          base::Days(dismissal_duration_days_)) {
-        tab_list->EraseValue(entry);
-      }
+  ScopedDictPrefUpdate visit_dict(profile_->GetPrefs(),
+                                  kDismissedVisitsPrefName);
+  for (auto it = visit_dict->begin(); it != visit_dict->end(); ++it) {
+    base::Time timestamp = base::Time::FromDeltaSinceWindowsEpoch(
+        base::Microseconds(it->second.GetDouble()));
+    if (base::Time::Now() - timestamp > base::Days(dismissal_duration_days_)) {
+      visit_dict->Remove(it->first);
     }
   }
 }
diff --git a/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler.h b/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler.h
index 1ece78a5..e34ca4e 100644
--- a/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler.h
+++ b/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler.h
@@ -52,8 +52,7 @@
       const std::vector<ntp::most_relevant_tab_resumption::mojom::URLVisitPtr>
           url_visits) override;
   void DismissURLVisit(
-      const ntp::most_relevant_tab_resumption::mojom::URLVisitPtr url_visit)
-      override;
+      ntp::most_relevant_tab_resumption::mojom::URLVisitPtr url_visit) override;
   void RestoreModule(
       const std::vector<ntp::most_relevant_tab_resumption::mojom::URLVisitPtr>
           url_visits) override;
@@ -91,6 +90,13 @@
   bool IsNewURL(
       ntp::most_relevant_tab_resumption::mojom::URLVisitPtr& url_visit);
 
+  void DismissURLVisits(
+      const std::vector<ntp::most_relevant_tab_resumption::mojom::URLVisitPtr>&
+          url_visits);
+  void RestoreURLVisits(
+      const std::vector<ntp::most_relevant_tab_resumption::mojom::URLVisitPtr>&
+          url_visits);
+
   // Method to clear dismissed tabs that are older than a certain amount of
   // time.
   void RemoveOldDismissedTabs();
diff --git a/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler_unittest.cc b/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler_unittest.cc
index 533b568..b46b8dd 100644
--- a/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler_unittest.cc
+++ b/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_page_handler_unittest.cc
@@ -371,7 +371,9 @@
                     {Fetcher::kSession}));
             url_visit_aggregates.emplace_back(
                 visited_url_ranking::CreateSampleURLVisitAggregate(
-                    GURL(visited_url_ranking::kSampleSearchUrl), 1.0f,
+                    GURL(visited_url_ranking::kSampleSearchUrl +
+                         std::string("1")),
+                    1.0f,
                     base::Time::FromDeltaSinceWindowsEpoch(
                         base::Microseconds(123456)),
                     {Fetcher::kHistory}));
diff --git a/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/url_visit_types.mojom b/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/url_visit_types.mojom
index 75446f0..a459f9b 100644
--- a/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/url_visit_types.mojom
+++ b/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/url_visit_types.mojom
@@ -22,6 +22,7 @@
 enum DecorationType {
   kVisitedXAgo,
   kMostRecent,
+  kFrequentlyVisited,
   kFrequentlyVisitedAtTime,
 };
 
diff --git a/chrome/browser/optimization_guide/android/java/src/org/chromium/chrome/browser/optimization_guide/OptimizationGuidePushNotificationManager.java b/chrome/browser/optimization_guide/android/java/src/org/chromium/chrome/browser/optimization_guide/OptimizationGuidePushNotificationManager.java
index db6768e..a00680c 100644
--- a/chrome/browser/optimization_guide/android/java/src/org/chromium/chrome/browser/optimization_guide/OptimizationGuidePushNotificationManager.java
+++ b/chrome/browser/optimization_guide/android/java/src/org/chromium/chrome/browser/optimization_guide/OptimizationGuidePushNotificationManager.java
@@ -200,7 +200,7 @@
     @VisibleForTesting
     public static String cacheKey(OptimizationType optimizationType) {
         return ChromePreferenceKeys.OPTIMIZATION_GUIDE_PUSH_NOTIFICATION_CACHE.createKey(
-                optimizationType.toString());
+                optimizationType.name());
     }
 
     public static void setNativeIsInitializedForTesting(Boolean nativeIsInitialized) {
diff --git a/chrome/browser/page_load_metrics/observers/third_party_cookie_deprecation_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/third_party_cookie_deprecation_metrics_observer_browsertest.cc
index cf02b08..0f8bf7ad 100644
--- a/chrome/browser/page_load_metrics/observers/third_party_cookie_deprecation_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/third_party_cookie_deprecation_metrics_observer_browsertest.cc
@@ -99,8 +99,6 @@
         .allow_by_global_setting = true,
         .expected_allow_mechanism_histogram_sample =
             ThirdPartyCookieAllowMechanism::kAllowByGlobalSetting,
-        .expected_web_feature_histogram_sample =
-            WebFeature::kThirdPartyCookieDeprecation_AllowByGlobalSetting,
     },
     {
         .allow_by_3pcd_1p_trial_token = true,
@@ -176,8 +174,6 @@
         .allow_by_explicit_setting = true,
         .expected_allow_mechanism_histogram_sample =
             ThirdPartyCookieAllowMechanism::kAllowByExplicitSetting,
-        .expected_web_feature_histogram_sample =
-            WebFeature::kThirdPartyCookieDeprecation_AllowByExplicitSetting,
     },
     // Precedence testing test cases:
     {
@@ -185,8 +181,6 @@
         .allow_by_3pcd_1p_trial_token = true,
         .expected_allow_mechanism_histogram_sample =
             ThirdPartyCookieAllowMechanism::kAllowByGlobalSetting,
-        .expected_web_feature_histogram_sample =
-            WebFeature::kThirdPartyCookieDeprecation_AllowByGlobalSetting,
     },
     {
         .allow_by_3pcd_1p_trial_token = true,
@@ -1280,11 +1274,9 @@
 
   histogram_tester.ExpectUniqueSample(kThirdPartyCookieAllowMechanismHistogram,
                                       /*kAllowByStorageAccess*/ 6, 2);
-  // Only record blink usage when tracking protection is onboard.
   histogram_tester.ExpectBucketCount(
       kWebFeatureHistogram,
-      WebFeature::kThirdPartyCookieDeprecation_AllowByStorageAccess,
-      GetParam() ? 1 : 0);
+      WebFeature::kThirdPartyCookieAccessBlockByExperiment, GetParam() ? 1 : 0);
 }
 
 class ThirdPartyCookieDeprecationObserverCookieReadBrowserTest
diff --git a/chrome/browser/page_load_metrics/observers/third_party_cookie_deprecation_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/third_party_cookie_deprecation_page_load_metrics_observer.cc
index 3fd5b36..6dbd91e 100644
--- a/chrome/browser/page_load_metrics/observers/third_party_cookie_deprecation_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/third_party_cookie_deprecation_page_load_metrics_observer.cc
@@ -173,16 +173,12 @@
         .Record(ukm::UkmRecorder::Get());
   }
 
-  if (!is_blocked_by_experiment) {
-    return;
-  }
-
-  // Record the following blink feature usage cookie metrics when the 3PCD
-  // experiment is actual block third party cookies, which means tracking
-  // protection is onboard.
+  // Record the following blink feature usage cookie metrics.
   std::vector<blink::mojom::WebFeature> third_party_cookie_features;
-  third_party_cookie_features.push_back(
-      blink::mojom::WebFeature::kThirdPartyCookieAccessBlockByExperiment);
+  if (is_blocked_by_experiment) {
+    third_party_cookie_features.push_back(
+        blink::mojom::WebFeature::kThirdPartyCookieAccessBlockByExperiment);
+  }
 
   switch (allow_mechanism) {
     case ThirdPartyCookieAllowMechanism::kAllowByExplicitSetting:
diff --git a/chrome/browser/partnercustomizations/java/src/org/chromium/chrome/browser/partnercustomizations/PartnerBrowserCustomizationsRoboUnitTest.java b/chrome/browser/partnercustomizations/java/src/org/chromium/chrome/browser/partnercustomizations/PartnerBrowserCustomizationsRoboUnitTest.java
index 0fa449b..8666313 100644
--- a/chrome/browser/partnercustomizations/java/src/org/chromium/chrome/browser/partnercustomizations/PartnerBrowserCustomizationsRoboUnitTest.java
+++ b/chrome/browser/partnercustomizations/java/src/org/chromium/chrome/browser/partnercustomizations/PartnerBrowserCustomizationsRoboUnitTest.java
@@ -118,21 +118,21 @@
 
         public ShadowCustomizationProviderDelegate() {}
 
+        /** Returns the homepage string or null if none is available. */
         @Implementation
         @Nullable
-        /** Returns the homepage string or null if none is available. */
         protected String getHomepage() {
             return sHomepage;
         }
 
-        @Implementation
         /** Returns whether incognito mode is disabled. */
+        @Implementation
         protected boolean isIncognitoModeDisabled() {
             return false;
         }
 
-        @Implementation
         /** Returns whether bookmark editing is disabled. */
+        @Implementation
         protected boolean isBookmarksEditingDisabled() {
             return false;
         }
diff --git a/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/PasswordChangeType.java b/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/PasswordChangeType.java
index c5ba009..3feeef2 100644
--- a/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/PasswordChangeType.java
+++ b/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/PasswordChangeType.java
@@ -22,8 +22,6 @@
     /** A user opened a site to change a password manually. */
     int MANUAL_CHANGE = 1;
 
-    /**
-     * Deprecated as a part of APC removal (crbug.com/1386065).
-     * int AUTOMATED_CHANGE = 2;
-     */
+    // Deprecated as a part of APC removal (crbug.com/1386065).
+    // int AUTOMATED_CHANGE = 2;
 }
diff --git a/chrome/browser/password_manager/android/access_loss/java/src/org/chromium/chrome/browser/access_loss/PasswordAccessLossWarningHelper.java b/chrome/browser/password_manager/android/access_loss/java/src/org/chromium/chrome/browser/access_loss/PasswordAccessLossWarningHelper.java
index e749d77..6ba7228 100644
--- a/chrome/browser/password_manager/android/access_loss/java/src/org/chromium/chrome/browser/access_loss/PasswordAccessLossWarningHelper.java
+++ b/chrome/browser/password_manager/android/access_loss/java/src/org/chromium/chrome/browser/access_loss/PasswordAccessLossWarningHelper.java
@@ -70,8 +70,8 @@
         coordinator.showSheet(model);
     }
 
-    @Nullable
     /** Creates the model that has the text and functionality appropriate for the warning type. */
+    @Nullable
     PropertyModel getModelForWarningType(@PasswordAccessLossWarningType int warningType) {
         switch (warningType) {
             case PasswordAccessLossWarningType.NO_GMS_CORE:
diff --git a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/GmsUpdateLauncher.java b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/GmsUpdateLauncher.java
index 0ae5fb2..67629b4 100644
--- a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/GmsUpdateLauncher.java
+++ b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/GmsUpdateLauncher.java
@@ -32,6 +32,7 @@
         intent.setPackage("com.android.vending");
         intent.setData(Uri.parse(deepLinkUrl));
         intent.putExtra("callerId", context.getPackageName());
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
         // Request for overlay flow, Play Store will fallback to the default
         // behaviour if overlay is not available.
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/GmsUpdateLauncherTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/GmsUpdateLauncherTest.java
index ca5d4d5..f1ce2e8 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/GmsUpdateLauncherTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/GmsUpdateLauncherTest.java
@@ -36,6 +36,7 @@
         assertEquals(intent.getAction(), Intent.ACTION_VIEW);
         assertEquals(intent.getPackage(), "com.android.vending");
         assertEquals(intent.getStringExtra("callerId"), mockContext.getPackageName());
+        assertEquals(intent.getFlags(), Intent.FLAG_ACTIVITY_NEW_TASK);
         assertEquals(
                 intent.getData(),
                 Uri.parse(
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index bad98a8..8cbedb5 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -1096,6 +1096,12 @@
     "privacy_sandbox.activity_type.record";
 #endif  // BUILDFLAG(IS_ANDROID)
 
+// Deprecated 09/2024.
+#if !BUILDFLAG(IS_ANDROID)
+const char kTabResumeDismissedTabsPrefName[] =
+    "NewTabPage.MostRelevantTabResumption.DismissedTabs";
+#endif  // !BUILDFLAG(IS_ANDROID)
+
 // Register local state used only for migration (clearing or moving to a new
 // key).
 void RegisterLocalStatePrefsForMigration(PrefRegistrySimple* registry) {
@@ -1539,6 +1545,12 @@
 #if BUILDFLAG(IS_ANDROID)
   registry->RegisterListPref(kPrivacySandboxActivityTypeRecord);
 #endif  // BUILDFLAG(IS_ANDROID)
+
+// Deprecated 09/2024
+#if !BUILDFLAG(IS_ANDROID)
+  registry->RegisterListPref(kTabResumeDismissedTabsPrefName,
+                             base::Value::List());
+#endif  // !BUILDFLAG(IS_ANDROID)
 }
 
 void ClearSyncRequestedPrefAndMaybeMigrate(PrefService* profile_prefs) {
@@ -2873,6 +2885,11 @@
   profile_prefs->ClearPref(kPrivacySandboxActivityTypeRecord);
 #endif  // BUILDFLAG(IS_ANDROID)
 
+// Added 09/2024
+#if !BUILDFLAG(IS_ANDROID)
+  profile_prefs->ClearPref(kTabResumeDismissedTabsPrefName);
+#endif  // !BUILDFLAG(IS_ANDROID)
+
   // Please don't delete the following line. It is used by PRESUBMIT.py.
   // END_MIGRATE_OBSOLETE_PROFILE_PREFS
 
diff --git a/chrome/browser/resources/lens/overlay/translate_button.html b/chrome/browser/resources/lens/overlay/translate_button.html
index e08c911..5347f277 100644
--- a/chrome/browser/resources/lens/overlay/translate_button.html
+++ b/chrome/browser/resources/lens/overlay/translate_button.html
@@ -386,7 +386,8 @@
 </style>
 <div id="translateContainer">
   <cr-button id="translateEnableButton" class="button"
-  on-click="onTranslateButtonClick">
+    on-click="onTranslateButtonClick" aria-hidden="[[isTranslateModeEnabled]]"
+    tabindex$="[[getTabIndexForTranslateEntry(isTranslateModeEnabled)]]">
     <span id="translateEnableIcon" class="translate-icon" slot="prefix-icon">
     </span>
     <span id="translateButtonLabel" class="button-label">
@@ -395,7 +396,9 @@
   </cr-button>
   <div id="languagePicker">
     <cr-button id="sourceLanguageButton" class="button language-picker-button"
-        on-click="onSourceLanguageButtonClick">
+        on-click="onSourceLanguageButtonClick"
+        aria-hidden="[[!isTranslateModeEnabled]]"
+        tabindex$="[[getTabIndexForLanguagePicker(isTranslateModeEnabled)]]">
       <span id="starsIcon" slot="prefix-icon"></span>
       <span id="sourceLanguageLabel"
           class="button-label language-picker-label">
@@ -405,7 +408,9 @@
     </cr-button>
     <span id="arrowRightIcon"></span>
     <cr-button id="targetLanguageButton" class="button language-picker-button"
-        on-click="onTargetLanguageButtonClick">
+        on-click="onTargetLanguageButtonClick"
+        aria-hidden="[[!isTranslateModeEnabled]]"
+        tabindex$="[[getTabIndexForLanguagePicker(isTranslateModeEnabled)]]">
       <span id="targetLanguageLabel" class="button-label language-picker-label">
         [[getTargetLanguageDisplayName(targetLanguage)]]
       </span>
@@ -413,7 +418,9 @@
   </div>
   <div id="translateDisableButtonContainer">
     <cr-button id="translateDisableButton" class="button"
-    on-click="onTranslateButtonClick">
+      on-click="onTranslateButtonClick"
+      aria-hidden="[[!isTranslateModeEnabled]]"
+      tabindex$="[[getTabIndexForLanguagePicker(isTranslateModeEnabled)]]">
     </cr-button>
     <span id="translateDisableIcon" class="translate-icon"></span>
   </div>
diff --git a/chrome/browser/resources/lens/overlay/translate_button.ts b/chrome/browser/resources/lens/overlay/translate_button.ts
index 12ba638..248f8861 100644
--- a/chrome/browser/resources/lens/overlay/translate_button.ts
+++ b/chrome/browser/resources/lens/overlay/translate_button.ts
@@ -395,6 +395,14 @@
     return this.sourceLanguage === null;
   }
 
+  private getTabIndexForTranslateEntry(): number {
+    return this.isTranslateModeEnabled ? -1 : 0;
+  }
+
+  private getTabIndexForLanguagePicker(): number {
+    return this.isTranslateModeEnabled ? 0 : -1;
+  }
+
   private getAutoCheckedClass(
       sourceLanguage: chrome.languageSettingsPrivate.Language): string {
     return sourceLanguage === null ? 'selected' : '';
diff --git a/chrome/browser/resources/new_tab_page/modules/v2/most_relevant_tab_resumption/module.ts b/chrome/browser/resources/new_tab_page/modules/v2/most_relevant_tab_resumption/module.ts
index 1baed51f..f0b0ab4e 100644
--- a/chrome/browser/resources/new_tab_page/modules/v2/most_relevant_tab_resumption/module.ts
+++ b/chrome/browser/resources/new_tab_page/modules/v2/most_relevant_tab_resumption/module.ts
@@ -134,7 +134,8 @@
   }
 
   private onDismissButtonClick_(e: DomRepeatEvent<URLVisit>) {
-    e.preventDefault();
+    e.preventDefault();   // Stop navigation
+    e.stopPropagation();  // Stop firing of click handler
     const urlVisit = (e.target! as HTMLElement).parentElement!;
     const index = e.model.index;
     chrome.metricsPrivate.recordSmallCount(
diff --git a/chrome/browser/resources/password_manager/chrome_branded_manifest.webmanifest b/chrome/browser/resources/password_manager/chrome_branded_manifest.webmanifest
index cf0c669..6d8075f8 100644
--- a/chrome/browser/resources/password_manager/chrome_branded_manifest.webmanifest
+++ b/chrome/browser/resources/password_manager/chrome_branded_manifest.webmanifest
@@ -1,6 +1,6 @@
 {
-  "short_name": "Google Password Manager",
-  "name": "Google Password Manager",
+  "short_name": "$i18n{passwordManagerTitle}",
+  "name": "$i18n{passwordManagerTitle}",
   "icons": [
     {
       "src": "chrome://password-manager/images/password_manager_pwa_icon.svg",
@@ -8,6 +8,7 @@
       "sizes": "any"
     }
   ],
+  "description": "$i18n{passwordManagerDescription}",
   "start_url": "/?source=pwa",
   "id": "chrome://password-manager/",
   "display": "standalone",
diff --git a/chrome/browser/resources/password_manager/manifest.webmanifest b/chrome/browser/resources/password_manager/manifest.webmanifest
index 3a4278be..56bc0ea 100644
--- a/chrome/browser/resources/password_manager/manifest.webmanifest
+++ b/chrome/browser/resources/password_manager/manifest.webmanifest
@@ -1,6 +1,6 @@
 {
-  "short_name": "Password Manager",
-  "name": "Password Manager",
+  "short_name": "$i18n{passwordManagerTitle}",
+  "name": "$i18n{passwordManagerTitle}",
   "icons": [
     {
       "src": "chrome://password-manager/images/password_manager_logo.svg",
@@ -12,4 +12,4 @@
   "id": "chrome://password-manager/",
   "display": "standalone",
   "scope": "chrome://password-manager/"
-}
\ No newline at end of file
+}
diff --git a/chrome/browser/resources/search_engine_choice/OWNERS b/chrome/browser/resources/search_engine_choice/OWNERS
index a8ff91d..c7aa42b 100644
--- a/chrome/browser/resources/search_engine_choice/OWNERS
+++ b/chrome/browser/resources/search_engine_choice/OWNERS
@@ -1 +1 @@
-file://components/search_engines/search_engine_choice/OWNERS
+file://components/search_engines/search_engine_choice/COMPLIANCE_OWNERS
diff --git a/chrome/browser/resources/side_panel/customize_chrome/categories.ts b/chrome/browser/resources/side_panel/customize_chrome/categories.ts
index ed95370..64ef3d0 100644
--- a/chrome/browser/resources/side_panel/customize_chrome/categories.ts
+++ b/chrome/browser/resources/side_panel/customize_chrome/categories.ts
@@ -23,7 +23,7 @@
 
 import {getCss} from './categories.css.js';
 import {getHtml} from './categories.html.js';
-import {CustomizeChromeAction, recordCustomizeChromeAction} from './common.js';
+import {CustomizeChromeAction, NtpImageType, recordCustomizeChromeAction, recordCustomizeChromeImageError} from './common.js';
 import type {BackgroundCollection, CustomizeChromePageHandlerInterface, Theme} from './customize_chrome.mojom-webui.js';
 import {CustomizeChromeApiProxy} from './customize_chrome_api_proxy.js';
 import {WindowProxy} from './window_proxy.js';
@@ -193,6 +193,7 @@
     if (!this.imageErrorDetectionEnabled_) {
       return;
     }
+    recordCustomizeChromeImageError(NtpImageType.COLLECTIONS);
     const index = Number((e.currentTarget as HTMLElement).dataset['index']);
     assert(this.collections_[index]);
     this.pageHandler_
diff --git a/chrome/browser/resources/side_panel/customize_chrome/common.ts b/chrome/browser/resources/side_panel/customize_chrome/common.ts
index 7309b00..6e0f4daf 100644
--- a/chrome/browser/resources/side_panel/customize_chrome/common.ts
+++ b/chrome/browser/resources/side_panel/customize_chrome/common.ts
@@ -62,3 +62,25 @@
       'NewTabPage.CustomizeChromeSidePanelImpression', action,
       CustomizeChromeImpression.MAX_VALUE + 1);
 }
+
+/**
+ * Types of images that are shown on the NTP (and therefore also appear in
+ * Customize Chrome). This enum must match the numbering for NtpImageType in
+ * enums.xml. These values are persisted to logs. Entries should not be
+ * renumbered, removed or reused.
+ *
+ * MAX_VALUE should always be at the end to help get the current number of
+ * buckets.
+ */
+export enum NtpImageType {
+  BACKGROUND_IMAGE,
+  COLLECTIONS,
+  COLLECTION_IMAGES,
+  MAX_VALUE = COLLECTION_IMAGES,
+}
+
+export function recordCustomizeChromeImageError(imageType: NtpImageType) {
+  chrome.metricsPrivate.recordEnumerationValue(
+      'NewTabPage.BackgroundService.Images.Headers.ErrorDetected', imageType,
+      NtpImageType.MAX_VALUE + 1);
+}
diff --git a/chrome/browser/resources/side_panel/customize_chrome/themes.html.ts b/chrome/browser/resources/side_panel/customize_chrome/themes.html.ts
index e09aa98..f2baec2 100644
--- a/chrome/browser/resources/side_panel/customize_chrome/themes.html.ts
+++ b/chrome/browser/resources/side_panel/customize_chrome/themes.html.ts
@@ -34,7 +34,8 @@
             <img is="cr-auto-img" data-index="${index}"
                 .autoSrc="${item.previewImageUrl.url}"
                 draggable="false"
-                @load="${this.onPreviewImageLoad_}">
+                @load="${this.onPreviewImageLoad_}"
+                @error="${this.onPreviewImageError_}">
             </img>
           </div>
         </customize-chrome-check-mark-wrapper>
diff --git a/chrome/browser/resources/side_panel/customize_chrome/themes.ts b/chrome/browser/resources/side_panel/customize_chrome/themes.ts
index ef1c292..47196caa 100644
--- a/chrome/browser/resources/side_panel/customize_chrome/themes.ts
+++ b/chrome/browser/resources/side_panel/customize_chrome/themes.ts
@@ -18,7 +18,7 @@
 import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 import type {PropertyValues} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 
-import {CustomizeChromeAction, recordCustomizeChromeAction} from './common.js';
+import {CustomizeChromeAction, NtpImageType, recordCustomizeChromeAction, recordCustomizeChromeImageError} from './common.js';
 import type {BackgroundCollection, CollectionImage, CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerInterface, Theme} from './customize_chrome.mojom-webui.js';
 import {CustomizeChromeApiProxy} from './customize_chrome_api_proxy.js';
 import {getCss} from './themes.css.js';
@@ -170,6 +170,12 @@
             this.previewImageLoadStartEpoch_));
   }
 
+  protected onPreviewImageError_() {
+    if (this.imageErrorDetectionEnabled_) {
+      recordCustomizeChromeImageError(NtpImageType.BACKGROUND_IMAGE);
+    }
+  }
+
   private onCollectionChange_() {
     this.header_ = '';
     this.themes_ = [];
diff --git a/chrome/browser/search/background/ntp_background_service.cc b/chrome/browser/search/background/ntp_background_service.cc
index 17577bd..3984ab24 100644
--- a/chrome/browser/search/background/ntp_background_service.cc
+++ b/chrome/browser/search/background/ntp_background_service.cc
@@ -374,10 +374,6 @@
 void NtpBackgroundService::FetchReplacementCollectionPreviewImage(
     const std::string& collection_id,
     FetchReplacementImageCallback fetch_replacement_image_callback) {
-  // TODO(b:367702048) - Move metric to frontend, where the error was detected.
-  UMA_HISTOGRAM_ENUMERATION(
-      "NewTabPage.BackgroundService.Images.Headers.ErrorDetected",
-      NtpImageType::kCollections);
   FetchCollectionImageInfoInternal(
       collection_id,
       base::BindOnce(&NtpBackgroundService::
diff --git a/chrome/browser/search/background/ntp_background_service_unittest.cc b/chrome/browser/search/background/ntp_background_service_unittest.cc
index ba49c83..ddd2fb6 100644
--- a/chrome/browser/search/background/ntp_background_service_unittest.cc
+++ b/chrome/browser/search/background/ntp_background_service_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/run_loop.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/run_until.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "chrome/browser/browser_process.h"
@@ -45,17 +46,13 @@
 
 }  // namespace
 
-class NtpBackgroundServiceTest : public testing::Test,
-                                 public ::testing::WithParamInterface<bool> {
+class NtpBackgroundServiceTest : public testing::Test {
  public:
   NtpBackgroundServiceTest()
       : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP),
         test_shared_loader_factory_(
             base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
                 &test_url_loader_factory_)) {
-    feature_list_.InitWithFeatureState(
-        std::move(ntp_features::kNtpBackgroundImageErrorDetection),
-        BackgroundImageErrorDetectionEnabled());
   }
 
   void TearDown() override {
@@ -95,10 +92,6 @@
     return &test_url_loader_factory_;
   }
 
-  // TODO(b:367699101) - Remove test parameters once image error
-  // detection logic is initiated solely in the frontend.
-  bool BackgroundImageErrorDetectionEnabled() const { return GetParam(); }
-
  protected:
   // Required to run tests from UI and threads.
   content::BrowserTaskEnvironment task_environment_;
@@ -111,14 +104,14 @@
   std::unique_ptr<NtpBackgroundService> service_;
 };
 
-INSTANTIATE_TEST_SUITE_P(All, NtpBackgroundServiceTest, ::testing::Bool());
-
-TEST_P(NtpBackgroundServiceTest, CollectionRequest) {
+TEST_F(NtpBackgroundServiceTest, CollectionRequest) {
   g_browser_process->SetApplicationLocale("foo");
-  service()->FetchCollectionInfo();
-  base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(1u, test_url_loader_factory()->pending_requests()->size());
+  service()->FetchCollectionInfo();
+  EXPECT_TRUE(base::test::RunUntil([&]() {
+    return test_url_loader_factory()->pending_requests()->size() == 1u;
+  }));
+
   std::string request_body(test_url_loader_factory()
                                ->pending_requests()
                                ->at(0)
@@ -129,13 +122,7 @@
   ntp::background::GetCollectionsRequest collection_request;
   EXPECT_TRUE(collection_request.ParseFromString(request_body));
   EXPECT_EQ("foo", collection_request.language());
-  if (BackgroundImageErrorDetectionEnabled()) {
-    EXPECT_EQ(5, collection_request.filtering_label_size());
-    EXPECT_EQ("chrome_desktop_ntp.error_detection",
-              collection_request.filtering_label(4));
-  } else {
-    EXPECT_EQ(4, collection_request.filtering_label_size());
-  }
+  EXPECT_EQ(4, collection_request.filtering_label_size());
   EXPECT_EQ("chrome_desktop_ntp", collection_request.filtering_label(0));
   EXPECT_EQ("chrome_desktop_ntp.M" + version_info::GetMajorVersionNumber(),
             collection_request.filtering_label(1));
@@ -144,7 +131,39 @@
   EXPECT_EQ("chrome_desktop_ntp.gm3", collection_request.filtering_label(3));
 }
 
-TEST_P(NtpBackgroundServiceTest, CollectionInfoNetworkError) {
+TEST_F(NtpBackgroundServiceTest,
+       CollectionRequestWithImageErrorDetectionEnabled) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      ntp_features::kNtpBackgroundImageErrorDetection);
+  g_browser_process->SetApplicationLocale("foo");
+
+  service()->FetchCollectionInfo();
+  EXPECT_TRUE(base::test::RunUntil([&]() {
+    return test_url_loader_factory()->pending_requests()->size() == 1u;
+  }));
+
+  std::string request_body(test_url_loader_factory()
+                               ->pending_requests()
+                               ->at(0)
+                               .request.request_body->elements()
+                               ->at(0)
+                               .As<network::DataElementBytes>()
+                               .AsStringPiece());
+  ntp::background::GetCollectionsRequest collection_request;
+  EXPECT_TRUE(collection_request.ParseFromString(request_body));
+  EXPECT_EQ("foo", collection_request.language());
+  EXPECT_EQ(5, collection_request.filtering_label_size());
+  EXPECT_EQ("chrome_desktop_ntp.error_detection",
+            collection_request.filtering_label(4));
+  EXPECT_EQ("chrome_desktop_ntp.M" + version_info::GetMajorVersionNumber(),
+            collection_request.filtering_label(1));
+  EXPECT_EQ("chrome_desktop_ntp.panorama",
+            collection_request.filtering_label(2));
+  EXPECT_EQ("chrome_desktop_ntp.gm3", collection_request.filtering_label(3));
+}
+
+TEST_F(NtpBackgroundServiceTest, CollectionInfoNetworkError) {
   SetUpResponseWithNetworkError(service()->GetCollectionsLoadURLForTesting());
 
   ASSERT_TRUE(service()->collection_info().empty());
@@ -158,7 +177,7 @@
             ErrorType::NET_ERROR);
 }
 
-TEST_P(NtpBackgroundServiceTest, BadCollectionsResponse) {
+TEST_F(NtpBackgroundServiceTest, BadCollectionsResponse) {
   SetUpResponseWithData(service()->GetCollectionsLoadURLForTesting(),
                         "bad serialized GetCollectionsResponse");
 
@@ -173,7 +192,7 @@
             ErrorType::SERVICE_ERROR);
 }
 
-TEST_P(NtpBackgroundServiceTest, GoodCollectionsResponse) {
+TEST_F(NtpBackgroundServiceTest, GoodCollectionsResponse) {
   ntp::background::Collection collection;
   collection.set_collection_id("shapes");
   collection.set_collection_name("Shapes");
@@ -203,7 +222,7 @@
   EXPECT_EQ(service()->collection_error_info().error_type, ErrorType::NONE);
 }
 
-TEST_P(NtpBackgroundServiceTest, BrokenCollectionPreviewImageHasNoReplacement) {
+TEST_F(NtpBackgroundServiceTest, BrokenCollectionPreviewImageHasNoReplacement) {
   ntp::background::Collection collection;
   collection.set_collection_id("shapes");
   collection.set_collection_name("Shapes");
@@ -231,15 +250,9 @@
   service()->FetchReplacementCollectionPreviewImage(
       collection.collection_id(), std::move(replacement_image_callback));
   run_loop.Run();
-
-  histogram_tester_.ExpectTotalCount(
-      "NewTabPage.BackgroundService.Images.Headers.ErrorDetected", 1);
-  ASSERT_EQ(1, histogram_tester_.GetBucketCount(
-                   "NewTabPage.BackgroundService.Images.Headers.ErrorDetected",
-                   NtpImageType::kCollections));
 }
 
-TEST_P(NtpBackgroundServiceTest, BrokenCollectionPreviewImageHasReplacement) {
+TEST_F(NtpBackgroundServiceTest, BrokenCollectionPreviewImageHasReplacement) {
   ntp::background::Collection collection;
   collection.set_collection_id("shapes");
   collection.set_collection_name("Shapes");
@@ -276,16 +289,9 @@
     service()->FetchReplacementCollectionPreviewImage(
         collection.collection_id(), std::move(replacement_image_callback));
     run_loop.Run();
-
-    histogram_tester_.ExpectTotalCount(
-        "NewTabPage.BackgroundService.Images.Headers.ErrorDetected", 1);
-    ASSERT_EQ(1,
-              histogram_tester_.GetBucketCount(
-                  "NewTabPage.BackgroundService.Images.Headers.ErrorDetected",
-                  NtpImageType::kCollections));
 }
 
-TEST_P(NtpBackgroundServiceTest, CollectionImagesNetworkError) {
+TEST_F(NtpBackgroundServiceTest, CollectionImagesNetworkError) {
   SetUpResponseWithNetworkError(service()->GetImagesURLForTesting());
 
   ASSERT_TRUE(service()->collection_images().empty());
@@ -294,16 +300,12 @@
   service()->FetchCollectionImageInfo("shapes");
   base::RunLoop().RunUntilIdle();
 
-  if (BackgroundImageErrorDetectionEnabled()) {
-    histogram_tester_.ExpectTotalCount(
-        "NewTabPage.BackgroundService.Images.Headers.ErrorDetected", 0);
-  }
   EXPECT_TRUE(service()->collection_images().empty());
   EXPECT_EQ(service()->collection_images_error_info().error_type,
             ErrorType::NET_ERROR);
 }
 
-TEST_P(NtpBackgroundServiceTest, BadCollectionImagesResponse) {
+TEST_F(NtpBackgroundServiceTest, BadCollectionImagesResponse) {
   SetUpResponseWithData(service()->GetImagesURLForTesting(),
                         "bad serialized GetImagesInCollectionResponse");
 
@@ -313,16 +315,12 @@
   service()->FetchCollectionImageInfo("shapes");
   base::RunLoop().RunUntilIdle();
 
-  if (BackgroundImageErrorDetectionEnabled()) {
-    histogram_tester_.ExpectTotalCount(
-        "NewTabPage.BackgroundService.Images.Headers.ErrorDetected", 0);
-  }
   EXPECT_TRUE(service()->collection_images().empty());
   EXPECT_EQ(service()->collection_images_error_info().error_type,
             ErrorType::SERVICE_ERROR);
 }
 
-TEST_P(NtpBackgroundServiceTest, ImageInCollectionHasNetworkError) {
+TEST_F(NtpBackgroundServiceTest, ImageInCollectionHasNetworkError) {
   ntp::background::Image image;
   image.set_asset_id(12345);
   image.set_image_url(kTestImageUrl);
@@ -357,7 +355,7 @@
               ErrorType::NONE);
 }
 
-TEST_P(NtpBackgroundServiceTest, GoodCollectionImagesResponse) {
+TEST_F(NtpBackgroundServiceTest, GoodCollectionImagesResponse) {
   ntp::background::Image image;
   image.set_asset_id(12345);
   image.set_image_url(kTestImageUrl);
@@ -386,17 +384,13 @@
   collection_image.attribution.push_back(image.attribution(0).text());
   collection_image.attribution_action_url = GURL(image.action_url());
 
-  if (BackgroundImageErrorDetectionEnabled()) {
-    histogram_tester_.ExpectTotalCount(
-        "NewTabPage.BackgroundService.Images.Headers.ErrorDetected", 0);
-  }
   EXPECT_FALSE(service()->collection_images().empty());
   EXPECT_THAT(service()->collection_images().at(0), Eq(collection_image));
   EXPECT_EQ(service()->collection_images_error_info().error_type,
             ErrorType::NONE);
 }
 
-TEST_P(NtpBackgroundServiceTest,
+TEST_F(NtpBackgroundServiceTest,
        CollectionImageInfoRequestsAreIgnoredIfAnotherIsInProgress) {
   ntp::background::Collection collection;
   collection.set_collection_id("shapes");
@@ -447,17 +441,13 @@
       GURL(image.image_url() + service()->GetImageOptionsForTesting());
   collection_image.attribution.push_back(image.attribution(0).text());
 
-  if (BackgroundImageErrorDetectionEnabled()) {
-    histogram_tester_.ExpectTotalCount(
-        "NewTabPage.BackgroundService.Images.Headers.ErrorDetected", 0);
-  }
   EXPECT_FALSE(service()->collection_info().empty());
   EXPECT_THAT(service()->collection_info().at(0), Eq(collection_info));
   EXPECT_FALSE(service()->collection_images().empty());
   EXPECT_THAT(service()->collection_images().at(0), Eq(collection_image));
 }
 
-TEST_P(NtpBackgroundServiceTest,
+TEST_F(NtpBackgroundServiceTest,
        CollectionImageInfoCanBeSuccessfullyFetchedMultipleTimes) {
   ntp::background::Image image;
   image.set_image_url(kTestImageUrl);
@@ -482,7 +472,7 @@
   EXPECT_THAT(service()->collection_images().at(0).collection_id, "colors");
 }
 
-TEST_P(NtpBackgroundServiceTest, NextImageNetworkError) {
+TEST_F(NtpBackgroundServiceTest, NextImageNetworkError) {
   SetUpResponseWithNetworkError(service()->GetNextImageURLForTesting());
 
   service()->FetchNextCollectionImage("shapes", std::nullopt);
@@ -492,7 +482,7 @@
               Eq(ErrorType::NET_ERROR));
 }
 
-TEST_P(NtpBackgroundServiceTest, BadNextImageResponse) {
+TEST_F(NtpBackgroundServiceTest, BadNextImageResponse) {
   SetUpResponseWithData(service()->GetNextImageURLForTesting(),
                         "bad serialized GetImageFromCollectionResponse");
 
@@ -503,7 +493,7 @@
               Eq(ErrorType::SERVICE_ERROR));
 }
 
-TEST_P(NtpBackgroundServiceTest, GoodNextImageResponse) {
+TEST_F(NtpBackgroundServiceTest, GoodNextImageResponse) {
   ntp::background::Image image;
   image.set_asset_id(12345);
   image.set_image_url(kTestImageUrl);
@@ -540,7 +530,7 @@
               Eq(ErrorType::NONE));
 }
 
-TEST_P(NtpBackgroundServiceTest, MultipleRequestsNextImage) {
+TEST_F(NtpBackgroundServiceTest, MultipleRequestsNextImage) {
   ntp::background::Image image;
   image.set_asset_id(12345);
   image.set_image_url(kTestImageUrl);
@@ -580,7 +570,7 @@
               Eq(ErrorType::NONE));
 }
 
-TEST_P(NtpBackgroundServiceTest, CheckValidAndInvalidBackdropUrls) {
+TEST_F(NtpBackgroundServiceTest, CheckValidAndInvalidBackdropUrls) {
   ntp::background::Image image;
   image.set_asset_id(12345);
   image.set_image_url(kTestImageUrl);
@@ -599,10 +589,6 @@
   service()->FetchCollectionImageInfo("shapes");
   base::RunLoop().RunUntilIdle();
 
-  if (BackgroundImageErrorDetectionEnabled()) {
-    histogram_tester_.ExpectTotalCount(
-        "NewTabPage.BackgroundService.Images.Headers.ErrorDetected", 0);
-  }
   EXPECT_TRUE(service()->IsValidBackdropUrl(
       GURL(image.image_url() + service()->GetImageOptionsForTesting())));
   EXPECT_FALSE(service()->IsValidBackdropUrl(
@@ -611,7 +597,7 @@
       GURL("https://wallpapers.co/another_image")));
 }
 
-TEST_P(NtpBackgroundServiceTest, GetThumbnailUrl) {
+TEST_F(NtpBackgroundServiceTest, GetThumbnailUrl) {
   const GURL kInvalidUrl("foo");
   const GURL kValidUrl("https://www.foo.com");
   const GURL kValidThumbnailUrl("https://www.foo.com/thumbnail");
@@ -622,7 +608,7 @@
   EXPECT_EQ(GURL(), service()->GetThumbnailUrl(kInvalidUrl));
 }
 
-TEST_P(NtpBackgroundServiceTest, OverrideBaseUrl) {
+TEST_F(NtpBackgroundServiceTest, OverrideBaseUrl) {
   base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
       "collections-base-url", "https://foo.com");
   service()->FetchCollectionInfo();
@@ -633,7 +619,7 @@
             test_url_loader_factory()->pending_requests()->at(0).request.url);
 }
 
-TEST_P(NtpBackgroundServiceTest, VerifyURLMetricsWithNetworkSuccess) {
+TEST_F(NtpBackgroundServiceTest, VerifyURLMetricsWithNetworkSuccess) {
   SetUpResponseWithNetworkSuccess(GURL(kTestImageUrl));
   histogram_tester_.ExpectTotalCount(
       "NewTabPage.BackgroundService.Images.Headers.RequestLatency", 0);
@@ -659,7 +645,7 @@
                    net::HTTP_OK));
 }
 
-TEST_P(NtpBackgroundServiceTest, VerifyURLMetricsWithNetworkError) {
+TEST_F(NtpBackgroundServiceTest, VerifyURLMetricsWithNetworkError) {
   SetUpResponseWithNetworkError(GURL(kTestImageUrl));
   histogram_tester_.ExpectTotalCount(
       "NewTabPage.BackgroundService.Images.Headers.RequestLatency", 0);
diff --git a/chrome/browser/search_engine_choice/OWNERS b/chrome/browser/search_engine_choice/OWNERS
index 5d7e015..c7aa42b 100644
--- a/chrome/browser/search_engine_choice/OWNERS
+++ b/chrome/browser/search_engine_choice/OWNERS
@@ -1 +1 @@
-file://components/search_engines/search_engine_choice/OWNERS
\ No newline at end of file
+file://components/search_engines/search_engine_choice/COMPLIANCE_OWNERS
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java
index dc2def9..7dfe52e 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java
@@ -128,13 +128,11 @@
                         if (bottomSheet == mBottomSheet) {
                             mBottomSheet
                                     .getContentView()
-                                    .addOnLayoutChangeListener(
-                                            ShareSheetCoordinator.this::onLayoutChange);
+                                    .addOnLayoutChangeListener(ShareSheetCoordinator.this);
                         } else {
                             mBottomSheet
                                     .getContentView()
-                                    .removeOnLayoutChangeListener(
-                                            ShareSheetCoordinator.this::onLayoutChange);
+                                    .removeOnLayoutChangeListener(ShareSheetCoordinator.this);
                         }
                     }
                 };
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/page_info_sheet/PageInfoSharingControllerUnitTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/page_info_sheet/PageInfoSharingControllerUnitTest.java
index b2ab65f8..0a62f9b 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/page_info_sheet/PageInfoSharingControllerUnitTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/page_info_sheet/PageInfoSharingControllerUnitTest.java
@@ -883,7 +883,9 @@
                                     .findViewById(R.id.learn_more_text);
                     var learnMoreTextLinks = learnMoreText.getClickableSpans();
                     assertNotEquals(
-                            "TextView should contain clickable spans", 0, learnMoreTextLinks);
+                            "TextView should contain clickable spans",
+                            0,
+                            learnMoreTextLinks.length);
                     // Click first span, which should contain a "learn more" text and link to a web
                     // page.
                     learnMoreTextLinks[0].onClick(learnMoreText);
diff --git a/chrome/browser/signin/BUILD.gn b/chrome/browser/signin/BUILD.gn
index acf1538..71997ec 100644
--- a/chrome/browser/signin/BUILD.gn
+++ b/chrome/browser/signin/BUILD.gn
@@ -61,6 +61,7 @@
       "//chrome/browser/search_engines",
       "//chrome/browser/themes",
       "//chrome/browser/ui:browser_list",
+      "//chrome/browser/ui:ui_features",
       "//chrome/browser/ui/signin",
       "//chrome/common:channel_info",
       "//chrome/common:constants",
diff --git a/chrome/browser/signin/dice_web_signin_interceptor.cc b/chrome/browser/signin/dice_web_signin_interceptor.cc
index 9a722ea..0db8385 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor.cc
@@ -51,6 +51,7 @@
 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
 #include "chrome/browser/ui/profiles/profile_colors_util.h"
 #include "chrome/browser/ui/signin/dice_web_signin_interceptor_delegate.h"
+#include "chrome/browser/ui/ui_features.h"
 #include "chrome/common/channel_info.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/themes/autogenerated_theme_util.h"
@@ -788,7 +789,7 @@
   DCHECK(IsRequiredExtendedAccountInfoAvailable(intercepted_account_info));
 
   if (!base::FeatureList::IsEnabled(
-          kShowEnterpriseDialogForAllManagedAccountsSignin)) {
+          features::kEnterpriseUpdatedProfileCreationScreen)) {
     return false;
   }
 
@@ -800,11 +801,16 @@
     return false;
   }
 
-  // Check if the intercepted account is managed.
-  if (intercepted_account_info.IsManaged() &&
+  // Check if the intercepted account is managed and has not yet accepted
+  // management.
+  if (!intercepted_account_info.IsManaged() ||
+      enterprise_util::UserAcceptedAccountManagement(profile_)) {
+    return false;
+  }
+
+  if (switches::IsImprovedSigninUIOnDesktopEnabled() ||
       IsPrimaryAccountInterception(intercepted_account_info.account_id,
-                                   identity_manager_) &&
-      !enterprise_util::UserAcceptedAccountManagement(profile_)) {
+                                   identity_manager_)) {
     return true;
   }
 
@@ -1375,13 +1381,16 @@
   } else {
     DCHECK_EQ(SigninInterceptionResult::kDeclined, create)
         << "The user can only accept or decline";
+    if (state_->interception_type_ ==
+        WebSigninInterceptor::SigninInterceptionType::kEnterpriseForced) {
+      auto* accounts_mutator = identity_manager_->GetAccountsMutator();
+      accounts_mutator->RemoveAccount(
+          account_info.account_id,
+          signin_metrics::SourceForRefreshTokenOperation::
+              kEnterpriseForcedProfileCreation_UserDecline);
+    }
     OnProfileCreationChoice(account_info, profile_color,
                             SigninInterceptionResult::kDeclined);
-    auto* accounts_mutator = identity_manager_->GetAccountsMutator();
-    accounts_mutator->RemoveAccount(
-        account_info.account_id,
-        signin_metrics::SourceForRefreshTokenOperation::
-            kTurnOnSyncHelper_Abort);
   }
 }
 
diff --git a/chrome/browser/signin/dice_web_signin_interceptor_unittest.cc b/chrome/browser/signin/dice_web_signin_interceptor_unittest.cc
index 46d739ac..9487c1e 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor_unittest.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor_unittest.cc
@@ -26,6 +26,7 @@
 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
 #include "chrome/browser/signin/web_signin_interceptor.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/ui_features.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
@@ -495,7 +496,7 @@
        NoForcedInterceptionShowsDialogIfFeatureEnabled) {
   base::test::ScopedFeatureList scoped_list;
   scoped_list.InitAndEnableFeature(
-      kShowEnterpriseDialogForAllManagedAccountsSignin);
+      features::kEnterpriseUpdatedProfileCreationScreen);
   // Reauth intercepted if enterprise confirmation not shown yet for forced
   // managed separation.
   AccountInfo account_info = identity_test_env()->MakePrimaryAccountAvailable(
@@ -523,7 +524,7 @@
     NoForcedInterceptionShowsNoDialogIfFeatureEnabledButDisabledDialogByPolicy) {
   base::test::ScopedFeatureList scoped_list;
   scoped_list.InitAndEnableFeature(
-      kShowEnterpriseDialogForAllManagedAccountsSignin);
+      features::kEnterpriseUpdatedProfileCreationScreen);
   // Reauth intercepted if enterprise confirmation not shown yet for forced
   // managed separation.
   AccountInfo account_info = identity_test_env()->MakePrimaryAccountAvailable(
diff --git a/chrome/browser/site_protection/site_familiarity_heuristic_name.h b/chrome/browser/site_protection/site_familiarity_heuristic_name.h
index 09ba54d..575bd5a8 100644
--- a/chrome/browser/site_protection/site_familiarity_heuristic_name.h
+++ b/chrome/browser/site_protection/site_familiarity_heuristic_name.h
@@ -22,7 +22,8 @@
   kSiteEngagementScoreExists = 7,
   kNoVisitsToAnySiteMoreThanADayAgo = 8,
   kGlobalAllowlistNotReady = 9,
-  kMaxValue = kGlobalAllowlistNotReady,
+  kFamiliarLikelyPreviouslyUnfamiliar = 10,
+  kMaxValue = kFamiliarLikelyPreviouslyUnfamiliar,
 };
 
 // Subset of SiteFamiliarityHeuristicName for heuristics related to navigation
@@ -32,6 +33,7 @@
   kVisitedMoreThanADayAgo = 1,
   kVisitedMoreThanFourHoursAgo = 2,
   kNoVisitsToAnySiteMoreThanADayAgo = 3,
+  kVisitedMoreThanADayAgoPreviouslyUnfamiliar = 4,
 };
 
 }  // namespace site_protection
diff --git a/chrome/browser/site_protection/site_protection_metrics_observer.cc b/chrome/browser/site_protection/site_protection_metrics_observer.cc
index db67e7e..51c5bd0f 100644
--- a/chrome/browser/site_protection/site_protection_metrics_observer.cc
+++ b/chrome/browser/site_protection/site_protection_metrics_observer.cc
@@ -140,6 +140,7 @@
         SiteFamiliarityHeuristicName::kVisitedMoreThanFourHoursAgo);
     metrics_data->most_strict_matched_history_heuristic =
         SiteFamiliarityHistoryHeuristicName::kVisitedMoreThanFourHoursAgo;
+    metrics_data->last_visit_time = last_visit_result.last_visit;
 
     if (last_visit_result.last_visit < (base::Time::Now() - base::Days(1))) {
       OnGotVisitToOriginOlderThanADayAgo(std::move(metrics_data),
@@ -165,6 +166,7 @@
         SiteFamiliarityHeuristicName::kVisitedMoreThanADayAgo);
     metrics_data->most_strict_matched_history_heuristic =
         SiteFamiliarityHistoryHeuristicName::kVisitedMoreThanADayAgo;
+    metrics_data->last_visit_time = last_visit_result.last_visit;
     OnKnowIfAnyVisitOlderThanADayAgo(std::move(metrics_data),
                                      /*has_visit_older_than_a_day_ago=*/true);
     return;
@@ -204,18 +206,20 @@
       GURL last_committed_url = metrics_data->last_committed_url;
       database_manager->CheckUrlForHighConfidenceAllowlist(
           last_committed_url,
-          base::BindOnce(&SiteProtectionMetricsObserver::LogMetrics,
+          base::BindOnce(&SiteProtectionMetricsObserver::
+                             OnGotHighConfidenceAllowlistResult,
                          weak_factory_.GetWeakPtr(), std::move(metrics_data)));
       return;
     }
   }
 
-  LogMetrics(std::move(metrics_data),
-             /*url_on_safe_browsing_high_confidence_allowlist=*/false,
-             /*logging_details=*/std::nullopt);
+  OnGotHighConfidenceAllowlistResult(
+      std::move(metrics_data),
+      /*url_on_safe_browsing_high_confidence_allowlist=*/false,
+      /*logging_details=*/std::nullopt);
 }
 
-void SiteProtectionMetricsObserver::LogMetrics(
+void SiteProtectionMetricsObserver::OnGotHighConfidenceAllowlistResult(
     std::unique_ptr<MetricsData> metrics_data,
     bool url_on_safe_browsing_high_confidence_allowlist,
     std::optional<safe_browsing::SafeBrowsingDatabaseManager::
@@ -228,10 +232,95 @@
     url_on_safe_browsing_high_confidence_allowlist = false;
   }
   if (url_on_safe_browsing_high_confidence_allowlist) {
+    metrics_data->url_on_safe_browsing_high_confidence_allowlist = true;
     metrics_data->matched_heuristics.push_back(
         SiteFamiliarityHeuristicName::kGlobalAllowlistMatch);
   }
 
+  // Guess as to whether the site was previously categorized as unfamiliar.
+  //
+  // For the purpose of
+  // SiteFamiliarityHeuristicName::kFamiliarLikelyPreviouslyUnfamiliar an
+  // unfamiliar site is a site which is:
+  // - Not on the safe browsing high confidence allowlist
+  // AND
+  // - Wasn't visited more than 24 hours ago
+  // AND
+  // - Wasn't visited with a fresh profile which doesn't have any history
+  //   older than 24 hours.
+  //
+  // Assume that high confidence allowlist is stable and that if origin is
+  // currently on high confidence allowlist that it would have been previously
+  // on high confidence allowlist. Ignore site engagement score. Ignoring
+  // site engagement score is ok because the site engagement score is capped
+  // for site engagement all on the same day.
+  std::optional<base::Time> last_visit_time = metrics_data->last_visit_time;
+  if (!url_on_safe_browsing_high_confidence_allowlist && last_visit_time &&
+      *last_visit_time < (base::Time::Now() - base::Days(1))) {
+    url::Origin last_committed_origin = metrics_data->last_committed_origin;
+    history_service_->GetLastVisitToOrigin(
+        last_committed_origin, base::Time(), *last_visit_time - base::Days(1),
+        base::BindOnce(&SiteProtectionMetricsObserver::
+                           OnGotVisitToOriginOlderThanADayPriorToPreviousVisit,
+                       weak_factory_.GetWeakPtr(), std::move(metrics_data)),
+        &task_tracker_);
+    return;
+  }
+
+  LogMetrics(std::move(metrics_data));
+}
+
+void SiteProtectionMetricsObserver::
+    OnGotVisitToOriginOlderThanADayPriorToPreviousVisit(
+        std::unique_ptr<MetricsData> metrics_data,
+        history::HistoryLastVisitResult last_visit_result) {
+  if (!last_visit_result.success || last_visit_result.last_visit.is_null()) {
+    // Check whether
+    // SiteFamiliarityHistoryHeuristicName::kNoVisitsToAnySiteMoreThanADayAgo
+    // heuristic would have matched the previous visit.
+    history::QueryOptions history_query_options;
+    history_query_options.end_time =
+        *metrics_data->last_visit_time - base::Days(1);
+    history_query_options.max_count = 1;
+    history_service_->QueryHistory(
+        u"", std::move(history_query_options),
+        base::BindOnce(&SiteProtectionMetricsObserver::
+                           OnGotVisitOlderThanADayPriorToPreviousVisit,
+                       weak_factory_.GetWeakPtr(), std::move(metrics_data)),
+        &task_tracker_);
+    return;
+  }
+
+  OnKnowIfSiteWasLikelyPreviouslyFamiliar(
+      std::move(metrics_data),
+      /*was_site_likely_previously_familiar=*/true);
+  return;
+}
+
+void SiteProtectionMetricsObserver::OnGotVisitOlderThanADayPriorToPreviousVisit(
+    std::unique_ptr<MetricsData> metrics_data,
+    history::QueryResults query_results) {
+  OnKnowIfSiteWasLikelyPreviouslyFamiliar(
+      std::move(metrics_data),
+      /*was_site_likely_previously_familiar=*/query_results.empty());
+}
+
+void SiteProtectionMetricsObserver::OnKnowIfSiteWasLikelyPreviouslyFamiliar(
+    std::unique_ptr<MetricsData> metrics_data,
+    bool was_site_likely_previously_familiar) {
+  if (!was_site_likely_previously_familiar) {
+    metrics_data->matched_heuristics.push_back(
+        SiteFamiliarityHeuristicName::kFamiliarLikelyPreviouslyUnfamiliar);
+    metrics_data->most_strict_matched_history_heuristic =
+        SiteFamiliarityHistoryHeuristicName::
+            kVisitedMoreThanADayAgoPreviouslyUnfamiliar;
+  }
+
+  LogMetrics(std::move(metrics_data));
+}
+
+void SiteProtectionMetricsObserver::LogMetrics(
+    std::unique_ptr<MetricsData> metrics_data) {
   bool no_heuristics_match = metrics_data->matched_heuristics.empty();
   if (no_heuristics_match) {
     metrics_data->matched_heuristics.push_back(
@@ -257,7 +346,7 @@
   ukm::builders::SiteFamiliarityHeuristicResult(metrics_data->ukm_source_id)
       .SetAnyHeuristicsMatch(!no_heuristics_match)
       .SetOnHighConfidenceAllowlist(
-          url_on_safe_browsing_high_confidence_allowlist)
+          metrics_data->url_on_safe_browsing_high_confidence_allowlist)
       .SetSiteEngagementScore(
           RoundSiteEngagementScoreForUkm(metrics_data->site_engagement_score))
       .SetSiteFamiliarityHistoryHeuristic(
diff --git a/chrome/browser/site_protection/site_protection_metrics_observer.h b/chrome/browser/site_protection/site_protection_metrics_observer.h
index 9d61562..3731a81 100644
--- a/chrome/browser/site_protection/site_protection_metrics_observer.h
+++ b/chrome/browser/site_protection/site_protection_metrics_observer.h
@@ -72,9 +72,11 @@
 
     ukm::SourceId ukm_source_id = ukm::kInvalidSourceId;
     double site_engagement_score = 0;
+    bool url_on_safe_browsing_high_confidence_allowlist = false;
     GURL last_committed_url;
     url::Origin last_committed_origin;
     base::Time data_fetch_start_time;
+    std::optional<base::Time> last_visit_time;
     std::vector<SiteFamiliarityHeuristicName> matched_heuristics;
     SiteFamiliarityHistoryHeuristicName most_strict_matched_history_heuristic =
         SiteFamiliarityHistoryHeuristicName::kNoHeuristicMatch;
@@ -103,11 +105,33 @@
       std::unique_ptr<MetricsData> metrics_data,
       bool has_visit_older_than_a_day_ago);
 
-  void LogMetrics(std::unique_ptr<MetricsData> metrics_data,
-                  bool url_on_safe_browsing_high_confidence_allowlist,
-                  std::optional<safe_browsing::SafeBrowsingDatabaseManager::
-                                    HighConfidenceAllowlistCheckLoggingDetails>
-                      logging_details);
+  // Called with whether the site is on the high confidence allowlist.
+  void OnGotHighConfidenceAllowlistResult(
+      std::unique_ptr<MetricsData> metrics_data,
+      bool url_on_safe_browsing_high_confidence_allowlist,
+      std::optional<safe_browsing::SafeBrowsingDatabaseManager::
+                        HighConfidenceAllowlistCheckLoggingDetails>
+          logging_details);
+
+  // Called with the history visit to the origin in `metrics_data` which
+  // occurred more than a day prior to the most recent visit to the origin.
+  void OnGotVisitToOriginOlderThanADayPriorToPreviousVisit(
+      std::unique_ptr<MetricsData> metrics_data,
+      history::HistoryLastVisitResult last_visit_result);
+
+  // Called with the history visit to any site which occurred more than a day
+  // prior to the visit to the origin in `metrics_data`.
+  void OnGotVisitOlderThanADayPriorToPreviousVisit(
+      std::unique_ptr<MetricsData> metrics_data,
+      history::QueryResults query_results);
+
+  // Called with whether there is a history visit to any site more than a day
+  // prior to the visit to the origin in `metrics_data`.
+  void OnKnowIfSiteWasLikelyPreviouslyFamiliar(
+      std::unique_ptr<MetricsData> metrics_data,
+      bool was_site_likely_previously_familiar);
+
+  void LogMetrics(std::unique_ptr<MetricsData> metrics_data);
 
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 
diff --git a/chrome/browser/site_protection/site_protection_metrics_observer_unittest.cc b/chrome/browser/site_protection/site_protection_metrics_observer_unittest.cc
index 116be2d0d..84a04845 100644
--- a/chrome/browser/site_protection/site_protection_metrics_observer_unittest.cc
+++ b/chrome/browser/site_protection/site_protection_metrics_observer_unittest.cc
@@ -184,6 +184,12 @@
     return values.size() == 1u ? values[0] : -1;
   }
 
+  int64_t GetUkmHistoryFamiliarityHeuristicValue(
+      ukm::TestUkmRecorder& ukm_recorder) {
+    return GetUkmFamiliarityHeuristicValue(ukm_recorder,
+                                           "SiteFamiliarityHistoryHeuristic");
+  }
+
   void NavigateAndCheckRecordedHeuristicUkm(const GURL& url,
                                             const std::string& metric_name,
                                             int64_t expected_value) {
@@ -222,8 +228,7 @@
       {SiteFamiliarityHeuristicName::kNoVisitsToAnySiteMoreThanADayAgo});
   EXPECT_EQ(static_cast<int>(SiteFamiliarityHistoryHeuristicName::
                                  kNoVisitsToAnySiteMoreThanADayAgo),
-            GetUkmFamiliarityHeuristicValue(ukm_recorder,
-                                            "SiteFamiliarityHistoryHeuristic"));
+            GetUkmHistoryFamiliarityHeuristicValue(ukm_recorder));
 }
 
 // Test the histograms and UKM which are logged by SiteProtectionMetricsObserver
@@ -250,8 +255,7 @@
          SiteFamiliarityHeuristicName::kVisitedMoreThanADayAgo});
     EXPECT_EQ(static_cast<int>(
                   SiteFamiliarityHistoryHeuristicName::kVisitedMoreThanADayAgo),
-              GetUkmFamiliarityHeuristicValue(
-                  ukm_recorder, "SiteFamiliarityHistoryHeuristic"));
+              GetUkmHistoryFamiliarityHeuristicValue(ukm_recorder));
   }
 
   {
@@ -262,8 +266,7 @@
     EXPECT_EQ(
         static_cast<int>(
             SiteFamiliarityHistoryHeuristicName::kVisitedMoreThanFourHoursAgo),
-        GetUkmFamiliarityHeuristicValue(ukm_recorder,
-                                        "SiteFamiliarityHistoryHeuristic"));
+        GetUkmHistoryFamiliarityHeuristicValue(ukm_recorder));
   }
 
   {
@@ -272,8 +275,7 @@
         kUrlVisited1HourAgo, {SiteFamiliarityHeuristicName::kNoHeuristicMatch});
     EXPECT_EQ(static_cast<int>(
                   SiteFamiliarityHistoryHeuristicName::kNoHeuristicMatch),
-              GetUkmFamiliarityHeuristicValue(
-                  ukm_recorder, "SiteFamiliarityHistoryHeuristic"));
+              GetUkmHistoryFamiliarityHeuristicValue(ukm_recorder));
   }
 }
 
@@ -383,6 +385,87 @@
   }
 }
 
+// Test that SiteProtectionMetricsObserver logs the correct histograms and UKM
+// if the SiteFamiliarityHeuristicName::kVisitedMoreThanADayAgo heuristic
+// applies for the current visit to the site but the heuristic did not apply
+// to the previous visit to the site.
+TEST_F(SiteProtectionMetricsObserverTest, SiteFamiliarWasPreviouslyUnfamiliar) {
+  GURL kPageUrl("https://bar.com");
+  GURL kOtherUrl("https://baz.com");
+
+  base::Time now = base::Time::Now();
+  base::Time kPageVisitTime1 = now - base::Hours(25);
+  base::Time kPageVisitTime2 = kPageVisitTime1 - base::Hours(25);
+  base::Time kOtherVisitTime = kPageVisitTime2 - base::Hours(25);
+
+  GetRegularProfileHistoryService()->AddPage(kPageUrl, kPageVisitTime1,
+                                             history::SOURCE_BROWSED);
+  GetRegularProfileHistoryService()->AddPage(kOtherUrl, kOtherVisitTime,
+                                             history::SOURCE_BROWSED);
+  {
+    ukm::TestAutoSetUkmRecorder ukm_recorder;
+    NavigateAndCheckRecordedHeuristicHistograms(
+        kPageUrl,
+        {SiteFamiliarityHeuristicName::kVisitedMoreThanFourHoursAgo,
+         SiteFamiliarityHeuristicName::kVisitedMoreThanADayAgo,
+         SiteFamiliarityHeuristicName::kFamiliarLikelyPreviouslyUnfamiliar});
+    EXPECT_EQ(static_cast<int>(SiteFamiliarityHistoryHeuristicName::
+                                   kVisitedMoreThanADayAgoPreviouslyUnfamiliar),
+              GetUkmHistoryFamiliarityHeuristicValue(ukm_recorder));
+  }
+
+  GetRegularProfileHistoryService()->AddPage(kPageUrl, kPageVisitTime2,
+                                             history::SOURCE_BROWSED);
+
+  {
+    ukm::TestAutoSetUkmRecorder ukm_recorder;
+    NavigateAndCheckRecordedHeuristicHistograms(
+        kPageUrl, {SiteFamiliarityHeuristicName::kVisitedMoreThanFourHoursAgo,
+                   SiteFamiliarityHeuristicName::kVisitedMoreThanADayAgo});
+    EXPECT_EQ(static_cast<int>(
+                  SiteFamiliarityHistoryHeuristicName::kVisitedMoreThanADayAgo),
+              GetUkmHistoryFamiliarityHeuristicValue(ukm_recorder));
+  }
+}
+
+// Test that SiteProtectionMetricsObserver logs the correct histograms
+// if:
+// - SiteFamiliarityHeuristicName::kVisitedMoreThanADayAgo heuristic
+//   applies for the current visit.
+// AND
+// -  SiteFamiliarityHeuristicName::kVisitedMoreThanADayAgo heuristic
+//   does not apply to the previous visit.
+// AND
+// - SiteFamiliarityHeuristicName::kNoVisitsToAnySiteMoreThanADayAgo
+//   applies to the previous visit.
+TEST_F(SiteProtectionMetricsObserverTest,
+       SiteFamiliarWasPreviouslyUnfamiliarDueToNoOldHistory) {
+  GURL kPageUrl("https://bar.com");
+  GURL kOtherUrl("https://baz.com");
+
+  base::Time now = base::Time::Now();
+  base::Time kPageVisitTime = now - base::Hours(25);
+  base::Time kOtherVisitTime1 = kPageVisitTime - base::Hours(1);
+  base::Time kOtherVisitTime2 = kOtherVisitTime1 - base::Hours(100);
+
+  GetRegularProfileHistoryService()->AddPage(kPageUrl, kPageVisitTime,
+                                             history::SOURCE_BROWSED);
+  GetRegularProfileHistoryService()->AddPage(kOtherUrl, kOtherVisitTime1,
+                                             history::SOURCE_BROWSED);
+
+  NavigateAndCheckRecordedHeuristicHistograms(
+      kPageUrl, {SiteFamiliarityHeuristicName::kVisitedMoreThanFourHoursAgo,
+                 SiteFamiliarityHeuristicName::kVisitedMoreThanADayAgo});
+
+  GetRegularProfileHistoryService()->AddPage(kOtherUrl, kOtherVisitTime2,
+                                             history::SOURCE_BROWSED);
+  NavigateAndCheckRecordedHeuristicHistograms(
+      kPageUrl,
+      {SiteFamiliarityHeuristicName::kVisitedMoreThanFourHoursAgo,
+       SiteFamiliarityHeuristicName::kVisitedMoreThanADayAgo,
+       SiteFamiliarityHeuristicName::kFamiliarLikelyPreviouslyUnfamiliar});
+}
+
 // Test that SiteProtectionMetricsObserver logs whether any heuristics matched
 // to UKM.
 TEST_F(SiteProtectionMetricsObserverTest, AnyHeuristicsMatchUkm) {
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 49e1183..3bc27ca 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
@@ -557,11 +557,10 @@
     public static void performStorageMaintenance(List<Integer> liveTabIds) {
         ThreadUtils.assertOnUiThread();
         for (Class<? extends PersistedTabData> clazz : sSupportedMaintenanceClasses) {
+            // Maintenance is supported only for regular Tabs.
+            boolean isEncrypted = false;
             PersistedTabDataConfiguration config =
-                    PersistedTabDataConfiguration.get(
-                            clazz, false
-                            /** Maintenance is only supported for regular Tabs */
-                            );
+                    PersistedTabDataConfiguration.get(clazz, isEncrypted);
             PersistedTabDataStorage storage = config.getStorage();
             storage.performMaintenance(liveTabIds, config.getId());
         }
diff --git a/chrome/browser/thumbnail/generator/android/java/src/org/chromium/chrome/browser/thumbnail/generator/ThumbnailMediaParserTest.java b/chrome/browser/thumbnail/generator/android/java/src/org/chromium/chrome/browser/thumbnail/generator/ThumbnailMediaParserTest.java
index aef2781..f0692db 100644
--- a/chrome/browser/thumbnail/generator/android/java/src/org/chromium/chrome/browser/thumbnail/generator/ThumbnailMediaParserTest.java
+++ b/chrome/browser/thumbnail/generator/android/java/src/org/chromium/chrome/browser/thumbnail/generator/ThumbnailMediaParserTest.java
@@ -73,29 +73,21 @@
         return result;
     }
 
+    /** Verify that the metadata from audio file can be retrieved correctly. */
     @Test
     @LargeTest
     @Feature({"MediaParser"})
-    /**
-     * Verify that the metadata from audio file can be retrieved correctly.
-     *
-     * @throws InterruptedException
-     */
     public void testParseAudioMetatadata() {
         String filePath = UrlUtils.getIsolatedTestRoot() + "/media/test/data/sfx.mp3";
         MediaParserResult result = parseMediaFile(filePath, "audio/mp3");
         Assert.assertTrue("Failed to parse audio metadata.", result.mediaData != null);
     }
 
+    /** Verify metadata and thumbnail can be retrieved correctly from h264 video file. */
     @Test
     @LargeTest
     @Feature({"MediaParser"})
     @Restriction(DeviceFormFactor.PHONE)
-    /**
-     * Verify metadata and thumbnail can be retrieved correctly from h264 video file.
-     *
-     * @throws InterruptedException
-     */
     public void testParseVideoH264() {
         String filePath = UrlUtils.getIsolatedTestRoot() + "/media/test/data/bear.mp4";
         MediaParserResult result = parseMediaFile(filePath, "video/mp4");
@@ -106,14 +98,10 @@
                 "Failed to retrieve thumbnail.", result.mediaData.thumbnail.getHeight() > 0);
     }
 
+    /** Verify metadata and thumbnail can be retrieved correctly from vp8 video file. */
     @Test
     @LargeTest
     @Feature({"MediaParser"})
-    /**
-     * Verify metadata and thumbnail can be retrieved correctly from vp8 video file.
-     *
-     * @throws InterruptedException
-     */
     public void testParseVideoThumbnailVp8() {
         String filePath = UrlUtils.getIsolatedTestRoot() + "/media/test/data/bear-vp8-webvtt.webm";
         MediaParserResult result = parseMediaFile(filePath, "video/webm");
@@ -124,15 +112,13 @@
                 "Failed to retrieve thumbnail.", result.mediaData.thumbnail.getHeight() > 0);
     }
 
-    @Test
-    @LargeTest
-    @Feature({"MediaParser"})
     /**
      * Verify metadata and thumbnail can be retrieved correctly from vp8 video file with alpha
      * plane.
-     *
-     * @throws InterruptedException
      */
+    @Test
+    @LargeTest
+    @Feature({"MediaParser"})
     public void testParseVideoThumbnailVp8WithAlphaPlane() {
         String filePath = UrlUtils.getIsolatedTestRoot() + "/media/test/data/bear-vp8a.webm";
         MediaParserResult result = parseMediaFile(filePath, "video/webm");
@@ -143,14 +129,10 @@
                 "Failed to retrieve thumbnail.", result.mediaData.thumbnail.getHeight() > 0);
     }
 
+    /** Verify metadata and thumbnail can be retrieved correctly from vp9 video file. */
     @Test
     @LargeTest
     @Feature({"MediaParser"})
-    /**
-     * Verify metadata and thumbnail can be retrieved correctly from vp9 video file.
-     *
-     * @throws InterruptedException
-     */
     public void testParseVideoThumbnailVp9() {
         String filePath = UrlUtils.getIsolatedTestRoot() + "/media/test/data/bear-vp9.webm";
         MediaParserResult result = parseMediaFile(filePath, "video/webm");
@@ -161,14 +143,10 @@
                 "Failed to retrieve thumbnail.", result.mediaData.thumbnail.getHeight() > 0);
     }
 
+    /** Verify metadata and thumbnail can be retrieved correctly from av1 video file. */
     @Test
     @LargeTest
     @Feature({"MediaParser"})
-    /**
-     * Verify metadata and thumbnail can be retrieved correctly from av1 video file.
-     *
-     * @throws InterruptedException
-     */
     public void testParseVideoThumbnailAv1() {
         String filePath = UrlUtils.getIsolatedTestRoot() + "/media/test/data/bear-av1.mp4";
         MediaParserResult result = parseMediaFile(filePath, "video/mp4");
@@ -179,14 +157,10 @@
                 "Failed to retrieve thumbnail.", result.mediaData.thumbnail.getHeight() > 0);
     }
 
+    /** Verify metadata and thumbnail can be retrieved correctly from h265 video file. */
     @Test
     @LargeTest
     @Feature({"MediaParser"})
-    /**
-     * Verify metadata and thumbnail can be retrieved correctly from h265 video file.
-     *
-     * @throws InterruptedException
-     */
     public void testParseVideoThumbnailH265() {
         String filePath = UrlUtils.getIsolatedTestRoot() + "/media/test/data/bear-hevc-frag.mp4";
         MediaParserResult result = parseMediaFile(filePath, "video/mp4");
@@ -197,14 +171,10 @@
                 "Failed to retrieve thumbnail.", result.mediaData.thumbnail.getHeight() > 0);
     }
 
+    /** Verify graceful failure on parsing invalid video file. */
     @Test
     @LargeTest
     @Feature({"MediaParser"})
-    /**
-     * Verify graceful failure on parsing invalid video file.
-     *
-     * @throws InterruptedException
-     */
     public void testParseInvalidVideoFile() throws Exception {
         File invalidFile = File.createTempFile("test", "webm");
         MediaParserResult result = parseMediaFile(invalidFile.getAbsolutePath(), "video/webm");
diff --git a/chrome/browser/translate/android/java/src/org/chromium/chrome/browser/translate/AutoTranslateSnackbarControllerJavaTest.java b/chrome/browser/translate/android/java/src/org/chromium/chrome/browser/translate/AutoTranslateSnackbarControllerJavaTest.java
index 869bb359..6ae84ee 100644
--- a/chrome/browser/translate/android/java/src/org/chromium/chrome/browser/translate/AutoTranslateSnackbarControllerJavaTest.java
+++ b/chrome/browser/translate/android/java/src/org/chromium/chrome/browser/translate/AutoTranslateSnackbarControllerJavaTest.java
@@ -87,14 +87,14 @@
         Assert.assertEquals("en", data.getTargetLanguage());
     }
 
-    @Test
-    @SmallTest
     /**
      * The target language is stored in Translate format, which uses the old deprecated Java codes
      * for several languages (Hebrew, Indonesian), and uses "tl" while Chromium uses "fil" for
      * Tagalog/Filipino. This tests that when using Translate format codes the Chrome version is
      * displayed in the Snackbar.
      */
+    @Test
+    @SmallTest
     public void testShowSnackbarChromeLanguage() throws Exception {
         // Use the Translate tag tl which Chrome should display as "Filipino"
         showSnackbar("tl");
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxSuggestionsDropdownEmbedderImplTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxSuggestionsDropdownEmbedderImplTest.java
index 72028a9..014ad6fd 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxSuggestionsDropdownEmbedderImplTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxSuggestionsDropdownEmbedderImplTest.java
@@ -121,6 +121,7 @@
         verify(mViewTreeObserver).removeOnGlobalLayoutListener(mImpl);
     }
 
+    @Test
     public void testRecalculateOmniboxAlignment_phone() {
         doReturn(mAnchorView).when(mHorizontalAlignmentView).getParent();
         doReturn(60).when(mHorizontalAlignmentView).getTop();
@@ -138,6 +139,7 @@
                 alignment);
     }
 
+    @Test
     public void testRecalculateOmniboxAlignment_bottomWindowPadding() {
         mBottomWindowPadding = 40;
         doReturn(mAnchorView).when(mHorizontalAlignmentView).getParent();
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java
index d4568293..b947577 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java
@@ -356,7 +356,8 @@
                 nextSuggestionLogicalIndex++;
             } else {
                 assert false
-                        : "Unsupported group render type: " + currentGroupConfig.getRenderType();
+                        : "Unsupported group render type: "
+                                + currentGroupConfig.getRenderType().name();
             }
 
             previousGroupConfig = currentGroupConfig;
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessorUnitTest.java
index 261e97e6..f99e9839 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessorUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessorUnitTest.java
@@ -339,7 +339,8 @@
         for (AnswerType type : ANSWER_TYPES) {
             SuggestionTestHelper suggHelper = createAnswerSuggestion(type, "", 1, "", 1, null);
             // Note: model is re-created on every iteration.
-            Assert.assertNotNull("No icon associated with type: " + type, suggHelper.getIcon());
+            Assert.assertNotNull(
+                    "No icon associated with type: " + type.name(), suggHelper.getIcon());
         }
     }
 
@@ -348,7 +349,8 @@
         for (AnswerType type : ANSWER_TYPES) {
             SuggestionTestHelper suggHelper = createRichAnswerSuggestion(type, 0, false);
             // Note: model is re-created on every iteration.
-            Assert.assertNotNull("No icon associated with type: " + type, suggHelper.getIcon());
+            Assert.assertNotNull(
+                    "No icon associated with type: " + type.name(), suggHelper.getIcon());
         }
     }
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/DynamicSpacingRecyclerViewItemDecorationUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/DynamicSpacingRecyclerViewItemDecorationUnitTest.java
index 8ca3109e..a4a2f8b 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/DynamicSpacingRecyclerViewItemDecorationUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/DynamicSpacingRecyclerViewItemDecorationUnitTest.java
@@ -223,7 +223,8 @@
 
         // It's unlikely that the minimum spacing would guarantee 2.5 items to be shown, but we can
         // verify this fast.
-        assertNotEquals(CONTAINER_SIZE, LEAD_IN_SPACE + itemWidth * 2.5 + MIN_ELEMENT_SPACE * 2);
+        assertNotEquals(
+                CONTAINER_SIZE, (int) (LEAD_IN_SPACE + itemWidth * 2.5 + MIN_ELEMENT_SPACE * 2));
 
         // However, we don't permit dynamic spacing in landscape mode, so this should fall back to
         // MIN_ELEMENT_SPACE.
diff --git a/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationErrorStateInfo.java b/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationErrorStateInfo.java
index b2a4079..0892f1ed 100644
--- a/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationErrorStateInfo.java
+++ b/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationErrorStateInfo.java
@@ -10,8 +10,8 @@
 import org.jni_zero.JNINamespace;
 import org.jni_zero.JniType;
 
-@JNINamespace("plus_addresses")
 /** Contains necessary information to show a meaningful error message to the user. */
+@JNINamespace("plus_addresses")
 class PlusAddressCreationErrorStateInfo {
     private final @PlusAddressCreationBottomSheetErrorType int mErrorType;
     private final String mTitle;
diff --git a/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationNormalStateInfo.java b/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationNormalStateInfo.java
index 35d26d9..d705d40 100644
--- a/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationNormalStateInfo.java
+++ b/chrome/browser/ui/android/plus_addresses/java/src/org/chromium/chrome/browser/ui/plus_addresses/PlusAddressCreationNormalStateInfo.java
@@ -14,8 +14,8 @@
 
 import java.util.Objects;
 
-@JNINamespace("plus_addresses")
 /** Contains necessary information to show a meaningful error message to the user. */
+@JNINamespace("plus_addresses")
 class PlusAddressCreationNormalStateInfo {
     private final String mTitle;
     private final String mDescription;
diff --git a/chrome/browser/ui/android/searchactivityutils/java/src/org/chromium/chrome/browser/ui/searchactivityutils/SearchActivityPreferencesManagerTest.java b/chrome/browser/ui/android/searchactivityutils/java/src/org/chromium/chrome/browser/ui/searchactivityutils/SearchActivityPreferencesManagerTest.java
index e2955f79..b64af8f 100644
--- a/chrome/browser/ui/android/searchactivityutils/java/src/org/chromium/chrome/browser/ui/searchactivityutils/SearchActivityPreferencesManagerTest.java
+++ b/chrome/browser/ui/android/searchactivityutils/java/src/org/chromium/chrome/browser/ui/searchactivityutils/SearchActivityPreferencesManagerTest.java
@@ -144,8 +144,6 @@
         SearchActivityPreferences p2 =
                 new SearchActivityPreferences(
                         "test", new GURL("https://test.url"), true, true, true);
-        Assert.assertEquals(p1, p1);
-        Assert.assertEquals(p2, p2);
         Assert.assertEquals(p1, p2);
         Assert.assertEquals(p1.hashCode(), p2.hashCode());
 
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/history_sync/HistorySyncTest.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/history_sync/HistorySyncTest.java
index 262d84e..0d9aeb2 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/history_sync/HistorySyncTest.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/history_sync/HistorySyncTest.java
@@ -481,13 +481,13 @@
         histogramWatcher.assertExpected();
     }
 
-    @Test
-    @MediumTest
     /**
      * This tests ensure that onClickListeners are attached to the accept/decline buttons when the
      * HistorySyncCoordinator is created without a view and the MinorModeHelper resolves before a
      * View is set.
      */
+    @Test
+    @MediumTest
     public void testOnClickListenersAttachedWithMinorModeAccount() {
         mSigninTestRule.addAccountThenSignin(AccountManagerTestRule.AADC_MINOR_ACCOUNT);
 
@@ -532,13 +532,13 @@
                 });
     }
 
-    @Test
-    @MediumTest
     /**
      * This tests ensure that onClickListeners are attached to the accept/decline buttons when the
      * HistorySyncCoordinator is created without a view and the MinorModeHelper resolves before a
      * View is set.
      */
+    @Test
+    @MediumTest
     public void testOnClickListenersAttachedWithNonMinorModeAccount() {
         mSigninTestRule.addAccountThenSignin(AccountManagerTestRule.AADC_ADULT_ACCOUNT);
 
diff --git a/chrome/browser/ui/aura/accessibility/automation_manager_aura_browsertest.cc b/chrome/browser/ui/aura/accessibility/automation_manager_aura_browsertest.cc
index a5d25b62..93be4045 100644
--- a/chrome/browser/ui/aura/accessibility/automation_manager_aura_browsertest.cc
+++ b/chrome/browser/ui/aura/accessibility/automation_manager_aura_browsertest.cc
@@ -160,7 +160,8 @@
     }
   }
   void DispatchAccessibilityLocationChange(
-      const ui::AXLocationChanges& details) override {}
+      const ui::AXTreeID& tree_id,
+      const ui::AXLocationChange& details) override {}
   void DispatchTreeDestroyedEvent(ui::AXTreeID tree_id) override {}
   void DispatchActionResult(
       const ui::AXActionData& data,
diff --git a/chrome/browser/ui/fullscreen_util_mac.cc b/chrome/browser/ui/fullscreen_util_mac.cc
index 7a3ad19..b94cff2 100644
--- a/chrome/browser/ui/fullscreen_util_mac.cc
+++ b/chrome/browser/ui/fullscreen_util_mac.cc
@@ -6,6 +6,7 @@
 
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_context.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
@@ -15,12 +16,13 @@
 
 namespace fullscreen_utils {
 
-bool IsInContentFullscreen(Browser* browser) {
-  if (!browser->exclusive_access_manager()) {
+bool IsInContentFullscreen(BrowserWindowInterface* browser_window_interface) {
+  if (!browser_window_interface->GetExclusiveAccessManager()) {
     return false;
   }
-  FullscreenController* controller =
-      browser->exclusive_access_manager()->fullscreen_controller();
+  FullscreenController* const controller =
+      browser_window_interface->GetExclusiveAccessManager()
+          ->fullscreen_controller();
   return controller && (controller->IsWindowFullscreenForTabOrPending() ||
                         controller->IsExtensionFullscreenOrPending());
 }
diff --git a/chrome/browser/ui/fullscreen_util_mac.h b/chrome/browser/ui/fullscreen_util_mac.h
index 1db44d4..96ab162 100644
--- a/chrome/browser/ui/fullscreen_util_mac.h
+++ b/chrome/browser/ui/fullscreen_util_mac.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_UI_FULLSCREEN_UTIL_MAC_H_
 
 class Browser;
+class BrowserWindowInterface;
 
 namespace fullscreen_utils {
 
@@ -13,7 +14,7 @@
 // - `browser` is currently in fullscreen
 // - the fullscreen mode is web or extension API initiated (as opposed to via
 //   macOS affordances like traffic lights
-bool IsInContentFullscreen(Browser* browser);
+bool IsInContentFullscreen(BrowserWindowInterface* browser_window_interface);
 
 // Whether the "Always Show Toolbar in Full Screen" setting is enabled. Properly
 // handles PWAs.
diff --git a/chrome/browser/ui/lens/lens_overlay_controller.cc b/chrome/browser/ui/lens/lens_overlay_controller.cc
index d67fcd0..f4371cb 100644
--- a/chrome/browser/ui/lens/lens_overlay_controller.cc
+++ b/chrome/browser/ui/lens/lens_overlay_controller.cc
@@ -2096,6 +2096,8 @@
     // Add our blur layer to the view.
     overlay_view_->SetPaintToLayer();
     overlay_view_->layer()->Add(lens_overlay_blur_layer_delegate_->layer());
+    overlay_view_->layer()->StackAtBottom(
+        lens_overlay_blur_layer_delegate_->layer());
     lens_overlay_blur_layer_delegate_->layer()->SetBounds(
         overlay_view_->layer()->bounds());
     return;
diff --git a/chrome/browser/ui/search_engine_choice/OWNERS b/chrome/browser/ui/search_engine_choice/OWNERS
index a8ff91d..c7aa42b 100644
--- a/chrome/browser/ui/search_engine_choice/OWNERS
+++ b/chrome/browser/ui/search_engine_choice/OWNERS
@@ -1 +1 @@
-file://components/search_engines/search_engine_choice/OWNERS
+file://components/search_engines/search_engine_choice/COMPLIANCE_OWNERS
diff --git a/chrome/browser/ui/toasts/toast_controller.cc b/chrome/browser/ui/toasts/toast_controller.cc
index 3fd8031..e7c441b 100644
--- a/chrome/browser/ui/toasts/toast_controller.cc
+++ b/chrome/browser/ui/toasts/toast_controller.cc
@@ -36,6 +36,10 @@
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
 
+#if BUILDFLAG(IS_MAC)
+#include "chrome/browser/ui/fullscreen_util_mac.h"
+#endif
+
 ToastParams::ToastParams(ToastId id) : toast_id_(id) {}
 ToastParams::ToastParams(ToastParams&& other) noexcept = default;
 ToastParams& ToastParams::operator=(ToastParams&& other) noexcept = default;
@@ -232,6 +236,18 @@
   }
 }
 
+bool ToastController::ShouldRenderToastOverWebContents() {
+  bool render_in_contents =
+      browser_window_interface_->ShouldHideUIForFullscreen();
+
+#if BUILDFLAG(IS_MAC)
+  render_in_contents |=
+      fullscreen_utils::IsInContentFullscreen(browser_window_interface_);
+#endif
+
+  return render_in_contents;
+}
+
 void ToastController::WebContentsDestroyed() {
   omnibox_helper_observer_.Reset();
   Observe(nullptr);
@@ -274,13 +290,13 @@
 
 void ToastController::CreateToast(const ToastParams& params,
                                   const ToastSpecification* spec) {
-  views::View* anchor_view = browser_window_interface_->TopContainer();
+  views::View* const anchor_view = browser_window_interface_->TopContainer();
   CHECK(anchor_view);
   auto toast_view = std::make_unique<toasts::ToastView>(
       anchor_view,
       FormatString(spec->body_string_id(),
                    params.body_string_replacement_params_),
-      spec->icon(), browser_window_interface_->ShouldHideUIForFullscreen(),
+      spec->icon(), ShouldRenderToastOverWebContents(),
       base::BindRepeating(&RecordToastDismissReason, params.toast_id_));
 
   if (spec->has_close_button()) {
@@ -335,7 +351,7 @@
 
 void ToastController::OnFullscreenStateChanged() {
   toast_view_->UpdateRenderToastOverWebContentsAndPaint(
-      browser_window_interface_->ShouldHideUIForFullscreen());
+      ShouldRenderToastOverWebContents());
 }
 
 void ToastController::ClearTabScopedToasts() {
diff --git a/chrome/browser/ui/toasts/toast_controller.h b/chrome/browser/ui/toasts/toast_controller.h
index eb4f4f3..cd51a6cd 100644
--- a/chrome/browser/ui/toasts/toast_controller.h
+++ b/chrome/browser/ui/toasts/toast_controller.h
@@ -91,6 +91,8 @@
 
   views::Widget* GetToastWidgetForTesting() { return toast_widget_; }
 
+  toasts::ToastView* GetToastViewForTesting() { return toast_view_; }
+
   base::OneShotTimer* GetToastCloseTimerForTesting();
 
  private:
@@ -104,6 +106,7 @@
                               std::vector<std::u16string> replacement);
   void ClearTabScopedToasts();
   void UpdateToastWidgetVisibility(bool show_toast_widget);
+  bool ShouldRenderToastOverWebContents();
 
   // FullscreenObserver:
   void OnFullscreenStateChanged() override;
diff --git a/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc b/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc
index de623d6..7e062c1 100644
--- a/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc
+++ b/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc
@@ -13,6 +13,8 @@
 #include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_features.h"
+#include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
+#include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
 #include "chrome/browser/ui/location_bar/location_bar.h"
 #include "chrome/browser/ui/omnibox/omnibox_tab_helper.h"
 #include "chrome/browser/ui/toasts/api/toast_id.h"
@@ -31,10 +33,12 @@
 #include "content/public/test/browser_test.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
+#include "third_party/blink/public/mojom/frame/fullscreen.mojom.h"
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/base/interaction/element_identifier.h"
 #include "ui/base/interaction/interactive_test.h"
 #include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/gfx/geometry/rect.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 #include "ui/views/focus/focus_manager.h"
 #include "ui/views/interaction/interactive_views_test.h"
@@ -448,3 +452,30 @@
                                            toasts::ToastCloseReason::kAbort, 1);
       }));
 }
+
+IN_PROC_BROWSER_TEST_F(ToastControllerInteractiveTest,
+                       ToastRendersOverWebContents) {
+#if BUILDFLAG(IS_MAC)
+  FullscreenController* const fullscreen_controller =
+      browser()->exclusive_access_manager()->fullscreen_controller();
+  fullscreen_controller->set_is_tab_fullscreen_for_testing(true);
+#else
+  ui_test_utils::FullscreenWaiter waiter(browser(), {.tab_fullscreen = true});
+  content::WebContents* const active_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  active_contents->GetDelegate()->EnterFullscreenModeForTab(
+      active_contents->GetPrimaryMainFrame(), {});
+  waiter.Wait();
+#endif
+
+  ToastController* const toast_controller = GetToastController();
+  EXPECT_TRUE(
+      toast_controller->MaybeShowToast(ToastParams(ToastId::kLinkCopied)));
+  const gfx::Rect toast_bounds =
+      toast_controller->GetToastViewForTesting()->GetBoundsInScreen();
+  const gfx::Rect web_view_bounds =
+      BrowserView::GetBrowserViewForBrowser(browser())
+          ->GetContentsWebView()
+          ->GetBoundsInScreen();
+  EXPECT_TRUE(web_view_bounds.Contains(toast_bounds));
+}
diff --git a/chrome/browser/ui/toasts/toast_view.cc b/chrome/browser/ui/toasts/toast_view.cc
index 4e9d9886..55542a8 100644
--- a/chrome/browser/ui/toasts/toast_view.cc
+++ b/chrome/browser/ui/toasts/toast_view.cc
@@ -19,9 +19,11 @@
 #include "ui/compositor/layer.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/animation/animation_builder.h"
+#include "ui/views/border.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/button/image_button_factory.h"
 #include "ui/views/controls/highlight_path_generator.h"
+#include "ui/views/layout/layout_provider.h"
 #include "ui/views/view_class_properties.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/window/dialog_client_view.h"
@@ -143,10 +145,12 @@
         close_button_callback_.Then(
             base::BindRepeating(&ToastView::Close, base::Unretained(this),
                                 ToastCloseReason::kCloseButton)),
-        vector_icons::kCloseIcon,
-        lp->GetDistanceMetric(DISTANCE_TOAST_BUBBLE_HEIGHT_CONTENT) -
-            lp->GetInsetsMetric(views::INSETS_VECTOR_IMAGE_BUTTON).height(),
+        vector_icons::kCloseChromeRefreshIcon,
+        lp->GetDistanceMetric(DISTANCE_TOAST_BUBBLE_ICON_SIZE),
         ui::kColorToastForeground));
+    // Override the image button's border with the appropriate icon border size.
+    close_button_->SetBorder(views::CreateEmptyBorder(
+        lp->GetInsetsMetric(views::InsetsMetric::INSETS_ICON_BUTTON)));
     views::InstallCircleHighlightPathGenerator(close_button_);
     close_button_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_CLOSE));
     close_button_->SetProperty(views::kElementIdentifierKey, kToastCloseButton);
@@ -280,7 +284,7 @@
   icon_view_->SetImage(ui::ImageModel::FromVectorIcon(
       *icon_, color_provider->GetColor(ui::kColorToastForeground),
       ChromeLayoutProvider::Get()->GetDistanceMetric(
-          DISTANCE_TOAST_BUBBLE_LEADING_ICON_SIZE)));
+          DISTANCE_TOAST_BUBBLE_ICON_SIZE)));
 }
 
 void ToastView::AnimateOut(base::OnceClosure callback,
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc
index 4d32cec..0c66beb 100644
--- a/chrome/browser/ui/ui_features.cc
+++ b/chrome/browser/ui/ui_features.cc
@@ -224,18 +224,6 @@
              "SearchWebInSidePanel",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Feature that controls whether or not feature engagement configurations can be
-// used to control automatic triggering for side search.
-BASE_FEATURE(kSideSearchAutoTriggering,
-             "SideSearchAutoTriggering",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
-// Feature param that determines how many times a user has to return to a given
-// SRP before we automatically trigger the side search side panel for that SRP
-// on a subsequent navigation.
-const base::FeatureParam<int> kSideSearchAutoTriggeringReturnCount{
-    &kSideSearchAutoTriggering, "SideSearchAutoTriggeringReturnCount", 2};
-
 BASE_FEATURE(kSidePanelWebView,
              "SidePanelWebView",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h
index 818cb38..1c46163 100644
--- a/chrome/browser/ui/ui_features.h
+++ b/chrome/browser/ui/ui_features.h
@@ -167,9 +167,6 @@
 BASE_DECLARE_FEATURE(kSideSearchFeedback);
 BASE_DECLARE_FEATURE(kSearchWebInSidePanel);
 
-BASE_DECLARE_FEATURE(kSideSearchAutoTriggering);
-extern const base::FeatureParam<int> kSideSearchAutoTriggeringReturnCount;
-
 BASE_DECLARE_FEATURE(kTabGroupsCollapseFreezing);
 
 BASE_DECLARE_FEATURE(kTabHoverCardImages);
diff --git a/chrome/browser/ui/views/chrome_layout_provider.cc b/chrome/browser/ui/views/chrome_layout_provider.cc
index 3539939..9b189df 100644
--- a/chrome/browser/ui/views/chrome_layout_provider.cc
+++ b/chrome/browser/ui/views/chrome_layout_provider.cc
@@ -172,7 +172,7 @@
       return 36;
     case DISTANCE_TOAST_BUBBLE_HEIGHT_CONTENT:
       return 24;
-    case DISTANCE_TOAST_BUBBLE_LEADING_ICON_SIZE:
+    case DISTANCE_TOAST_BUBBLE_ICON_SIZE:
       return 20;
     case DISTANCE_TOAST_BUBBLE_LEADING_ICON_SIDE_MARGINS:
       return 2;
diff --git a/chrome/browser/ui/views/chrome_layout_provider.h b/chrome/browser/ui/views/chrome_layout_provider.h
index 7d283922..d93d7fa 100644
--- a/chrome/browser/ui/views/chrome_layout_provider.h
+++ b/chrome/browser/ui/views/chrome_layout_provider.h
@@ -110,8 +110,8 @@
   DISTANCE_TOAST_BUBBLE_HEIGHT_ACTION_BUTTON,
   // Height of the toast text and close button icon.
   DISTANCE_TOAST_BUBBLE_HEIGHT_CONTENT,
-  // Width and height of the leading vector icon shown in the toast bubble.
-  DISTANCE_TOAST_BUBBLE_LEADING_ICON_SIZE,
+  // Width and height of the vector icons shown in the toast bubble.
+  DISTANCE_TOAST_BUBBLE_ICON_SIZE,
   // Left and right margins of the leading vector icon shown in the toast
   // bubble.
   DISTANCE_TOAST_BUBBLE_LEADING_ICON_SIDE_MARGINS,
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index c31c546..079c462 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -317,20 +317,18 @@
   // has placeholder text. Use that instead of the DSE placeholder text.
   if (!model()->keyword_placeholder().empty()) {
     SetPlaceholderText(model()->keyword_placeholder());
-    return;
-  }
-
-  // Otherwise, if a DSE is set, use the DSE placeholder text.
-  const TemplateURL* const default_provider = controller()
-                                                  ->client()
-                                                  ->GetTemplateURLService()
-                                                  ->GetDefaultSearchProvider();
-  if (default_provider) {
+  } else if (const auto* default_provider = controller()
+                                                ->client()
+                                                ->GetTemplateURLService()
+                                                ->GetDefaultSearchProvider()) {
+    // Otherwise, if a DSE is set, use the DSE placeholder text.
     SetPlaceholderText(l10n_util::GetStringFUTF16(
         IDS_OMNIBOX_PLACEHOLDER_TEXT, default_provider->short_name()));
   } else {
     SetPlaceholderText(std::u16string());
   }
+
+  UpdatePlaceholderTextColor();
 }
 
 bool OmniboxViewViews::GetSelectionAtEnd() const {
@@ -630,8 +628,7 @@
 void OmniboxViewViews::OnThemeChanged() {
   views::Textfield::OnThemeChanged();
 
-  set_placeholder_text_color(
-      GetColorProvider()->GetColor(kColorOmniboxTextDimmed));
+  UpdatePlaceholderTextColor();
   SetSelectionBackgroundColor(
       GetColorProvider()->GetColor(kColorOmniboxSelectionBackground));
   SetSelectionTextColor(
@@ -2004,6 +2001,18 @@
 #endif
 }
 
+void OmniboxViewViews::UpdatePlaceholderTextColor() {
+  // Keyword placeholders are dim to differentiate from user input. DSE
+  // placeholders are not dim to draw attention to the omnibox and because the
+  // omnibox is unfocused so there's less risk of confusion with user input.
+  // Null in tests.
+  if (!GetColorProvider())
+    return;
+  set_placeholder_text_color(GetColorProvider()->GetColor(
+      model()->keyword_placeholder().empty() ? kColorOmniboxText
+                                             : kColorOmniboxTextDimmed));
+}
+
 BEGIN_METADATA(OmniboxViewViews)
 ADD_READONLY_PROPERTY_METADATA(bool, SelectionAtEnd)
 ADD_READONLY_PROPERTY_METADATA(int, TextWidth)
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.h b/chrome/browser/ui/views/omnibox/omnibox_view_views.h
index a39f58c6..e8377f0 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.h
@@ -84,9 +84,7 @@
 
   // Exposes the RenderText for tests.
 #if defined(UNIT_TEST)
-  gfx::RenderText* GetRenderText() {
-    return views::Textfield::GetRenderText();
-  }
+  gfx::RenderText* GetRenderText() { return views::Textfield::GetRenderText(); }
 #endif
 
   // For use when switching tabs, this saves the current state onto the tab so
@@ -326,6 +324,10 @@
   // Called when the popup view becomes visible.
   void OnPopupOpened();
 
+  // Helper for updating placeholder color depending on whether its a keyword or
+  // DSE placeholder.
+  void UpdatePlaceholderTextColor();
+
   // When true, the location bar view is read only and also is has a slightly
   // different presentation (smaller font size). This is used for popups.
   bool popup_window_mode_;
diff --git a/chrome/browser/ui/views/search_engine_choice/OWNERS b/chrome/browser/ui/views/search_engine_choice/OWNERS
index a8ff91d..c7aa42b 100644
--- a/chrome/browser/ui/views/search_engine_choice/OWNERS
+++ b/chrome/browser/ui/views/search_engine_choice/OWNERS
@@ -1 +1 @@
-file://components/search_engines/search_engine_choice/OWNERS
+file://components/search_engines/search_engine_choice/COMPLIANCE_OWNERS
diff --git a/chrome/browser/ui/views/side_search/unified_side_search_controller.cc b/chrome/browser/ui/views/side_search/unified_side_search_controller.cc
index c6ad7543..7e5a490 100644
--- a/chrome/browser/ui/views/side_search/unified_side_search_controller.cc
+++ b/chrome/browser/ui/views/side_search/unified_side_search_controller.cc
@@ -14,7 +14,6 @@
 #include "chrome/browser/ui/browser_window/public/browser_window_features.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/side_search/side_search_utils.h"
-#include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/side_panel/side_panel.h"
@@ -322,8 +321,10 @@
   const GURL& previously_committed_url =
       navigation_handle->GetPreviousPrimaryMainFrameURL();
   const bool is_renderer_initiated = navigation_handle->IsRendererInitiated();
-  const int auto_triggering_return_count =
-      features::kSideSearchAutoTriggeringReturnCount.Get();
+
+  // How many times a user has to return to a given SRP before we automatically
+  // trigger the side search side panel for that SRP on a subsequent navigation.
+  constexpr int kAutoTriggeringReturnCount = 2;
 
   // Trigger the side panel only if we've returned to the same SRP n times and
   // this is the first navigation after navigating away from the Google SRP. We
@@ -332,7 +333,7 @@
   // etc.
   return is_renderer_initiated &&
          tab_contents_helper->returned_to_previous_srp_count() ==
-             auto_triggering_return_count &&
+             kAutoTriggeringReturnCount &&
          previously_committed_url == tab_contents_helper->last_search_url();
 }
 
diff --git a/chrome/browser/ui/views/web_apps/web_app_navigation_capturing_iph_interactive_uitest.cc b/chrome/browser/ui/views/web_apps/web_app_navigation_capturing_iph_interactive_uitest.cc
index de77d71d..c0c7c16 100644
--- a/chrome/browser/ui/views/web_apps/web_app_navigation_capturing_iph_interactive_uitest.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_navigation_capturing_iph_interactive_uitest.cc
@@ -34,6 +34,7 @@
 #include "third_party/blink/public/common/input/web_mouse_event.h"
 #include "ui/base/interaction/element_identifier.h"
 #include "ui/base/interaction/state_observer.h"
+#include "ui/base/test/ui_controls.h"
 #include "ui/views/interaction/interaction_test_util_views.h"
 #include "url/gurl.h"
 
@@ -158,51 +159,24 @@
   // Clicks on the "launch app" link on the start page with element ID
   // `element_id`. The start page must be open in at least one browser. The
   // context of the last step is the browser window containing the start page.
-  auto ClickLaunchLink(test::ClickMethod click, const std::string& element_id) {
-    int mouse_button;
-    bool shift = false;
-    switch (click) {
-      case test::ClickMethod::kLeftClick:
-        mouse_button = 0;
-        break;
-      case test::ClickMethod::kMiddleClick:
-        mouse_button = 1;
-        break;
-      case test::ClickMethod::kShiftClick:
-        mouse_button = 0;
-        shift = true;
-        break;
-      case test::ClickMethod::kRightClickLaunchApp:
-        mouse_button = 2;
-        break;
-    }
-    const auto js = base::StringPrintf(R"(
-      function(el) {
-        const event = new MouseEvent(
-            'click',
-            {
-              bubbles: true,
-              button: %d,
-              cancelable: true,
-              shiftKey: %s
-            }
-        );
-        el.dispatchEvent(event);
-      }
-    )",
-                                       mouse_button, shift ? "true" : "false");
-
-    return InAnyContext(ExecuteJsAt(kStartPageId, {"#" + element_id}, js)
-                            .SetDescription("ClickLaunchLink()"));
+  auto ClickLaunchLink(
+      const std::string& element_id,
+      ui_controls::MouseButton button,
+      ui_controls::AcceleratorState accel = ui_controls::kNoAccelerator) {
+    return InAnyContext(
+        ClickElement(kStartPageId, {"#" + element_id}, button, accel)
+            .SetDescription("ClickLaunchLink()"));
   }
 
   // Clicks on `element_id` in the start page, which must be open in at least
   // one browser, launching a new app window. The context of the last step is
   // the window in which the link was opened.
-  auto TriggerAppLaunch(test::ClickMethod click,
-                        const std::string& element_id) {
+  auto TriggerAppLaunch(
+      const std::string& element_id,
+      ui_controls::MouseButton button,
+      ui_controls::AcceleratorState accel = ui_controls::kNoAccelerator) {
     auto steps = Steps(
-        ClickLaunchLink(click, element_id),
+        ClickLaunchLink(element_id, button, accel),
         InAnyContext(
             WaitForShow(kBrowserViewElementId).SetTransitionOnlyOnEvent(true)),
         InSameContext(CheckViewProperty(kBrowserViewElementId,
@@ -230,11 +204,11 @@
 IN_PROC_BROWSER_TEST_F(WebAppNavigationCapturingIphUiTest,
                        IPHShownOnLinkLeftClick) {
   const webapps::AppId app_id = InstallTestWebApp(GetDestinationUrl());
-  RunTestSequence(OpenStartPage(),
-                  TriggerAppLaunch(test::ClickMethod::kLeftClick,
-                                   kToSiteBTargetBlankNoOpener),
-                  InSameContext(WaitForPromo(
-                      feature_engagement::kIPHDesktopPWAsLinkCapturingLaunch)));
+  RunTestSequence(
+      OpenStartPage(),
+      TriggerAppLaunch(kToSiteBTargetBlankNoOpener, ui_controls::LEFT),
+      InSameContext(WaitForPromo(
+          feature_engagement::kIPHDesktopPWAsLinkCapturingLaunch)));
 }
 
 // Middle click does not work (consistently?) on Mac; see
@@ -247,11 +221,11 @@
 IN_PROC_BROWSER_TEST_F(WebAppNavigationCapturingIphUiTest,
                        MAYBE_IPHShownOnLinkMiddleClick) {
   const webapps::AppId app_id = InstallTestWebApp(GetStartUrl());
-  RunTestSequence(OpenAppStartPage(app_id),
-                  TriggerAppLaunch(test::ClickMethod::kMiddleClick,
-                                   kToSiteATargetBlankWithOpener),
-                  InSameContext(WaitForPromo(
-                      feature_engagement::kIPHDesktopPWAsLinkCapturingLaunch)));
+  RunTestSequence(
+      OpenAppStartPage(app_id),
+      TriggerAppLaunch(kToSiteATargetBlankWithOpener, ui_controls::MIDDLE),
+      InSameContext(WaitForPromo(
+          feature_engagement::kIPHDesktopPWAsLinkCapturingLaunch)));
 }
 
 // Shift-click click does not work (consistently?) on Mac; see
@@ -266,8 +240,8 @@
   const webapps::AppId app_id_a = InstallTestWebApp(GetStartUrl());
   const webapps::AppId app_id_b = InstallTestWebApp(GetDestinationUrl());
   RunTestSequence(OpenAppStartPage(app_id_a),
-                  TriggerAppLaunch(test::ClickMethod::kShiftClick,
-                                   kToSiteBTargetBlankWithOpener),
+                  TriggerAppLaunch(kToSiteBTargetBlankWithOpener,
+                                   ui_controls::LEFT, ui_controls::kShift),
                   InSameContext(WaitForPromo(
                       feature_engagement::kIPHDesktopPWAsLinkCapturingLaunch)));
 }
@@ -279,14 +253,14 @@
       blink::Manifest::LaunchHandler(
           blink::mojom::ManifestLaunchHandler_ClientMode::kFocusExisting));
 
-  RunTestSequence(OpenStartPage(), OpenApp(app_id),
-                  ClickLaunchLink(test::ClickMethod::kLeftClick,
-                                  kToSiteBTargetBlankNoOpener),
-                  // Switch back to the app browser's context and verify the IPH
-                  // shows there.
-                  InAnyContext(WithElement(kAppPageId, base::DoNothing())),
-                  InSameContext(WaitForPromo(
-                      feature_engagement::kIPHDesktopPWAsLinkCapturingLaunch)));
+  RunTestSequence(
+      OpenStartPage(), OpenApp(app_id),
+      ClickLaunchLink(kToSiteBTargetBlankNoOpener, ui_controls::LEFT),
+      // Switch back to the app browser's context and verify the IPH
+      // shows there.
+      InAnyContext(WithElement(kAppPageId, base::DoNothing())),
+      InSameContext(WaitForPromo(
+          feature_engagement::kIPHDesktopPWAsLinkCapturingLaunch)));
 }
 
 IN_PROC_BROWSER_TEST_F(WebAppNavigationCapturingIphUiTest,
@@ -296,8 +270,7 @@
 
   RunTestSequence(
       OpenAppStartPage(app_id_a),
-      TriggerAppLaunch(test::ClickMethod::kLeftClick,
-                       kToSiteBTargetBlankWithOpener),
+      TriggerAppLaunch(kToSiteBTargetBlankWithOpener, ui_controls::LEFT),
       InSameContext(CheckPromoIsActive(
           feature_engagement::kIPHDesktopPWAsLinkCapturingLaunch, false)));
 }
@@ -308,8 +281,7 @@
 
   RunTestSequence(
       OpenStartPage(),
-      TriggerAppLaunch(test::ClickMethod::kLeftClick,
-                       kToSiteBTargetBlankNoOpener),
+      TriggerAppLaunch(kToSiteBTargetBlankNoOpener, ui_controls::LEFT),
       InSameContext(Steps(
           WaitForPromo(feature_engagement::kIPHDesktopPWAsLinkCapturingLaunch),
           CheckActionCount("LinkCapturingIPHAppBubbleShown", 1),
@@ -326,8 +298,7 @@
 
   RunTestSequence(
       OpenStartPage(),
-      TriggerAppLaunch(test::ClickMethod::kLeftClick,
-                       kToSiteBTargetBlankNoOpener),
+      TriggerAppLaunch(kToSiteBTargetBlankNoOpener, ui_controls::LEFT),
       InSameContext(Steps(
           WaitForPromo(feature_engagement::kIPHDesktopPWAsLinkCapturingLaunch),
           CheckActionCount("LinkCapturingIPHAppBubbleShown", 1),
@@ -342,8 +313,7 @@
 
   RunTestSequence(
       OpenStartPage(),
-      TriggerAppLaunch(test::ClickMethod::kLeftClick,
-                       kToSiteBTargetBlankNoOpener),
+      TriggerAppLaunch(kToSiteBTargetBlankNoOpener, ui_controls::LEFT),
       InSameContext(Steps(
           WaitForPromo(feature_engagement::kIPHDesktopPWAsLinkCapturingLaunch),
           PressDefaultPromoButton(),
diff --git a/chrome/browser/ui/webui/cr_components/history_embeddings/history_embeddings_handler.cc b/chrome/browser/ui/webui/cr_components/history_embeddings/history_embeddings_handler.cc
index d1c3474..be2dc4d 100644
--- a/chrome/browser/ui/webui/cr_components/history_embeddings/history_embeddings_handler.cc
+++ b/chrome/browser/ui/webui/cr_components/history_embeddings/history_embeddings_handler.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/webui/cr_components/history_embeddings/history_embeddings_handler.h"
 
+#include "base/i18n/time_formatting.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/feedback/show_feedback_page.h"
@@ -122,6 +123,8 @@
     item->relative_time = base::UTF16ToUTF8(ui::TimeFormat::Simple(
         ui::TimeFormat::FORMAT_ELAPSED, ui::TimeFormat::LENGTH_SHORT,
         base::Time::Now() - scored_url_row.row.last_visit()));
+    item->short_date_time = base::UTF16ToUTF8(
+        base::TimeFormatShortDate(scored_url_row.row.last_visit()));
     item->last_url_visit_timestamp =
         scored_url_row.row.last_visit().InMillisecondsFSinceUnixEpoch();
 
diff --git a/chrome/browser/ui/webui/cr_components/history_embeddings/history_embeddings_handler_unittest.cc b/chrome/browser/ui/webui/cr_components/history_embeddings/history_embeddings_handler_unittest.cc
index 86823f70..b8bb99a 100644
--- a/chrome/browser/ui/webui/cr_components/history_embeddings/history_embeddings_handler_unittest.cc
+++ b/chrome/browser/ui/webui/cr_components/history_embeddings/history_embeddings_handler_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/webui/cr_components/history_embeddings/history_embeddings_handler.h"
 
+#include "base/i18n/time_formatting.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_feature_list.h"
@@ -209,6 +210,9 @@
             base::UTF16ToUTF8(ui::TimeFormat::Simple(
                 ui::TimeFormat::FORMAT_ELAPSED, ui::TimeFormat::LENGTH_SHORT,
                 base::Time::Now() - scored_url_row.row.last_visit())));
+  EXPECT_EQ(mojo_result->items[0]->short_date_time,
+            base::UTF16ToUTF8(
+                base::TimeFormatShortDate(scored_url_row.row.last_visit())));
   EXPECT_EQ(mojo_result->items[0]->last_url_visit_timestamp,
             scored_url_row.row.last_visit().InMillisecondsFSinceUnixEpoch());
   EXPECT_EQ(mojo_result->items[0]->url_for_display, "google.com");
diff --git a/chrome/browser/ui/webui/password_manager/password_manager_ui.cc b/chrome/browser/ui/webui/password_manager/password_manager_ui.cc
index 5c08590..d63f1dd 100644
--- a/chrome/browser/ui/webui/password_manager/password_manager_ui.cc
+++ b/chrome/browser/ui/webui/password_manager/password_manager_ui.cc
@@ -355,6 +355,8 @@
       {"passwordManager",
        IDS_PASSWORD_BUBBLES_PASSWORD_MANAGER_LINK_TEXT_SYNCED_TO_ACCOUNT},
       // Header for the page, always "Password Manager".
+      {"passwordManagerDescription",
+       IDS_PASSWORD_MANAGER_UI_DESCRIPTION},
       {"passwordManagerPinChanged", IDS_PASSWORD_MANAGER_PIN_CHANGED},
       {"passwordManagerString", IDS_PASSWORD_MANAGER_UI_TITLE},
       // Page title, branded. "Google Password Manager" or "Password Manager"
diff --git a/chrome/browser/ui/webui/search_engine_choice/OWNERS b/chrome/browser/ui/webui/search_engine_choice/OWNERS
index 89018e9..c591a23 100644
--- a/chrome/browser/ui/webui/search_engine_choice/OWNERS
+++ b/chrome/browser/ui/webui/search_engine_choice/OWNERS
@@ -1,3 +1,3 @@
-file://components/search_engines/search_engine_choice/OWNERS
+file://components/search_engines/search_engine_choice/COMPLIANCE_OWNERS
 per-file *.mojom=set noparent
 per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chrome/browser/ui/webui/settings/settings_interactive_uitest.cc b/chrome/browser/ui/webui/settings/settings_interactive_uitest.cc
index 53902e5..786a66e 100644
--- a/chrome/browser/ui/webui/settings/settings_interactive_uitest.cc
+++ b/chrome/browser/ui/webui/settings/settings_interactive_uitest.cc
@@ -106,15 +106,10 @@
   UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
   UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
 
-  bool is_3pcd = base::FeatureList::IsEnabled(
-      content_settings::features::kTrackingProtection3pcd);
-  const std::string cookie_row_selector =
-      is_3pcd ? "cr-link-row#trackingProtectionLinkRow"
-              : "cr-link-row#thirdPartyCookiesLinkRow";
   const GURL cookie_setting_url("chrome://settings/privacy");
   const WebContentsInteractionTestUtil::DeepQuery cookies_link_row = {
       "settings-ui", "settings-main", "settings-basic-page",
-      "settings-privacy-page", cookie_row_selector};
+      "settings-privacy-page", "cr-link-row#thirdPartyCookiesLinkRow"};
   const WebContentsInteractionTestUtil::DeepQuery
       cookies_setting_page_help_icon = {
           "settings-ui",
@@ -152,14 +147,8 @@
                         auto* util =
                             element->AsA<TrackedElementWebContents>()->owner();
                         auto* const contents = util->web_contents();
-                        if (is_3pcd) {
-                          EXPECT_EQ(
-                              contents->GetURL(),
-                              GURL(chrome::kTrackingProtectionHelpCenterURL));
-                        } else {
-                          EXPECT_EQ(contents->GetURL(),
-                                    chrome::kCookiesSettingsHelpCenterURL);
-                        }
+                        EXPECT_EQ(contents->GetURL(),
+                                  chrome::kCookiesSettingsHelpCenterURL);
                       }))
                   .Build())
           .Build();
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc
index 623fc7a..1b92d88 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc
@@ -241,8 +241,9 @@
 }
 
 void ReadAnythingWebContentsObserver::AccessibilityLocationChangesReceived(
-    const std::vector<ui::AXLocationChanges>& details) {
-  page_handler_->AccessibilityLocationChangesReceived(details);
+    const ui::AXTreeID& tree_id,
+    ui::AXLocationAndScrollUpdates& details) {
+  page_handler_->AccessibilityLocationChangesReceived(tree_id, details);
 }
 
 void ReadAnythingWebContentsObserver::PrimaryPageChanged(content::Page& page) {
@@ -390,9 +391,10 @@
 }
 
 void ReadAnythingUntrustedPageHandler::AccessibilityLocationChangesReceived(
-    const std::vector<ui::AXLocationChanges>& details) {
+    const ui::AXTreeID& tree_id,
+    ui::AXLocationAndScrollUpdates& details) {
   if (features::IsReadAnythingDocsIntegrationEnabled()) {
-    page_->AccessibilityLocationChangesReceived(details);
+    page_->AccessibilityLocationChangesReceived(tree_id, details);
   }
 }
 
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h
index 24968ac..abbf48be 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h
@@ -57,7 +57,8 @@
   void AccessibilityEventReceived(
       const ui::AXUpdatesAndEvents& details) override;
   void AccessibilityLocationChangesReceived(
-      const std::vector<ui::AXLocationChanges>& details) override;
+      const ui::AXTreeID& tree_id,
+      ui::AXLocationAndScrollUpdates& details) override;
   void PrimaryPageChanged(content::Page& page) override;
   void WebContentsDestroyed() override;
 
@@ -102,7 +103,8 @@
 
   void AccessibilityEventReceived(const ui::AXUpdatesAndEvents& details);
   void AccessibilityLocationChangesReceived(
-      const std::vector<ui::AXLocationChanges>& details);
+      const ui::AXTreeID& tree_id,
+      ui::AXLocationAndScrollUpdates& details);
   void PrimaryPageChanged();
   void WebContentsDestroyed();
 
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_unittest.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_unittest.cc
index c18fff7..b45f4255 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_unittest.cc
@@ -26,7 +26,6 @@
 #include "ui/accessibility/ax_node_id_forward.h"
 #include "ui/accessibility/ax_tree_id.h"
 #include "ui/accessibility/mojom/ax_event.mojom.h"
-#include "ui/accessibility/mojom/ax_location_changes.mojom.h"
 #include "ui/accessibility/mojom/ax_tree_id.mojom.h"
 #include "ui/accessibility/mojom/ax_tree_update.mojom.h"
 #include "ui/gfx/geometry/size.h"
@@ -51,7 +50,12 @@
                     const std::vector<ui::AXEvent>& events));
   MOCK_METHOD(void,
               AccessibilityLocationChangesReceived,
-              (const std::vector<ui::AXLocationChanges>& details));
+              (const ui::AXTreeID& tree_id,
+               ui::AXLocationAndScrollUpdates& details));
+  MOCK_METHOD(void,
+              AccessibilityLocationChangesReceived,
+              (const ui::AXTreeID& tree_id,
+               const ui::AXLocationAndScrollUpdates& details));
   MOCK_METHOD(void,
               OnSettingsRestoredFromPrefs,
               (read_anything::mojom::LineSpacing line_spacing,
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 768145d..a2bc247 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1727791183-18ebb677bfded080d4fbccf41c258d8f5b869cce-5c8b1389ff74057dc8513e6687b93c9019d4a496.profdata
+chrome-mac-arm-main-1727805595-7e9a010c1d8e5cb05c53975b96ca20fa6c385199-bdb53115de59bca1fe7e29f68cb950f5b1280da2.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index c7b36b7..5a4b2b4 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1727773147-71e35f3eeb906599fea4b886352dcd4a11b1a762-a163f2d7227da4a5b9792888bd14304ff72ce6f2.profdata
+chrome-win64-main-1727783961-fa1fbfa59af5e894dd8c60c24877d4aaf771b088-f7bc99221b34f0a43ca97da3ec7dcc2ae2e90c0c.profdata
diff --git a/chrome/common/accessibility/read_anything.mojom b/chrome/common/accessibility/read_anything.mojom
index 080c697..cf38aa56 100644
--- a/chrome/common/accessibility/read_anything.mojom
+++ b/chrome/common/accessibility/read_anything.mojom
@@ -11,7 +11,7 @@
 import "skia/public/mojom/bitmap.mojom";
 import "ui/accessibility/ax_features.mojom";
 import "ui/accessibility/mojom/ax_event.mojom";
-import "ui/accessibility/mojom/ax_location_changes.mojom";
+import "ui/accessibility/mojom/ax_location_and_scroll_updates.mojom";
 import "ui/accessibility/mojom/ax_tree_id.mojom";
 import "ui/accessibility/mojom/ax_tree_update.mojom";
 import "mojo/public/mojom/base/values.mojom";
@@ -226,7 +226,9 @@
   // Send a location change notification to the WebUI. The WebUI updates the
   // locations for each node in the vector. This does not trigger a distillation.
   [RuntimeFeature=ax.mojom.features.kReadAnythingDocsIntegration]
-  AccessibilityLocationChangesReceived(array<ax.mojom.AXLocationChanges> details);
+  AccessibilityLocationChangesReceived(
+    ax.mojom.AXTreeID tree_id,
+    ax.mojom.AXLocationAndScrollUpdates details);
 
   // Sends the active AXTreeID to the WebUI. This is not guaranteed to be called
   // after the tree_id was previously passed to AccessibilityEventsReceived.
diff --git a/chrome/enterprise_companion/enterprise_companion_client.cc b/chrome/enterprise_companion/enterprise_companion_client.cc
index bf8d438..2b4cc72 100644
--- a/chrome/enterprise_companion/enterprise_companion_client.cc
+++ b/chrome/enterprise_companion/enterprise_companion_client.cc
@@ -96,6 +96,7 @@
     VLOG(1) << "Failed to connect to EnterpriseCompanionService remote. "
                "The service could not be launched.";
     std::move(callback).Run({});
+    return;
   }
 
   mojo::PlatformChannelEndpoint endpoint =
diff --git a/chrome/renderer/accessibility/read_anything_app_controller.cc b/chrome/renderer/accessibility/read_anything_app_controller.cc
index 6992db7..8154fbb 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller.cc
+++ b/chrome/renderer/accessibility/read_anything_app_controller.cc
@@ -47,6 +47,7 @@
 #include "third_party/skia/include/core/SkImageInfo.h"
 #include "ui/accessibility/accessibility_features.h"
 #include "ui/accessibility/ax_enums.mojom-shared.h"
+#include "ui/accessibility/ax_location_and_scroll_updates.h"
 #include "ui/accessibility/ax_node.h"
 #include "ui/accessibility/ax_node_id_forward.h"
 #include "ui/accessibility/ax_role_properties.h"
@@ -523,10 +524,18 @@
 }
 
 void ReadAnythingAppController::AccessibilityLocationChangesReceived(
-    const std::vector<ui::AXLocationChanges>& details) {
+    const ui::AXTreeID& tree_id,
+    const ui::AXLocationAndScrollUpdates& details) {
+  NOTREACHED() << "Non-const ref version of this method should be used as a "
+                  "performance optimization.";
+}
+
+void ReadAnythingAppController::AccessibilityLocationChangesReceived(
+    const ui::AXTreeID& tree_id,
+    ui::AXLocationAndScrollUpdates& details) {
   // Listen to location change notifications to update locations of the nodes
   // accordingly.
-  for (auto& change : details) {
+  for (auto& change : details.location_changes) {
     ui::AXNode* ax_node = model_.GetAXNode(change.id);
     if (!ax_node) {
       continue;
diff --git a/chrome/renderer/accessibility/read_anything_app_controller.h b/chrome/renderer/accessibility/read_anything_app_controller.h
index 974648b..b6f9fad 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller.h
+++ b/chrome/renderer/accessibility/read_anything_app_controller.h
@@ -25,6 +25,7 @@
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/accessibility/ax_location_and_scroll_updates.h"
 #include "ui/accessibility/ax_node_id_forward.h"
 #include "ui/accessibility/ax_node_position.h"
 #include "ui/accessibility/ax_position.h"
@@ -121,7 +122,11 @@
       const std::vector<ui::AXTreeUpdate>& updates,
       const std::vector<ui::AXEvent>& events) override;
   void AccessibilityLocationChangesReceived(
-      const std::vector<ui::AXLocationChanges>& details) override;
+      const ui::AXTreeID& tree_id,
+      ui::AXLocationAndScrollUpdates& details) override;
+  void AccessibilityLocationChangesReceived(
+      const ui::AXTreeID& tree_id,
+      const ui::AXLocationAndScrollUpdates& details) override;
   void OnActiveAXTreeIDChanged(const ui::AXTreeID& tree_id,
                                ukm::SourceId ukm_source_id,
                                bool is_pdf) override;
diff --git a/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc b/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc
index 414e831..ac37ac2 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc
+++ b/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc
@@ -24,6 +24,7 @@
 #include "services/strings/grit/services_strings.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "ui/accessibility/accessibility_features.h"
+#include "ui/accessibility/ax_location_and_scroll_updates.h"
 #include "ui/accessibility/ax_node.h"
 #include "ui/accessibility/ax_node_id_forward.h"
 #include "ui/accessibility/ax_serializable_tree.h"
@@ -292,8 +293,9 @@
   }
 
   void AccessibilityLocationChangesReceived(
-      const std::vector<ui::AXLocationChanges>& details) {
-    controller_->AccessibilityLocationChangesReceived(details);
+      const ui::AXTreeID& tree_id,
+      ui::AXLocationAndScrollUpdates& details) {
+    controller_->AccessibilityLocationChangesReceived(tree_id, details);
   }
 
   // Since a11y events happen asynchronously, they can come between the time
@@ -1732,13 +1734,11 @@
   ui::AXRelativeBounds location_update;
   location_update.offset_container_id = 1;
   location_update.bounds = gfx::RectF(5, 5, 100, 100);
-  ui::AXLocationChanges location_changes;
-  location_changes.id = 2;
-  location_changes.ax_tree_id = id_1;
-  location_changes.new_location = location_update;
+  ui::AXLocationAndScrollUpdates location_and_scroll_updates;
+  location_and_scroll_updates.location_changes.emplace_back(2, location_update);
 
   // Test that the node data updates correctly
-  AccessibilityLocationChangesReceived({location_changes});
+  AccessibilityLocationChangesReceived(id_1, location_and_scroll_updates);
   node = GetNodeData(2);
   EXPECT_EQ(node.relative_bounds, location_update);
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeBrowserTestRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeBrowserTestRule.java
index 1416c84..443786b 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeBrowserTestRule.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeBrowserTestRule.java
@@ -25,10 +25,8 @@
                 new Statement() {
                     @Override
                     public void evaluate() throws Throwable {
-                        /**
-                         * Loads the native library on the activity UI thread.  After loading the library,
-                         * this will initialize the browser process if necessary.
-                         */
+                        // Loads the native library on the activity UI thread. After loading the
+                        // library, this will initialize the browser process if necessary.
                         NativeLibraryTestUtils.loadNativeLibraryAndInitBrowserProcess();
                         base.evaluate();
                     }
diff --git a/chrome/test/base/chrome_unit_test_suite.cc b/chrome/test/base/chrome_unit_test_suite.cc
index 0ea9af3a..fb961c4 100644
--- a/chrome/test/base/chrome_unit_test_suite.cc
+++ b/chrome/test/base/chrome_unit_test_suite.cc
@@ -52,8 +52,11 @@
 #include "components/web_package/signed_web_bundles/signed_web_bundle_id.h"
 #endif  // !BUILDFLAG(IS_ANDROID)
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
 #include "chrome/common/initialize_extensions_client.h"
+#endif
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/common/extension_paths.h"
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
@@ -176,7 +179,9 @@
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::RegisterPathProvider();
+#endif
 
+#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
   EnsureExtensionsClientInitialized();
 #endif
 
diff --git a/chrome/test/base/testing_browser_process.cc b/chrome/test/base/testing_browser_process.cc
index 1f214167..1778d1bc 100644
--- a/chrome/test/base/testing_browser_process.cc
+++ b/chrome/test/base/testing_browser_process.cc
@@ -68,6 +68,10 @@
 #include "components/storage_monitor/test_storage_monitor.h"
 #endif
 
+#if BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS)
+#include "chrome/browser/extensions/desktop_android/desktop_android_extensions_browser_client.h"
+#endif
+
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
 #include "chrome/browser/printing/background_printing_manager.h"
 #include "chrome/browser/printing/print_preview_dialog_controller.h"
@@ -175,7 +179,11 @@
         test_network_connection_tracker_.get());
   }
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS)
+  extensions_browser_client_ =
+      std::make_unique<extensions::DesktopAndroidExtensionsBrowserClient>();
+  extensions::ExtensionsBrowserClient::Set(extensions_browser_client_.get());
+#elif BUILDFLAG(ENABLE_EXTENSIONS)
   extensions_browser_client_ =
       std::make_unique<extensions::ChromeExtensionsBrowserClient>();
   extensions_browser_client_->AddAPIProvider(
diff --git a/chrome/test/base/testing_browser_process.h b/chrome/test/base/testing_browser_process.h
index 8ef74cb..617e441 100644
--- a/chrome/test/base/testing_browser_process.h
+++ b/chrome/test/base/testing_browser_process.h
@@ -267,7 +267,9 @@
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   std::unique_ptr<MediaFileSystemRegistry> media_file_system_registry_;
+#endif
 
+#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
   std::unique_ptr<extensions::ExtensionsBrowserClient>
       extensions_browser_client_;
 #endif
diff --git a/chrome/test/data/click.html b/chrome/test/data/click.html
new file mode 100644
index 0000000..ab726b0
--- /dev/null
+++ b/chrome/test/data/click.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<body>
+<button id="button">Button</button>
+<script>
+const button = document.getElementById('button');
+button.addEventListener('click', function(event) {
+  button.lastClickEvent = event;
+});
+</script>
+</body>
+</html>
diff --git a/chrome/test/data/webui/commerce/product_specifications/loading_state_test.ts b/chrome/test/data/webui/commerce/product_specifications/loading_state_test.ts
index de7c847..4053a91 100644
--- a/chrome/test/data/webui/commerce/product_specifications/loading_state_test.ts
+++ b/chrome/test/data/webui/commerce/product_specifications/loading_state_test.ts
@@ -17,6 +17,19 @@
     document.body.appendChild(loadingElement);
   });
 
+  function resizeContainer(width: number): Promise<void> {
+    // A ResizeObserver is used to ensure the ResizeObserver callback that shows
+    // the last column gradient has completed.
+    return new Promise<void>(resolve => {
+      const observer = new ResizeObserver(() => {
+        resolve();
+        observer.unobserve(loadingElement.$.loadingContainer);
+      });
+      observer.observe(loadingElement.$.loadingContainer);
+      loadingElement.$.loadingContainer.style.width = `${width}px`;
+    });
+  }
+
   test(
       'last column gradient appears when SVG is wider than its container',
       async () => {
@@ -31,7 +44,7 @@
 
         // Container should be smaller than the loading gradient, so the last
         // column gradient should appear.
-        document.body.style.width = '200px';
+        await resizeContainer(200);
         await microtasksFinished();
         assertTrue(isVisible(lastColumnGradient));
       });
diff --git a/chrome/test/data/webui/cr_components/history_embeddings/history_embeddings_test.ts b/chrome/test/data/webui/cr_components/history_embeddings/history_embeddings_test.ts
index 6f0775a..fe5a6f0 100644
--- a/chrome/test/data/webui/cr_components/history_embeddings/history_embeddings_test.ts
+++ b/chrome/test/data/webui/cr_components/history_embeddings/history_embeddings_test.ts
@@ -29,6 +29,7 @@
         url: {url: 'http://google.com'},
         urlForDisplay: 'google.com',
         relativeTime: '2 hours ago',
+        shortDateTime: 'Sept 2, 2022',
         sourcePassage: 'Google description',
         lastUrlVisitTimestamp: 1000,
         answerData: null,
@@ -38,6 +39,7 @@
         url: {url: 'http://youtube.com'},
         urlForDisplay: 'youtube.com',
         relativeTime: '4 hours ago',
+        shortDateTime: 'Sept 2, 2022',
         sourcePassage: 'Youtube description',
         lastUrlVisitTimestamp: 2000,
         answerData: null,
@@ -464,6 +466,7 @@
         url: {url: 'http://answer.com'},
         urlForDisplay: 'Answer.com',
         relativeTime: '2 months ago',
+        shortDateTime: 'Sept 2, 2022',
         sourcePassage: 'Answer description',
         lastUrlVisitTimestamp: 2000,
         answerData: {answerTextDirectives: []},
@@ -482,9 +485,11 @@
       assertTrue(!!answerSource);
       assertFalse(answerSource.hidden);
       assertEquals('http://answer.com', answerSource.getAttribute('href'));
-      assertEquals(
-          'Answer.com',
-          answerSource.querySelector<HTMLElement>('.result-url')!.innerText);
+
+      const answerUrlAndDate =
+          answerSource.querySelector<HTMLElement>('.result-url')!.innerText;
+      assertTrue(answerUrlAndDate.startsWith('Answer.com'));
+      assertTrue(answerUrlAndDate.endsWith('Sept 2, 2022'));
       assertEquals(
           getFaviconForPageURL('http://answer.com', true),
           answerSource.querySelector<HTMLElement>(
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/categories_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/categories_test.ts
index 1468442..3073bb1 100644
--- a/chrome/test/data/webui/side_panel/customize_chrome/categories_test.ts
+++ b/chrome/test/data/webui/side_panel/customize_chrome/categories_test.ts
@@ -6,7 +6,7 @@
 
 import type {CategoriesElement} from 'chrome://customize-chrome-side-panel.top-chrome/categories.js';
 import {CHANGE_CHROME_THEME_CLASSIC_ELEMENT_ID, CHROME_THEME_COLLECTION_ELEMENT_ID} from 'chrome://customize-chrome-side-panel.top-chrome/categories.js';
-import {CustomizeChromeAction} from 'chrome://customize-chrome-side-panel.top-chrome/common.js';
+import {CustomizeChromeAction, NtpImageType} from 'chrome://customize-chrome-side-panel.top-chrome/common.js';
 import type {BackgroundCollection, CustomizeChromePageRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
 import {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
 import {CustomizeChromeApiProxy} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome_api_proxy.js';
@@ -21,6 +21,11 @@
 
 import {$$, createBackgroundImage, createTheme, installMock} from './test_support.js';
 
+interface CollectionOptions {
+  numCollections: number;
+  shouldReplaceBrokenImages?: boolean;
+}
+
 function createTestCollections(length: number): BackgroundCollection[] {
   const testCollections: BackgroundCollection[] = [];
   for (let i = 1; i < length + 1; i++) {
@@ -41,16 +46,21 @@
   let callbackRouterRemote: CustomizeChromePageRemote;
   let metrics: MetricsTracker;
 
-  async function setInitialSettings(numCollections: number) {
+  async function setInitialSettings(
+      {numCollections, shouldReplaceBrokenImages = false}: CollectionOptions) {
     handler.setResultFor('getBackgroundCollections', Promise.resolve({
       collections: createTestCollections(numCollections),
     }));
     if (loadTimeData.getBoolean('imageErrorDetectionEnabled')) {
       handler.setResultMapperFor(
           'getReplacementCollectionPreviewImage', (collectionId: string) => {
-            return Promise.resolve({
-              previewImageUrl: {url: `https://replaced-${collectionId}.jpg`},
-            });
+            if (shouldReplaceBrokenImages) {
+              return Promise.resolve({
+                previewImageUrl: {url: `https://replaced-${collectionId}.jpg`},
+              });
+            } else {
+              return Promise.resolve(null);
+            }
           });
     }
     categoriesElement = document.createElement('customize-chrome-categories');
@@ -73,7 +83,7 @@
 
   test('hide collection elements when collections empty', async () => {
     const numCollections = 0;
-    await setInitialSettings(numCollections);
+    await setInitialSettings({numCollections: numCollections});
 
     const collections =
         categoriesElement.shadowRoot!.querySelectorAll('.collection');
@@ -90,8 +100,7 @@
 
       test('collection visibility based on error detection', async () => {
         const numCollections = 2;
-        await setInitialSettings(numCollections);
-        await microtasksFinished();
+        await setInitialSettings({numCollections: numCollections});
 
         const collections =
             categoriesElement.shadowRoot!.querySelectorAll('.collection');
@@ -114,7 +123,8 @@
 
       test('collection image src based on error detection', async () => {
         const numCollections = 2;
-        await setInitialSettings(numCollections);
+        await setInitialSettings(
+            {numCollections: numCollections, shouldReplaceBrokenImages: true});
 
         const images =
             categoriesElement.shadowRoot!.querySelectorAll<CrAutoImgElement>(
@@ -128,6 +138,10 @@
         if (!errorDetectionEnabled) {
           assertEquals('https://collection-1.jpg', images[0]!.autoSrc);
           assertEquals('https://collection-2.jpg', images[1]!.autoSrc);
+          assertEquals(
+              0,
+              metrics.count(
+                  'NewTabPage.BackgroundService.Images.Headers.ErrorDetected'));
         } else {
           assertEquals('https://replaced-1.jpg', images[0]!.autoSrc);
           assertEquals('https://replaced-2.jpg', images[1]!.autoSrc);
@@ -135,17 +149,16 @@
       });
 
       test('collections surface if their images load', async () => {
-        await setInitialSettings(1);
+        await setInitialSettings({numCollections: 1});
         await microtasksFinished();
         const collection = $$(categoriesElement, '.collection');
         assertTrue(!!collection);
-        const img1 = collection!.querySelector<CrAutoImgElement>('img');
-        assertTrue(!!img1);
-
         if (errorDetectionEnabled) {
           assertFalse(isVisible(collection));
         }
 
+        const img1 = collection!.querySelector<CrAutoImgElement>('img');
+        assertTrue(!!img1);
         img1.dispatchEvent(new Event('load'));
 
         await microtasksFinished();
@@ -155,13 +168,44 @@
             metrics.count(
                 'NewTabPage.Images.ShownTime.CollectionPreviewImage'));
       });
+
+      test('error detection metrics fire correctly', async () => {
+        const numCollections = 2;
+        await setInitialSettings({numCollections: 2});
+
+        const images =
+            categoriesElement.shadowRoot!.querySelectorAll<CrAutoImgElement>(
+                '.collection img');
+        assertEquals(numCollections, images.length);
+        const img1Error = eventToPromise('error', images[0]!);
+        const img2Error = eventToPromise('error', images[1]!);
+        await Promise.all([img1Error, img2Error]);
+        await microtasksFinished();
+
+        if (!errorDetectionEnabled) {
+          assertEquals(
+              0,
+              metrics.count(
+                  'NewTabPage.BackgroundService.Images.Headers.ErrorDetected'));
+        } else {
+          assertEquals(
+              2,
+              metrics.count(
+                  'NewTabPage.BackgroundService.Images.Headers.ErrorDetected'));
+          assertEquals(
+              2,
+              metrics.count(
+                  'NewTabPage.BackgroundService.Images.Headers.ErrorDetected',
+                  NtpImageType.COLLECTIONS));
+        }
+      });
     });
   });
 
   test('collection preview images create metrics when loaded', async () => {
     const startTime = 123.45;
     windowProxy.setResultFor('now', startTime);
-    await setInitialSettings(1);
+    await setInitialSettings({numCollections: 1});
     assertEquals(1, windowProxy.getCallCount('now'));
     const imageLoadTime = 678.90;
     windowProxy.setResultFor('now', imageLoadTime);
@@ -180,7 +224,7 @@
   });
 
   test('clicking collection sends event', async () => {
-    await setInitialSettings(1);
+    await setInitialSettings({numCollections: 1});
 
     const eventPromise = eventToPromise('collection-select', categoriesElement);
     const category =
@@ -193,7 +237,7 @@
   });
 
   test('back button creates event', async () => {
-    await setInitialSettings(0);
+    await setInitialSettings({numCollections: 0});
     const eventPromise = eventToPromise('back-click', categoriesElement);
     categoriesElement.$.heading.getBackButton().click();
     const event = await eventPromise;
@@ -201,7 +245,7 @@
   });
 
   test('clicking classic chrome sets theme', async () => {
-    await setInitialSettings(0);
+    await setInitialSettings({numCollections: 0});
     categoriesElement.$.classicChromeTile.click();
     assertEquals(1, handler.getCallCount('removeBackgroundImage'));
     assertEquals(1, handler.getCallCount('setDefaultColor'));
@@ -214,7 +258,7 @@
         loadTimeData.overrideValues({
           updatedToUploadedImage: 'Theme updated to uploaded image',
         });
-        await setInitialSettings(0);
+        await setInitialSettings({numCollections: 0});
         handler.setResultFor('chooseLocalCustomBackground', Promise.resolve({
           success: true,
         }));
@@ -239,14 +283,14 @@
       });
 
   test('clicking Chrome Web Store tile opens Chrome Web Store', async () => {
-    await setInitialSettings(0);
+    await setInitialSettings({numCollections: 0});
 
     categoriesElement.$.chromeWebStoreTile.click();
     assertEquals(1, handler.getCallCount('openChromeWebStore'));
   });
 
   test('checks selected category', async () => {
-    await setInitialSettings(2);
+    await setInitialSettings({numCollections: 2});
 
     // Set an empty theme with no color and no background.
     const theme = createTheme();
@@ -314,7 +358,7 @@
   });
 
   test('help bubble can correctly find anchor elements', async () => {
-    await setInitialSettings(5);
+    await setInitialSettings({numCollections: 5});
     assertDeepEquals(
         categoriesElement.getSortedAnchorStatusesForTesting(),
         [
@@ -325,7 +369,7 @@
   });
 
   test('classic chrome tile shows correct image', async () => {
-    await setInitialSettings(0);
+    await setInitialSettings({numCollections: 0});
 
     assertEquals(
         $$<HTMLImageElement>(
@@ -346,7 +390,7 @@
       test(
           `wallpaper search does ${flagEnabled ? '' : 'not '}show`,
           async () => {
-            await setInitialSettings(0);
+            await setInitialSettings({numCollections: 0});
             assertEquals(
                 !!categoriesElement.shadowRoot!.querySelector(
                     '#wallpaperSearchTile'),
@@ -354,7 +398,7 @@
           });
 
       test('check category for wallpaper search background', async () => {
-        await setInitialSettings(1);
+        await setInitialSettings({numCollections: 1});
 
         // Set a theme with wallpaper search background.
         const theme = createTheme();
@@ -392,7 +436,7 @@
     });
 
     test('choosing collection sets metric', async () => {
-      await setInitialSettings(1);
+      await setInitialSettings({numCollections: 1});
 
       const tile = categoriesElement.shadowRoot!.querySelector('.collection');
       assertTrue(!!tile);
@@ -409,7 +453,7 @@
     });
 
     test('choosing default chrome sets metric', async () => {
-      await setInitialSettings(0);
+      await setInitialSettings({numCollections: 0});
 
       categoriesElement.$.classicChromeTile.click();
 
@@ -423,7 +467,7 @@
     });
 
     test('choosing wallpaper search sets metric', async () => {
-      await setInitialSettings(0);
+      await setInitialSettings({numCollections: 0});
 
       const tile =
           categoriesElement.shadowRoot!.querySelector('#wallpaperSearchTile');
@@ -440,7 +484,7 @@
     });
 
     test('choosing upload sets metric', async () => {
-      await setInitialSettings(0);
+      await setInitialSettings({numCollections: 0});
 
       categoriesElement.$.uploadImageTile.click();
 
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/themes_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/themes_test.ts
index 2482f8c..8e9eba6 100644
--- a/chrome/test/data/webui/side_panel/customize_chrome/themes_test.ts
+++ b/chrome/test/data/webui/side_panel/customize_chrome/themes_test.ts
@@ -4,7 +4,7 @@
 
 import 'chrome://customize-chrome-side-panel.top-chrome/themes.js';
 
-import {CustomizeChromeAction} from 'chrome://customize-chrome-side-panel.top-chrome/common.js';
+import {CustomizeChromeAction, NtpImageType} from 'chrome://customize-chrome-side-panel.top-chrome/common.js';
 import type {BackgroundCollection, CollectionImage, CustomizeChromePageRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
 import {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
 import {CustomizeChromeApiProxy} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome_api_proxy.js';
@@ -98,13 +98,13 @@
   });
 
   test('get collection images when collection changes', async () => {
-    let numThemes = 3;
-    await setCollection('test1', numThemes);
+    let numImages = 3;
+    await setCollection('test1', numImages);
 
     let header = themesElement.$.heading;
     assertEquals('test1', header.textContent!.trim());
     let themes = themesElement.shadowRoot!.querySelectorAll('.theme');
-    assertEquals(themes.length, numThemes);
+    assertEquals(themes.length, numImages);
     assertEquals(
         'https://preview_1.jpg',
         themes[0]!.querySelector('img')!.getAttribute('auto-src'));
@@ -115,13 +115,13 @@
         'https://preview_3.jpg',
         themes[2]!.querySelector('img')!.getAttribute('auto-src'));
 
-    numThemes = 5;
-    await setCollection('test2', numThemes);
+    numImages = 5;
+    await setCollection('test2', numImages);
 
     header = themesElement.$.heading;
     assertEquals('test2', header.textContent!.trim());
     themes = themesElement.shadowRoot!.querySelectorAll('.theme');
-    assertEquals(themes.length, numThemes);
+    assertEquals(themes.length, numImages);
     assertEquals(
         'https://preview_1.jpg',
         themes[0]!.querySelector('img')!.getAttribute('auto-src'));
@@ -350,11 +350,11 @@
       });
 
       test('theme visibility based on error detection', async () => {
-        const numThemes = 2;
-        await setCollection('test1', numThemes);
+        const numImages = 2;
+        await setCollection('test1', numImages);
 
         const themes = themesElement.shadowRoot!.querySelectorAll('.theme');
-        assertEquals(numThemes, themes.length);
+        assertEquals(numImages, themes.length);
         if (!errorDetectionEnabled) {
           assertTrue(isVisible(themes[0]!));
           assertTrue(isVisible(themes[1]!));
@@ -369,17 +369,43 @@
 
         const theme = $$(themesElement, '.theme');
         assertTrue(!!theme);
-        const img1 = theme!.querySelector<CrAutoImgElement>('img');
-        assertTrue(!!img1);
+        const img = theme!.querySelector<CrAutoImgElement>('img');
+        assertTrue(!!img);
 
         if (errorDetectionEnabled) {
           assertFalse(isVisible(theme));
         }
 
-        img1.dispatchEvent(new Event('load'));
+        img.dispatchEvent(new Event('load'));
+        await microtasksFinished();
+
+        assertTrue(isVisible(theme));
+      });
+
+      test('error detection metrics fire correctly', async () => {
+        const numImages = 1;
+        await setCollection('test1', numImages);
+        const img = $$(themesElement, '.theme img');
+        assertTrue(!!img);
 
         await microtasksFinished();
-        assertTrue(isVisible(theme));
+
+        if (!errorDetectionEnabled) {
+          assertEquals(
+              0,
+              metrics.count(
+                  'NewTabPage.BackgroundService.Images.Headers.ErrorDetected'));
+        } else {
+          assertEquals(
+              numImages,
+              metrics.count(
+                  'NewTabPage.BackgroundService.Images.Headers.ErrorDetected'));
+          assertEquals(
+              numImages,
+              metrics.count(
+                  'NewTabPage.BackgroundService.Images.Headers.ErrorDetected',
+                  NtpImageType.BACKGROUND_IMAGE));
+        }
       });
     });
   });
diff --git a/chrome/test/interaction/README.md b/chrome/test/interaction/README.md
index 77b1f5be..5ac3db9 100644
--- a/chrome/test/interaction/README.md
+++ b/chrome/test/interaction/README.md
@@ -146,6 +146,8 @@
     - `ScrollIntoView()` [Views, Browser]
       - Recommended before doing anything that needs the screen coordinates of
         a UI or DOM element that is in a scrollable container.
+    - `ClickElement()` [Browser]
+      - For use with instrumented webcontents; see below.
 - **Mouse** verbs simulate mouse input to the entire application, and are
   therefore only reliable in test fixtures that run as exclusive processes (e.g.
   interactive_browser_tests). Examples include:
diff --git a/chrome/test/interaction/interactive_browser_test.cc b/chrome/test/interaction/interactive_browser_test.cc
index cc310100..6ae3aeb 100644
--- a/chrome/test/interaction/interactive_browser_test.cc
+++ b/chrome/test/interaction/interactive_browser_test.cc
@@ -40,6 +40,7 @@
 #include "ui/base/interaction/interaction_sequence.h"
 #include "ui/base/interaction/interaction_test_util.h"
 #include "ui/base/interaction/interactive_test_internal.h"
+#include "ui/base/test/ui_controls.h"
 #include "ui/views/controls/webview/webview.h"
 #include "ui/views/interaction/interaction_test_util_views.h"
 #include "ui/views/interaction/interactive_views_test.h"
@@ -762,6 +763,70 @@
           .SetDescription("ScrollIntoView()"));
 }
 
+ui::InteractionSequence::StepBuilder InteractiveBrowserTestApi::ClickElement(
+    ui::ElementIdentifier web_contents,
+    const DeepQuery& where,
+    ui_controls::MouseButton button,
+    ui_controls::AcceleratorState modifiers) {
+  int js_button;
+  switch (button) {
+    case ui_controls::LEFT:
+      js_button = 0;
+      break;
+    case ui_controls::MIDDLE:
+      js_button = 1;
+      break;
+    case ui_controls::RIGHT:
+      js_button = 2;
+      break;
+  }
+
+  const bool shift = modifiers & ui_controls::AcceleratorState::kShift;
+  const bool alt = modifiers & ui_controls::AcceleratorState::kAlt;
+  const bool ctrl = modifiers & ui_controls::AcceleratorState::kControl;
+  const bool meta = modifiers & ui_controls::AcceleratorState::kCommand;
+
+  auto b2s = [](bool b) { return b ? "true" : "false"; };
+
+  const std::string command = base::StringPrintf(
+      R"(
+      function(el) {
+        const rect = el.getBoundingClientRect();
+        const left = Math.max(0, rect.x);
+        const top = Math.max(0, rect.y);
+        const right = Math.min(rect.x + rect.width, window.innerWidth);
+        const bottom = Math.min(rect.y + rect.height, window.innerHeight);
+        if (right <= left || bottom <= top) {
+          throw new Error(
+              'Target element is zero size or ' +
+              'has empty intersection with the viewport.');
+        }
+        const x = (left + right) / 2;
+        const y = (top + bottom) / 2;
+
+        const event = new MouseEvent(
+            'click',
+            {
+              bubbles: true,
+              cancelable: true,
+              clientX: x,
+              clientY: y,
+              button: %d,
+              shiftKey: %s,
+              altKey: %s,
+              ctrlKey: %s,
+              metaKey: %s
+            }
+        );
+        el.dispatchEvent(event);
+      }
+    )",
+      js_button, b2s(shift), b2s(alt), b2s(ctrl), b2s(meta));
+
+  return std::move(ExecuteJsAt(web_contents, where, command)
+                       .SetDescription("ClickElement()"));
+}
+
 // static
 InteractiveBrowserTestApi::RelativePositionCallback
 InteractiveBrowserTestApi::DeepQueryToRelativePosition(const DeepQuery& query) {
diff --git a/chrome/test/interaction/interactive_browser_test.h b/chrome/test/interaction/interactive_browser_test.h
index 360e034..75c1eba7 100644
--- a/chrome/test/interaction/interactive_browser_test.h
+++ b/chrome/test/interaction/interactive_browser_test.h
@@ -358,6 +358,22 @@
   [[nodiscard]] StepBuilder ScrollIntoView(ui::ElementIdentifier web_contents,
                                            const DeepQuery& where);
 
+  // Simulates clicking on an HTML element by injecting the click event directly
+  // into the DOM. You can specify the mouse button and additional modifier
+  // keys (default is left-click, no modifiers).
+  //
+  // Normally, clicking with buttons other than the left mouse button generates
+  // an auxclick event rather than a click event. However, injecting auxclick
+  // does not e.g. trigger navigation when clicking a link, so in all these
+  // cases, vanilla click events are sent, which should be handled normally for
+  // backwards-compatibility reasons.
+  [[nodiscard]] StepBuilder ClickElement(
+      ui::ElementIdentifier web_contents,
+      const DeepQuery& where,
+      ui_controls::MouseButton button = ui_controls::LEFT,
+      ui_controls::AcceleratorState modifiers =
+          ui_controls::AcceleratorState::kNoAccelerator);
+
  protected:
   explicit InteractiveBrowserTestApi(
       std::unique_ptr<internal::InteractiveBrowserTestPrivate>
diff --git a/chrome/test/interaction/interactive_browser_test_browsertest.cc b/chrome/test/interaction/interactive_browser_test_browsertest.cc
index 669177d..9e32acd8 100644
--- a/chrome/test/interaction/interactive_browser_test_browsertest.cc
+++ b/chrome/test/interaction/interactive_browser_test_browsertest.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/test/interaction/interactive_browser_test.h"
 
+#include <sstream>
+#include <tuple>
+
 #include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -19,6 +22,7 @@
 #include "ui/base/interaction/expect_call_in_scope.h"
 #include "ui/base/interaction/interaction_sequence.h"
 #include "ui/base/mojom/ui_base_types.mojom-shared.h"
+#include "ui/base/test/ui_controls.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/native_widget_types.h"
@@ -30,6 +34,7 @@
 DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kWebContentsId);
 constexpr char kDocumentWithNamedElement[] = "/select.html";
 constexpr char kDocumentWithLinks[] = "/links.html";
+constexpr char kDocumentWithClickDetection[] = "/click.html";
 constexpr char kScrollableDocument[] =
     "/scroll/scrollable_page_with_content.html";
 }  // namespace
@@ -561,6 +566,99 @@
                  WaitForStateChange(kTabId, state_change)));
 }
 
+using ClickElementParams =
+    std::tuple<ui_controls::MouseButton, ui_controls::AcceleratorState>;
+
+class InteractiveBrowserTestClickElementTest
+    : public InteractiveBrowserTestBrowsertest,
+      public testing::WithParamInterface<ClickElementParams> {
+ public:
+  InteractiveBrowserTestClickElementTest() = default;
+  ~InteractiveBrowserTestClickElementTest() override = default;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    InteractiveBrowserTestClickElementTest,
+    testing::Combine(
+        testing::Values(ui_controls::LEFT,
+                        ui_controls::MIDDLE,
+                        ui_controls::RIGHT),
+        testing::Values(ui_controls::AcceleratorState::kNoAccelerator,
+                        ui_controls::AcceleratorState::kShift,
+                        ui_controls::AcceleratorState::kControl,
+                        ui_controls::AcceleratorState::kAlt,
+                        ui_controls::AcceleratorState::kCommand,
+                        static_cast<ui_controls::AcceleratorState>(
+                            ui_controls::AcceleratorState::kAlt |
+                            ui_controls::AcceleratorState::kShift),
+                        static_cast<ui_controls::AcceleratorState>(
+                            ui_controls::AcceleratorState::kControl |
+                            ui_controls::AcceleratorState::kCommand |
+                            ui_controls::AcceleratorState::kAlt |
+                            ui_controls::AcceleratorState::kShift))),
+    [](const testing::TestParamInfo<ClickElementParams>& params) {
+      std::ostringstream oss;
+      switch (std::get<0>(params.param)) {
+        case ui_controls::LEFT:
+          oss << "Left";
+          break;
+        case ui_controls::MIDDLE:
+          oss << "Middle";
+          break;
+        case ui_controls::RIGHT:
+          oss << "Right";
+          break;
+      }
+      const auto accel = std::get<1>(params.param);
+      if (accel & ui_controls::AcceleratorState::kControl) {
+        oss << "_Control";
+      }
+      if (accel & ui_controls::AcceleratorState::kAlt) {
+        oss << "_Alt";
+      }
+      if (accel & ui_controls::AcceleratorState::kShift) {
+        oss << "_Shift";
+      }
+      if (accel & ui_controls::AcceleratorState::kCommand) {
+        oss << "_Meta";
+      }
+      return oss.str();
+    });
+
+IN_PROC_BROWSER_TEST_P(InteractiveBrowserTestClickElementTest, ClickElement) {
+  const GURL url = embedded_test_server()->GetURL(kDocumentWithClickDetection);
+  const auto mouse_button = std::get<0>(GetParam());
+  const auto modifier = std::get<1>(GetParam());
+  const DeepQuery kButton = {"#button"};
+  RunTestSequence(
+      InstrumentTab(kWebContentsId), NavigateWebContents(kWebContentsId, url),
+      ClickElement(kWebContentsId, kButton, mouse_button, modifier),
+      CheckJsResultAt(kWebContentsId, kButton, "el => el.lastClickEvent.button",
+                      static_cast<int>(mouse_button)),
+      CheckJsResultAt(kWebContentsId, kButton, "el => el.lastClickEvent.altKey",
+                      (modifier & ui_controls::AcceleratorState::kAlt) != 0),
+      CheckJsResultAt(kWebContentsId, kButton,
+                      "el => el.lastClickEvent.shiftKey",
+                      (modifier & ui_controls::AcceleratorState::kShift) != 0),
+      CheckJsResultAt(
+          kWebContentsId, kButton, "el => el.lastClickEvent.ctrlKey",
+          (modifier & ui_controls::AcceleratorState::kControl) != 0),
+      CheckJsResultAt(
+          kWebContentsId, kButton, "el => el.lastClickEvent.metaKey",
+          (modifier & ui_controls::AcceleratorState::kCommand) != 0),
+      CheckJsResultAt(kWebContentsId, kButton,
+                      R"(
+            function(el) {
+              const x = el.lastClickEvent.x;
+              const y = el.lastClickEvent.y;
+              const rect = el.getBoundingClientRect();
+              return x >= rect.left && x < rect.right &&
+                     y >= rect.top && y < rect.bottom;
+            }
+          )"));
+}
+
 // Parameter for WebUI coverage tests.
 struct CoverageConfig {
   // Whether to set the --devtools-code-coverage flag. If it's not set, nothing
diff --git a/chromecast/media/cma/backend/android/java/src/org/chromium/chromecast/cma/backend/android/AudioSinkAudioTrackImpl.java b/chromecast/media/cma/backend/android/java/src/org/chromium/chromecast/cma/backend/android/AudioSinkAudioTrackImpl.java
index 848e5c14..2626ee8 100644
--- a/chromecast/media/cma/backend/android/java/src/org/chromium/chromecast/cma/backend/android/AudioSinkAudioTrackImpl.java
+++ b/chromecast/media/cma/backend/android/java/src/org/chromium/chromecast/cma/backend/android/AudioSinkAudioTrackImpl.java
@@ -454,11 +454,8 @@
         return (playtimeLeftNsecs < 0) ? 0 : playtimeLeftNsecs / 1000; // return usecs
     }
 
+    /** Closes the instance by stopping playback and releasing the AudioTrack object. */
     @CalledByNative
-    /**
-     * Closes the instance by stopping playback and releasing the AudioTrack
-     * object.
-     */
     private void close() {
         Log.i(mTag, "Close AudioSinkAudioTrackImpl!");
         if (!isStopped()) mAudioTrack.stop();
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 2585f39..ce8a865 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-16049.0.0-1063698
\ No newline at end of file
+16050.0.0-1063704
\ No newline at end of file
diff --git a/chromeos/ash/components/boca/babelorca/BUILD.gn b/chromeos/ash/components/boca/babelorca/BUILD.gn
index 67e203b..26e69b8 100644
--- a/chromeos/ash/components/boca/babelorca/BUILD.gn
+++ b/chromeos/ash/components/boca/babelorca/BUILD.gn
@@ -25,7 +25,8 @@
     "tachyon_registrar.cc",
     "tachyon_registrar.h",
     "tachyon_request_data_provider.h",
-    "tachyon_request_error.h",
+    "tachyon_response.cc",
+    "tachyon_response.h",
     "tachyon_utils.cc",
     "tachyon_utils.h",
     "token_data_wrapper.h",
@@ -83,6 +84,7 @@
     "tachyon_authed_client_impl_unittest.cc",
     "tachyon_client_impl_unittest.cc",
     "tachyon_registrar_unittest.cc",
+    "tachyon_response_unittest.cc",
     "token_manager_impl_unittest.cc",
     "transcript_sender_unittest.cc",
   ]
diff --git a/chromeos/ash/components/boca/babelorca/fakes/fake_tachyon_authed_client.cc b/chromeos/ash/components/boca/babelorca/fakes/fake_tachyon_authed_client.cc
index 054d42f..9692683 100644
--- a/chromeos/ash/components/boca/babelorca/fakes/fake_tachyon_authed_client.cc
+++ b/chromeos/ash/components/boca/babelorca/fakes/fake_tachyon_authed_client.cc
@@ -12,7 +12,7 @@
 #include "base/run_loop.h"
 #include "base/types/expected.h"
 #include "chromeos/ash/components/boca/babelorca/request_data_wrapper.h"
-#include "chromeos/ash/components/boca/babelorca/tachyon_request_error.h"
+#include "chromeos/ash/components/boca/babelorca/tachyon_response.h"
 #include "third_party/protobuf/src/google/protobuf/message_lite.h"
 
 namespace ash::babelorca {
@@ -40,7 +40,7 @@
 }
 
 void FakeTachyonAuthedClient::ExecuteResponseCallback(
-    base::expected<std::string, TachyonRequestError> response) {
+    TachyonResponse response) {
   CHECK(response_cb_);
   std::move(response_cb_).Run(std::move(response));
 }
diff --git a/chromeos/ash/components/boca/babelorca/fakes/fake_tachyon_authed_client.h b/chromeos/ash/components/boca/babelorca/fakes/fake_tachyon_authed_client.h
index 4ad3db5..6686098 100644
--- a/chromeos/ash/components/boca/babelorca/fakes/fake_tachyon_authed_client.h
+++ b/chromeos/ash/components/boca/babelorca/fakes/fake_tachyon_authed_client.h
@@ -9,13 +9,13 @@
 #include <string>
 
 #include "base/run_loop.h"
-#include "base/types/expected.h"
 #include "chromeos/ash/components/boca/babelorca/request_data_wrapper.h"
 #include "chromeos/ash/components/boca/babelorca/tachyon_authed_client.h"
-#include "chromeos/ash/components/boca/babelorca/tachyon_request_error.h"
 
 namespace ash::babelorca {
 
+class TachyonResponse;
+
 class FakeTachyonAuthedClient : public TachyonAuthedClient {
  public:
   FakeTachyonAuthedClient();
@@ -33,8 +33,7 @@
       std::unique_ptr<RequestDataWrapper> request_data,
       std::string request_string) override;
 
-  void ExecuteResponseCallback(
-      base::expected<std::string, TachyonRequestError> response);
+  void ExecuteResponseCallback(TachyonResponse response);
 
   RequestDataWrapper::ResponseCallback TakeResponseCallback();
 
diff --git a/chromeos/ash/components/boca/babelorca/request_data_wrapper.h b/chromeos/ash/components/boca/babelorca/request_data_wrapper.h
index 8285249..2d60e47 100644
--- a/chromeos/ash/components/boca/babelorca/request_data_wrapper.h
+++ b/chromeos/ash/components/boca/babelorca/request_data_wrapper.h
@@ -9,15 +9,14 @@
 #include <string_view>
 
 #include "base/functional/callback.h"
-#include "base/types/expected.h"
-#include "chromeos/ash/components/boca/babelorca/tachyon_request_error.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 
 namespace ash::babelorca {
 
+class TachyonResponse;
+
 struct RequestDataWrapper {
-  using ResponseCallback = base::OnceCallback<void(
-      base::expected<std::string, TachyonRequestError>)>;
+  using ResponseCallback = base::OnceCallback<void(TachyonResponse)>;
 
   RequestDataWrapper(
       const net::NetworkTrafficAnnotationTag& annotation_tag_param,
diff --git a/chromeos/ash/components/boca/babelorca/tachyon_authed_client_impl.cc b/chromeos/ash/components/boca/babelorca/tachyon_authed_client_impl.cc
index a900e4f..53501289 100644
--- a/chromeos/ash/components/boca/babelorca/tachyon_authed_client_impl.cc
+++ b/chromeos/ash/components/boca/babelorca/tachyon_authed_client_impl.cc
@@ -16,10 +16,9 @@
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
 #include "base/task/thread_pool.h"
-#include "base/types/expected.h"
 #include "chromeos/ash/components/boca/babelorca/request_data_wrapper.h"
 #include "chromeos/ash/components/boca/babelorca/tachyon_client.h"
-#include "chromeos/ash/components/boca/babelorca/tachyon_request_error.h"
+#include "chromeos/ash/components/boca/babelorca/tachyon_response.h"
 #include "chromeos/ash/components/boca/babelorca/token_manager.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "third_party/protobuf/src/google/protobuf/message_lite.h"
@@ -70,7 +69,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!request_string) {
     std::move(request_data->response_cb)
-        .Run(base::unexpected(TachyonRequestError::kInternalError));
+        .Run(TachyonResponse(TachyonResponse::Status::kInternalError));
     return;
   }
   request_data->content_data = std::move(*request_string);
@@ -91,7 +90,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!has_oauth_token) {
     std::move(request_data->response_cb)
-        .Run(base::unexpected(TachyonRequestError::kAuthError));
+        .Run(TachyonResponse(TachyonResponse::Status::kAuthError));
     return;
   }
   std::string oauth_token = *(oauth_token_manager_->GetTokenString());
@@ -108,7 +107,7 @@
   static int constexpr kMaxAuthRetries = 1;
   if (request_data->oauth_retry_num >= kMaxAuthRetries) {
     std::move(request_data->response_cb)
-        .Run(base::unexpected(TachyonRequestError::kAuthError));
+        .Run(TachyonResponse(TachyonResponse::Status::kAuthError));
     return;
   }
   ++(request_data->oauth_retry_num);
diff --git a/chromeos/ash/components/boca/babelorca/tachyon_authed_client_impl_unittest.cc b/chromeos/ash/components/boca/babelorca/tachyon_authed_client_impl_unittest.cc
index 3381d50..cdf95ff9 100644
--- a/chromeos/ash/components/boca/babelorca/tachyon_authed_client_impl_unittest.cc
+++ b/chromeos/ash/components/boca/babelorca/tachyon_authed_client_impl_unittest.cc
@@ -11,12 +11,11 @@
 #include "base/memory/raw_ptr.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
-#include "base/types/expected.h"
 #include "chromeos/ash/components/boca/babelorca/fakes/fake_tachyon_client.h"
 #include "chromeos/ash/components/boca/babelorca/fakes/fake_token_manager.h"
 #include "chromeos/ash/components/boca/babelorca/proto/testing_message.pb.h"
 #include "chromeos/ash/components/boca/babelorca/request_data_wrapper.h"
-#include "chromeos/ash/components/boca/babelorca/tachyon_request_error.h"
+#include "chromeos/ash/components/boca/babelorca/tachyon_response.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -24,8 +23,6 @@
 namespace ash::babelorca {
 namespace {
 
-using ExpectedTestingMessage = base::expected<std::string, TachyonRequestError>;
-
 constexpr char kOAuthToken1[] = "oauth-token1";
 constexpr char kOAuthToken2[] = "oauth-token2";
 constexpr int kMaxRetries = 2;
@@ -64,7 +61,7 @@
                                                 test_future_.GetCallback());
   }
 
-  base::test::TestFuture<ExpectedTestingMessage>* test_future() {
+  base::test::TestFuture<TachyonResponse>* test_future() {
     return &test_future_;
   }
 
@@ -76,7 +73,7 @@
   FakeTokenManager fake_token_manager_;
   std::unique_ptr<TestingMessage> request_message_;
   std::string request_string_;
-  base::test::TestFuture<ExpectedTestingMessage> test_future_;
+  base::test::TestFuture<TachyonResponse> test_future_;
 };
 
 TEST_F(TachyonAuthedClientImplTest, InitiallyAuthed) {
@@ -215,8 +212,7 @@
   fake_client_ptr()->WaitForRequest();
   fake_client_ptr()->ExecuteAuthFailCb();
 
-  EXPECT_EQ(test_future()->Get(),
-            base::unexpected(TachyonRequestError::kAuthError));
+  EXPECT_EQ(test_future()->Get().status(), TachyonResponse::Status::kAuthError);
 }
 
 TEST_F(TachyonAuthedClientImplTest, TokenFetchFailed) {
@@ -226,8 +222,7 @@
   fake_token_manager()->WaitForForceFetchRequest();
   fake_token_manager()->ExecuteFetchCallback(/*success=*/false);
 
-  EXPECT_EQ(test_future()->Get(),
-            base::unexpected(TachyonRequestError::kAuthError));
+  EXPECT_EQ(test_future()->Get().status(), TachyonResponse::Status::kAuthError);
 }
 
 }  // namespace
diff --git a/chromeos/ash/components/boca/babelorca/tachyon_client_impl.cc b/chromeos/ash/components/boca/babelorca/tachyon_client_impl.cc
index 003d4a0..270b022 100644
--- a/chromeos/ash/components/boca/babelorca/tachyon_client_impl.cc
+++ b/chromeos/ash/components/boca/babelorca/tachyon_client_impl.cc
@@ -5,6 +5,7 @@
 #include "chromeos/ash/components/boca/babelorca/tachyon_client_impl.h"
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 
@@ -13,9 +14,8 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/stringprintf.h"
-#include "base/types/expected.h"
 #include "chromeos/ash/components/boca/babelorca/request_data_wrapper.h"
-#include "chromeos/ash/components/boca/babelorca/tachyon_request_error.h"
+#include "chromeos/ash/components/boca/babelorca/tachyon_response.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_request_headers.h"
@@ -80,34 +80,17 @@
     std::unique_ptr<RequestDataWrapper> request_data,
     AuthFailureCallback auth_failure_cb,
     std::unique_ptr<std::string> response_body) {
-  if (url_loader->NetError() != net::OK &&
-      url_loader->NetError() != net::ERR_HTTP_RESPONSE_CODE_FAILURE) {
-    std::move(request_data->response_cb)
-        .Run(base::unexpected(TachyonRequestError::kNetworkError));
-    return;
+  std::optional<int> http_status_code;
+  if (url_loader->ResponseInfo() && url_loader->ResponseInfo()->headers) {
+    http_status_code = url_loader->ResponseInfo()->headers->response_code();
   }
-  if (!url_loader->ResponseInfo() || !url_loader->ResponseInfo()->headers) {
-    std::move(request_data->response_cb)
-        .Run(base::unexpected(TachyonRequestError::kInternalError));
-    return;
-  }
-  const int response_code =
-      url_loader->ResponseInfo()->headers->response_code();
-  if (response_code == net::HttpStatusCode::HTTP_UNAUTHORIZED) {
+  TachyonResponse response(url_loader->NetError(), http_status_code,
+                           std::move(response_body));
+  if (response.status() == TachyonResponse::Status::kAuthError) {
     std::move(auth_failure_cb).Run(std::move(request_data));
     return;
   }
-  if (!network::IsSuccessfulStatus(response_code)) {
-    std::move(request_data->response_cb)
-        .Run(base::unexpected(TachyonRequestError::kHttpError));
-    return;
-  }
-  if (!response_body) {
-    std::move(request_data->response_cb)
-        .Run(base::unexpected(TachyonRequestError::kInternalError));
-    return;
-  }
-  std::move(request_data->response_cb).Run(std::move(*response_body));
+  std::move(request_data->response_cb).Run(std::move(response));
 }
 
 }  // namespace ash::babelorca
diff --git a/chromeos/ash/components/boca/babelorca/tachyon_client_impl_unittest.cc b/chromeos/ash/components/boca/babelorca/tachyon_client_impl_unittest.cc
index 6d7b4ad2..01cd300 100644
--- a/chromeos/ash/components/boca/babelorca/tachyon_client_impl_unittest.cc
+++ b/chromeos/ash/components/boca/babelorca/tachyon_client_impl_unittest.cc
@@ -10,10 +10,9 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
-#include "base/types/expected.h"
 #include "chromeos/ash/components/boca/babelorca/proto/testing_message.pb.h"
 #include "chromeos/ash/components/boca/babelorca/request_data_wrapper.h"
-#include "chromeos/ash/components/boca/babelorca/tachyon_request_error.h"
+#include "chromeos/ash/components/boca/babelorca/tachyon_response.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_status_code.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
@@ -27,7 +26,6 @@
 namespace ash::babelorca {
 namespace {
 
-using ExpectedTestingMessage = base::expected<std::string, TachyonRequestError>;
 using RequestDataPtr = std::unique_ptr<RequestDataWrapper>;
 
 constexpr char kOAuthToken[] = "oauth-token";
@@ -50,13 +48,13 @@
     return &auth_failure_future_;
   }
 
-  base::test::TestFuture<ExpectedTestingMessage>* result_future() {
+  base::test::TestFuture<TachyonResponse>* result_future() {
     return &result_future_;
   }
 
  private:
   base::test::TestFuture<RequestDataPtr> auth_failure_future_;
-  base::test::TestFuture<ExpectedTestingMessage> result_future_;
+  base::test::TestFuture<TachyonResponse> result_future_;
   base::test::TaskEnvironment task_env_;
 };
 
@@ -70,10 +68,10 @@
   client.StartRequest(request_data(), kOAuthToken,
                       auth_failure_future()->GetCallback());
 
-  auto result = result_future()->Get();
-  ASSERT_TRUE(result.has_value());
+  auto result = result_future()->Take();
+  EXPECT_TRUE(result.ok());
   TestingMessage result_proto;
-  ASSERT_TRUE(result_proto.ParseFromString(result.value()));
+  ASSERT_TRUE(result_proto.ParseFromString(result.response_body()));
   EXPECT_EQ(result_proto.int_field(), 9999);
   EXPECT_FALSE(auth_failure_future()->IsReady());
 }
@@ -88,9 +86,8 @@
   client.StartRequest(request_data(), kOAuthToken,
                       auth_failure_future()->GetCallback());
 
-  auto result = result_future()->Get();
-  ASSERT_FALSE(result.has_value());
-  EXPECT_EQ(result.error(), TachyonRequestError::kNetworkError);
+  auto result = result_future()->Take();
+  EXPECT_EQ(result.status(), TachyonResponse::Status::kNetworkError);
   EXPECT_FALSE(auth_failure_future()->IsReady());
 }
 
@@ -103,9 +100,8 @@
   client.StartRequest(request_data(), kOAuthToken,
                       auth_failure_future()->GetCallback());
 
-  auto result = result_future()->Get();
-  ASSERT_FALSE(result.has_value());
-  EXPECT_EQ(result.error(), TachyonRequestError::kHttpError);
+  auto result = result_future()->Take();
+  EXPECT_EQ(result.status(), TachyonResponse::Status::kHttpError);
   EXPECT_FALSE(auth_failure_future()->IsReady());
 }
 
diff --git a/chromeos/ash/components/boca/babelorca/tachyon_registrar.cc b/chromeos/ash/components/boca/babelorca/tachyon_registrar.cc
index 28d13a1..5041ebac 100644
--- a/chromeos/ash/components/boca/babelorca/tachyon_registrar.cc
+++ b/chromeos/ash/components/boca/babelorca/tachyon_registrar.cc
@@ -12,7 +12,6 @@
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/sequence_checker.h"
-#include "base/types/expected.h"
 #include "base/uuid.h"
 #include "chromeos/ash/components/boca/babelorca/proto/tachyon.pb.h"
 #include "chromeos/ash/components/boca/babelorca/proto/tachyon_common.pb.h"
@@ -20,6 +19,7 @@
 #include "chromeos/ash/components/boca/babelorca/request_data_wrapper.h"
 #include "chromeos/ash/components/boca/babelorca/tachyon_authed_client.h"
 #include "chromeos/ash/components/boca/babelorca/tachyon_constants.h"
+#include "chromeos/ash/components/boca/babelorca/tachyon_response.h"
 #include "chromeos/ash/components/boca/babelorca/tachyon_utils.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 
@@ -69,16 +69,15 @@
   return tachyon_token_;
 }
 
-void TachyonRegistrar::OnResponse(
-    base::OnceCallback<void(bool)> success_cb,
-    base::expected<std::string, TachyonRequestError> response) {
+void TachyonRegistrar::OnResponse(base::OnceCallback<void(bool)> success_cb,
+                                  TachyonResponse response) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!response.has_value()) {
+  if (!response.ok()) {
     std::move(success_cb).Run(false);
     return;
   }
   SignInGaiaResponse signin_response;
-  if (!signin_response.ParseFromString(response.value())) {
+  if (!signin_response.ParseFromString(response.response_body())) {
     std::move(success_cb).Run(false);
     return;
   }
diff --git a/chromeos/ash/components/boca/babelorca/tachyon_registrar.h b/chromeos/ash/components/boca/babelorca/tachyon_registrar.h
index 7f079cf..3b14125 100644
--- a/chromeos/ash/components/boca/babelorca/tachyon_registrar.h
+++ b/chromeos/ash/components/boca/babelorca/tachyon_registrar.h
@@ -15,13 +15,12 @@
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
 #include "base/thread_annotations.h"
-#include "base/types/expected.h"
-#include "chromeos/ash/components/boca/babelorca/tachyon_request_error.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 
 namespace ash::babelorca {
 
 class TachyonAuthedClient;
+class TachyonResponse;
 
 // Register user with Tachyon and store tachyon token to be used by other
 // tachyon requests.
@@ -45,7 +44,7 @@
 
  private:
   void OnResponse(base::OnceCallback<void(bool)> success_cb,
-                  base::expected<std::string, TachyonRequestError> response);
+                  TachyonResponse response);
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/chromeos/ash/components/boca/babelorca/tachyon_registrar_unittest.cc b/chromeos/ash/components/boca/babelorca/tachyon_registrar_unittest.cc
index 8b9a0a9..4b5aa66d 100644
--- a/chromeos/ash/components/boca/babelorca/tachyon_registrar_unittest.cc
+++ b/chromeos/ash/components/boca/babelorca/tachyon_registrar_unittest.cc
@@ -9,10 +9,11 @@
 
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
-#include "base/types/expected.h"
 #include "chromeos/ash/components/boca/babelorca/fakes/fake_tachyon_authed_client.h"
 #include "chromeos/ash/components/boca/babelorca/proto/tachyon.pb.h"
-#include "chromeos/ash/components/boca/babelorca/tachyon_request_error.h"
+#include "chromeos/ash/components/boca/babelorca/tachyon_response.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_status_code.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -31,7 +32,9 @@
   registrar.Register(kClientUuid, test_future.GetCallback());
   SignInGaiaResponse signin_response;
   signin_response.mutable_auth_token()->set_payload(kTachyonToken);
-  authed_client.ExecuteResponseCallback(signin_response.SerializeAsString());
+  authed_client.ExecuteResponseCallback(TachyonResponse(
+      net::OK, net::HttpStatusCode::HTTP_OK,
+      std::make_unique<std::string>(signin_response.SerializeAsString())));
 
   EXPECT_TRUE(test_future.Get());
   std::optional<std::string> tachyon_token = registrar.GetTachyonToken();
@@ -47,7 +50,7 @@
 
   registrar.Register(kClientUuid, test_future.GetCallback());
   authed_client.ExecuteResponseCallback(
-      base::unexpected(TachyonRequestError::kHttpError));
+      TachyonResponse(TachyonResponse::Status::kHttpError));
 
   EXPECT_FALSE(test_future.Get());
   std::optional<std::string> tachyon_token = registrar.GetTachyonToken();
diff --git a/chromeos/ash/components/boca/babelorca/tachyon_request_error.h b/chromeos/ash/components/boca/babelorca/tachyon_request_error.h
deleted file mode 100644
index 41ae4f4..0000000
--- a/chromeos/ash/components/boca/babelorca/tachyon_request_error.h
+++ /dev/null
@@ -1,19 +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 CHROMEOS_ASH_COMPONENTS_BOCA_BABELORCA_TACHYON_REQUEST_ERROR_H_
-#define CHROMEOS_ASH_COMPONENTS_BOCA_BABELORCA_TACHYON_REQUEST_ERROR_H_
-
-namespace ash::babelorca {
-
-enum class TachyonRequestError {
-  kHttpError,
-  kNetworkError,
-  kInternalError,
-  kAuthError
-};
-
-}
-
-#endif  // CHROMEOS_ASH_COMPONENTS_BOCA_BABELORCA_TACHYON_REQUEST_ERROR_H_
diff --git a/chromeos/ash/components/boca/babelorca/tachyon_response.cc b/chromeos/ash/components/boca/babelorca/tachyon_response.cc
new file mode 100644
index 0000000..b2695a1
--- /dev/null
+++ b/chromeos/ash/components/boca/babelorca/tachyon_response.cc
@@ -0,0 +1,66 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/ash/components/boca/babelorca/tachyon_response.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "net/base/net_errors.h"
+#include "net/http/http_status_code.h"
+#include "services/network/public/cpp/header_util.h"
+
+namespace ash::babelorca {
+
+TachyonResponse::TachyonResponse(Status status) : status_(status) {}
+
+TachyonResponse::TachyonResponse(int rpc_code,
+                                 const std::string& error_message) {
+  constexpr int kOkCode = 0;
+  constexpr int kUnauthenticatedCode = 16;
+  switch (rpc_code) {
+    case kOkCode:
+      status_ = Status::kOk;
+      break;
+    case kUnauthenticatedCode:
+      status_ = Status::kAuthError;
+      break;
+    default:
+      status_ = Status::kHttpError;
+  }
+  error_message_ = error_message;
+}
+
+TachyonResponse::TachyonResponse(int net_error,
+                                 std::optional<int> http_status_code,
+                                 std::unique_ptr<std::string> response_body) {
+  if (net_error != net::OK &&
+      net_error != net::ERR_HTTP_RESPONSE_CODE_FAILURE) {
+    status_ = Status::kNetworkError;
+    return;
+  }
+  if (!http_status_code.has_value()) {
+    status_ = Status::kInternalError;
+    return;
+  }
+  if (http_status_code == net::HttpStatusCode::HTTP_UNAUTHORIZED) {
+    status_ = Status::kAuthError;
+    return;
+  }
+  if (!network::IsSuccessfulStatus(http_status_code.value())) {
+    status_ = Status::kHttpError;
+    return;
+  }
+  response_body_ = response_body ? std::move(*response_body) : "";
+  status_ = Status::kOk;
+}
+
+TachyonResponse::~TachyonResponse() = default;
+
+bool TachyonResponse::ok() const {
+  return status_ == Status::kOk;
+}
+
+}  // namespace ash::babelorca
diff --git a/chromeos/ash/components/boca/babelorca/tachyon_response.h b/chromeos/ash/components/boca/babelorca/tachyon_response.h
new file mode 100644
index 0000000..a2d7950c
--- /dev/null
+++ b/chromeos/ash/components/boca/babelorca/tachyon_response.h
@@ -0,0 +1,51 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_ASH_COMPONENTS_BOCA_BABELORCA_TACHYON_RESPONSE_H_
+#define CHROMEOS_ASH_COMPONENTS_BOCA_BABELORCA_TACHYON_RESPONSE_H_
+
+#include <memory>
+#include <string>
+
+namespace ash::babelorca {
+
+class TachyonResponse {
+ public:
+  enum class Status {
+    kOk,
+    kHttpError,
+    kNetworkError,
+    kInternalError,
+    kAuthError
+  };
+  explicit TachyonResponse(Status status);
+  explicit TachyonResponse(int rpc_code, const std::string& error_message = "");
+  TachyonResponse(int net_error,
+                  std::optional<int> http_status_code,
+                  std::unique_ptr<std::string> response_body);
+
+  TachyonResponse(TachyonResponse&& other) = default;
+  TachyonResponse& operator=(TachyonResponse&& other) = default;
+
+  ~TachyonResponse();
+
+  bool ok() const;
+
+  Status status() const { return status_; }
+
+  // Empty string if there is no response body.
+  const std::string& response_body() const { return response_body_; }
+
+  // Empty string if there is no error message.
+  const std::string& error_message() const { return error_message_; }
+
+ private:
+  Status status_;
+  std::string response_body_;
+  std::string error_message_;
+};
+
+}  // namespace ash::babelorca
+
+#endif  // CHROMEOS_ASH_COMPONENTS_BOCA_BABELORCA_TACHYON_RESPONSE_H_
diff --git a/chromeos/ash/components/boca/babelorca/tachyon_response_unittest.cc b/chromeos/ash/components/boca/babelorca/tachyon_response_unittest.cc
new file mode 100644
index 0000000..c414e574
--- /dev/null
+++ b/chromeos/ash/components/boca/babelorca/tachyon_response_unittest.cc
@@ -0,0 +1,96 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/ash/components/boca/babelorca/tachyon_response.h"
+
+#include <memory>
+#include <string>
+
+#include "net/base/net_errors.h"
+#include "net/http/http_status_code.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ash::babelorca {
+namespace {
+
+struct TachyonResponseTestCase {
+  std::string test_name;
+  int net_error;
+  std::optional<int> http_status_code;
+  std::string response_body;
+  TachyonResponse::Status expected_status;
+};
+
+using TachyonResponseTest = testing::TestWithParam<TachyonResponseTestCase>;
+
+TEST(TachyonResponseTest, InternalErrorStatus) {
+  TachyonResponse response(TachyonResponse::Status::kInternalError);
+  EXPECT_FALSE(response.ok());
+  EXPECT_EQ(response.status(), TachyonResponse::Status::kInternalError);
+  EXPECT_THAT(response.error_message(), testing::IsEmpty());
+  EXPECT_THAT(response.response_body(), testing::IsEmpty());
+}
+
+TEST(TachyonResponseTest, OkRpcCode) {
+  TachyonResponse response(/*rpc_code=*/0);
+  EXPECT_TRUE(response.ok());
+  EXPECT_EQ(response.status(), TachyonResponse::Status::kOk);
+  EXPECT_THAT(response.error_message(), testing::IsEmpty());
+  EXPECT_THAT(response.response_body(), testing::IsEmpty());
+}
+
+TEST(TachyonResponseTest, AuthErrorRpcCode) {
+  constexpr char kAuthErrorMessage[] = "auth error";
+  TachyonResponse response(/*rpc_code=*/16,
+                           /*error_message=*/kAuthErrorMessage);
+  EXPECT_FALSE(response.ok());
+  EXPECT_EQ(response.status(), TachyonResponse::Status::kAuthError);
+  EXPECT_THAT(response.error_message(), testing::StrEq(kAuthErrorMessage));
+  EXPECT_THAT(response.response_body(), testing::IsEmpty());
+}
+
+TEST(TachyonResponseTest, OtherRpcCode) {
+  constexpr char kResourcedMessage[] = "resource exhausted";
+  TachyonResponse response(/*rpc_code=*/8, /*error_message=*/kResourcedMessage);
+  EXPECT_FALSE(response.ok());
+  EXPECT_EQ(response.status(), TachyonResponse::Status::kHttpError);
+  EXPECT_THAT(response.error_message(), testing::StrEq(kResourcedMessage));
+  EXPECT_THAT(response.response_body(), testing::IsEmpty());
+}
+
+TEST_P(TachyonResponseTest, HttpHeader) {
+  TachyonResponse response(
+      GetParam().net_error, GetParam().http_status_code,
+      std::make_unique<std::string>(GetParam().response_body));
+  EXPECT_EQ(response.status(), GetParam().expected_status);
+  EXPECT_THAT(response.response_body(),
+              testing::StrEq(GetParam().response_body));
+  EXPECT_THAT(response.error_message(), testing::IsEmpty());
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    TachyonResponseTestSuiteInstantiation,
+    TachyonResponseTest,
+    testing::ValuesIn<TachyonResponseTestCase>({
+        {"NetError", net::ERR_TOO_MANY_RETRIES, std::nullopt, "",
+         TachyonResponse::Status::kNetworkError},
+        {"NoHttpStatus", net::OK, std::nullopt, "",
+         TachyonResponse::Status::kInternalError},
+        {"AuthError", net::ERR_HTTP_RESPONSE_CODE_FAILURE,
+         net::HttpStatusCode::HTTP_UNAUTHORIZED, "",
+         TachyonResponse::Status::kAuthError},
+        {"OtherHttpError", net::ERR_HTTP_RESPONSE_CODE_FAILURE,
+         net::HttpStatusCode::HTTP_PRECONDITION_FAILED, "",
+         TachyonResponse::Status::kHttpError},
+        {"Success", net::ERR_HTTP_RESPONSE_CODE_FAILURE,
+         net::HttpStatusCode::HTTP_OK, "response",
+         TachyonResponse::Status::kOk},
+    }),
+    [](const testing::TestParamInfo<TachyonResponseTest::ParamType>& info) {
+      return info.param.test_name;
+    });
+
+}  // namespace
+}  // namespace ash::babelorca
diff --git a/chromeos/ash/components/boca/babelorca/transcript_sender.cc b/chromeos/ash/components/boca/babelorca/transcript_sender.cc
index 3e4ba0b..532278f 100644
--- a/chromeos/ash/components/boca/babelorca/transcript_sender.cc
+++ b/chromeos/ash/components/boca/babelorca/transcript_sender.cc
@@ -28,7 +28,7 @@
 #include "chromeos/ash/components/boca/babelorca/tachyon_authed_client.h"
 #include "chromeos/ash/components/boca/babelorca/tachyon_constants.h"
 #include "chromeos/ash/components/boca/babelorca/tachyon_request_data_provider.h"
-#include "chromeos/ash/components/boca/babelorca/tachyon_request_error.h"
+#include "chromeos/ash/components/boca/babelorca/tachyon_response.h"
 #include "chromeos/ash/components/boca/babelorca/tachyon_utils.h"
 #include "media/mojo/mojom/speech_recognition_result.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
@@ -203,10 +203,9 @@
       std::move(request_string));
 }
 
-void TranscriptSender::OnSendResponse(
-    base::expected<std::string, TachyonRequestError> response) {
+void TranscriptSender::OnSendResponse(TachyonResponse response) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (response.has_value()) {
+  if (response.ok()) {
     errors_num_ = 0;
     return;
   }
diff --git a/chromeos/ash/components/boca/babelorca/transcript_sender.h b/chromeos/ash/components/boca/babelorca/transcript_sender.h
index d34a310..f97ee73 100644
--- a/chromeos/ash/components/boca/babelorca/transcript_sender.h
+++ b/chromeos/ash/components/boca/babelorca/transcript_sender.h
@@ -18,8 +18,6 @@
 #include "base/sequence_checker.h"
 #include "base/thread_annotations.h"
 #include "base/time/time.h"
-#include "base/types/expected.h"
-#include "chromeos/ash/components/boca/babelorca/tachyon_request_error.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 
 namespace media {
@@ -35,6 +33,7 @@
 class BabelOrcaMessage;
 class TachyonAuthedClient;
 class TachyonRequestDataProvider;
+class TachyonResponse;
 
 // Class to send transcriptions.
 class TranscriptSender {
@@ -74,8 +73,7 @@
 
   void Send(int max_retries, std::string message);
 
-  void OnSendResponse(
-      base::expected<std::string, TachyonRequestError> response);
+  void OnSendResponse(TachyonResponse response);
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/chromeos/ash/components/boca/babelorca/transcript_sender_unittest.cc b/chromeos/ash/components/boca/babelorca/transcript_sender_unittest.cc
index a87f7e0..0969b1e 100644
--- a/chromeos/ash/components/boca/babelorca/transcript_sender_unittest.cc
+++ b/chromeos/ash/components/boca/babelorca/transcript_sender_unittest.cc
@@ -4,12 +4,13 @@
 
 #include "chromeos/ash/components/boca/babelorca/transcript_sender.h"
 
+#include <optional>
 #include <string>
+#include <utility>
 
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "base/time/time.h"
-#include "base/types/expected.h"
 #include "chromeos/ash/components/boca/babelorca/fakes/fake_tachyon_authed_client.h"
 #include "chromeos/ash/components/boca/babelorca/fakes/fake_tachyon_client.h"
 #include "chromeos/ash/components/boca/babelorca/fakes/fake_tachyon_request_data_provider.h"
@@ -18,8 +19,10 @@
 #include "chromeos/ash/components/boca/babelorca/proto/tachyon_enums.pb.h"
 #include "chromeos/ash/components/boca/babelorca/request_data_wrapper.h"
 #include "chromeos/ash/components/boca/babelorca/tachyon_constants.h"
-#include "chromeos/ash/components/boca/babelorca/tachyon_request_error.h"
+#include "chromeos/ash/components/boca/babelorca/tachyon_response.h"
 #include "media/mojo/mojom/speech_recognition_result.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_status_code.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -72,8 +75,9 @@
                                             /*is_final=*/false);
   EXPECT_TRUE(sender.SendTranscriptionUpdate(transcript, kLanguage));
   authed_client.WaitForRequest();
-  authed_client.ExecuteResponseCallback(
-      InboxSendResponse().SerializeAsString());
+  authed_client.ExecuteResponseCallback(TachyonResponse(
+      net::OK, net::HttpStatusCode::HTTP_OK,
+      std::make_unique<std::string>(InboxSendResponse().SerializeAsString())));
 
   EXPECT_FALSE(failure_future.IsReady());
   InboxSendRequest sent_request;
@@ -135,16 +139,18 @@
   EXPECT_TRUE(sender.SendTranscriptionUpdate(transcript1, kLanguage));
   authed_client.WaitForRequest();
   request_string1 = authed_client.GetRequestString();
-  authed_client.ExecuteResponseCallback(
-      InboxSendResponse().SerializeAsString());
+  authed_client.ExecuteResponseCallback(TachyonResponse(
+      net::OK, net::HttpStatusCode::HTTP_OK,
+      std::make_unique<std::string>(InboxSendResponse().SerializeAsString())));
 
   media::SpeechRecognitionResult transcript2(kTranscriptText,
                                              /*is_final=*/false);
   EXPECT_TRUE(sender.SendTranscriptionUpdate(transcript2, kLanguage));
   authed_client.WaitForRequest();
   request_string2 = authed_client.GetRequestString();
-  authed_client.ExecuteResponseCallback(
-      InboxSendResponse().SerializeAsString());
+  authed_client.ExecuteResponseCallback(TachyonResponse(
+      net::OK, net::HttpStatusCode::HTTP_OK,
+      std::make_unique<std::string>(InboxSendResponse().SerializeAsString())));
 
   EXPECT_FALSE(failure_future.IsReady());
   InboxSendRequest sent_request1;
@@ -194,14 +200,14 @@
   EXPECT_TRUE(sender.SendTranscriptionUpdate(transcript1, kLanguage));
   authed_client.WaitForRequest();
   authed_client.ExecuteResponseCallback(
-      base::unexpected(TachyonRequestError::kHttpError));
+      TachyonResponse(TachyonResponse::Status::kHttpError));
 
   media::SpeechRecognitionResult transcript2(kTranscriptText,
                                              /*is_final=*/false);
   EXPECT_TRUE(sender.SendTranscriptionUpdate(transcript2, kLanguage));
   authed_client.WaitForRequest();
   authed_client.ExecuteResponseCallback(
-      base::unexpected(TachyonRequestError::kHttpError));
+      TachyonResponse(TachyonResponse::Status::kHttpError));
 
   media::SpeechRecognitionResult transcript3(kTranscriptText,
                                              /*is_final=*/false);
@@ -230,15 +236,16 @@
   EXPECT_TRUE(sender.SendTranscriptionUpdate(transcript1, kLanguage));
   authed_client.WaitForRequest();
   authed_client.ExecuteResponseCallback(
-      base::unexpected(TachyonRequestError::kHttpError));
+      TachyonResponse(TachyonResponse::Status::kHttpError));
 
   // Successful request, should reset error count.
   media::SpeechRecognitionResult transcript2(kTranscriptText,
                                              /*is_final=*/false);
   EXPECT_TRUE(sender.SendTranscriptionUpdate(transcript2, kLanguage));
   authed_client.WaitForRequest();
-  authed_client.ExecuteResponseCallback(
-      InboxSendResponse().SerializeAsString());
+  authed_client.ExecuteResponseCallback(TachyonResponse(
+      net::OK, net::HttpStatusCode::HTTP_OK,
+      std::make_unique<std::string>(InboxSendResponse().SerializeAsString())));
 
   // Failed request, should not trigger failure callback since the error count
   // was reset.
@@ -247,7 +254,7 @@
   EXPECT_TRUE(sender.SendTranscriptionUpdate(transcript3, kLanguage));
   authed_client.WaitForRequest();
   authed_client.ExecuteResponseCallback(
-      base::unexpected(TachyonRequestError::kHttpError));
+      TachyonResponse(TachyonResponse::Status::kHttpError));
 
   EXPECT_FALSE(failure_future.IsReady());
 }
@@ -288,14 +295,14 @@
       authed_client.TakeResponseCallback();
 
   std::move(response_cb1)
-      .Run(base::unexpected(TachyonRequestError::kHttpError));
+      .Run(TachyonResponse(TachyonResponse::Status::kHttpError));
   std::move(response_cb2)
-      .Run(base::unexpected(TachyonRequestError::kHttpError));
+      .Run(TachyonResponse(TachyonResponse::Status::kHttpError));
 
   EXPECT_TRUE(failure_future.IsReady());
 
   std::move(response_cb3)
-      .Run(base::unexpected(TachyonRequestError::kHttpError));
+      .Run(TachyonResponse(TachyonResponse::Status::kHttpError));
 
   media::SpeechRecognitionResult transcript4(kTranscriptText,
                                              /*is_final=*/false);
diff --git a/chromeos/profiles/arm.afdo.newest.txt b/chromeos/profiles/arm.afdo.newest.txt
index 7c19afd68..efe8bc0 100644
--- a/chromeos/profiles/arm.afdo.newest.txt
+++ b/chromeos/profiles/arm.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-none-131-6723.9-1727662744-benchmark-131.0.6749.0-r1-redacted.afdo.xz
+chromeos-chrome-arm-none-131-6723.9-1727662744-benchmark-131.0.6750.0-r1-redacted.afdo.xz
diff --git a/clank b/clank
index 59c00202..4120c4d 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 59c00202c7c9270d760bf9a57ffd242ef6b0f271
+Subproject commit 4120c4d811f4c534ae4be631037163de6f797935
diff --git a/components/OWNERS b/components/OWNERS
index e0ba2bb..6fa527a 100644
--- a/components/OWNERS
+++ b/components/OWNERS
@@ -48,8 +48,8 @@
 per-file privacy_sandbox_strings.grd=file://components/privacy_sandbox/OWNERS
 per-file reset_password_strings.grdp=file://components/safe_browsing/OWNERS
 per-file saved_tab_groups_strings.grdp=file://components/saved_tab_groups/OWNERS
-per-file search_engine_choice_strings.grdp=file://components/search_engines/search_engine_choice/OWNERS
-per-file search_engine_descriptions_strings.grd=file://components/search_engines/search_engine_choice/OWNERS
+per-file search_engine_choice_strings.grdp=file://components/search_engines/search_engine_choice/COMPLIANCE_OWNERS
+per-file search_engine_descriptions_strings.grd=file://components/search_engines/search_engine_choice/COMPLIANCE_OWNERS
 per-file security_interstitials_strings.grdp=file://components/security_interstitials/OWNERS
 per-file send_tab_to_self_strings.grdp=file://components/send_tab_to_self/OWNERS
 per-file site_settings_strings.grdp=file://components/browser_ui/site_settings/OWNERS
diff --git a/components/autofill/android/java/src/org/chromium/components/autofill/payments/AutofillSaveIbanUiInfo.java b/components/autofill/android/java/src/org/chromium/components/autofill/payments/AutofillSaveIbanUiInfo.java
index e11ead6..1ab20afa 100644
--- a/components/autofill/android/java/src/org/chromium/components/autofill/payments/AutofillSaveIbanUiInfo.java
+++ b/components/autofill/android/java/src/org/chromium/components/autofill/payments/AutofillSaveIbanUiInfo.java
@@ -17,11 +17,11 @@
 import java.util.List;
 import java.util.Objects;
 
-@JNINamespace("autofill")
 /**
  * The android version of the C++ AutofillSaveIbanUiInfo providing UI resources for the save IBAN
  * bottom sheet.
  */
+@JNINamespace("autofill")
 public class AutofillSaveIbanUiInfo {
     private final String mAcceptText;
     private final String mCancelText;
@@ -64,8 +64,6 @@
         return mTitleText;
     }
 
-    @CalledByNative
-    @VisibleForTesting
     /**
      * Construct the {@link AutofillSaveIbanUiInfo} given all the members. This constructor is used
      * for native binding purposes.
@@ -80,6 +78,8 @@
      *     {@code 0} for local save.
      * @param titleText A bottom sheet title UI string. This value must not be {@code null}.
      */
+    @CalledByNative
+    @VisibleForTesting
     /* package */ AutofillSaveIbanUiInfo(
             @JniType("std::u16string") String acceptText,
             @JniType("std::u16string") String cancelText,
diff --git a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine_impl.cc b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine_impl.cc
index a1ba10c..9e01583 100644
--- a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine_impl.cc
+++ b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine_impl.cc
@@ -154,6 +154,10 @@
         // in the suggestion as the main text.
         select_option_text = predicted_select_option_it->text;
       }
+      // Skip predictions for non-empty text fields.
+      else if (field.IsTextInputElement() && !field.value().empty()) {
+        continue;
+      }
 
       const std::u16string label =
           filled_form_field_proto.normalized_label().empty()
diff --git a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine_impl_unittest.cc b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine_impl_unittest.cc
index f1ee7f68..b58c00f 100644
--- a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine_impl_unittest.cc
+++ b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine_impl_unittest.cc
@@ -108,6 +108,7 @@
   AddFieldToResponse(response,
                      "Country - response equals selected value, not filled", "",
                      "2");
+  AddFieldToResponse(response, "Field has value, not filled", "", "value");
   optimization_guide::proto::Any any;
   any.set_type_url(response.GetTypeName());
   response.SerializeToString(any.mutable_value());
@@ -139,7 +140,8 @@
            .value = u"2",
            .form_control_type = autofill::FormControlType::kSelectOne,
            .select_options = {{.value = u"1", .text = u"France"},
-                              {.value = u"2", .text = u"Spain"}}}}};
+                              {.value = u"2", .text = u"Spain"}}},
+          {.label = u"Field has value, not filled", .value = u"value"}}};
   autofill::FormData form = autofill::test::GetFormData(form_description);
 
   optimization_guide::proto::AXTreeUpdate ax_tree;
diff --git a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager.cc b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager.cc
index e923a85..09f11c2 100644
--- a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager.cc
+++ b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager.cc
@@ -36,6 +36,8 @@
 
 namespace {
 
+constexpr int kNumberFieldsToShowInSuggestionLabel = 2;
+
 // Define `field_types_to_fill` as Autofill address types +
 // `IMPROVED_PREDICTION`.
 // TODO(crbug.com/364808228): Remove `UNKNOWN_TYPE` from `field_types_to_fill`.
@@ -213,8 +215,6 @@
   // Add a `kFillPredictionImprovements` suggestion with a separator to
   // `suggestion.children` before the field-by-field filling entries.
   {
-    // TODO(crbug.com/361434879): Add hardcoded string to an appropriate grd
-    // file.
     autofill::Suggestion fill_all_child(
         l10n_util::GetStringUTF16(
             IDS_AUTOFILL_PREDICTION_IMPROVEMENTS_FILL_ALL_MAIN_TEXT),
@@ -225,7 +225,13 @@
   }
   // Add the child suggestion for the triggering field on top.
   suggestion.children.emplace_back(CreateChildSuggestionForFilling(prediction));
-  // Then add child suggestions for all remaining, non-empty fields.
+  // Initialize as 1 because of the suggestion added above.
+  size_t n_fields_to_fill = 1;
+  // The label depends on the fields that will be filled.
+  std::u16string label =
+      l10n_util::GetStringUTF16(
+          IDS_AUTOFILL_PREDICTION_IMPROVEMENTS_FILL_LABEL_TEXT) +
+      u" " + prediction.label;
   for (const auto& [child_field_global_id, child_prediction] : (*cache_)) {
     // Only add a child suggestion if the field is not the triggering field and
     // the value to fill is not empty.
@@ -235,16 +241,34 @@
     }
     suggestion.children.emplace_back(
         CreateChildSuggestionForFilling(child_prediction));
+    ++n_fields_to_fill;
+    if (n_fields_to_fill == 2) {
+      label += l10n_util::GetStringUTF16(
+                   IDS_AUTOFILL_PREDICTION_IMPROVEMENTS_FILL_LABEL_SEPARATOR) +
+               child_prediction.label;
+    }
   }
-  if (!suggestion.children.empty()) {
-    suggestion.labels.emplace_back();
-    // TODO(crbug.com/361434879): Add hardcoded string to an appropriate grd
-    // file.
-    suggestion.labels.back().emplace_back(u"& more");
-    suggestion.children.emplace_back(autofill::SuggestionType::kSeparator);
-    suggestion.children.emplace_back(
-        CreateEditPredictionImprovementsInformation());
+
+  suggestion.children.emplace_back(autofill::SuggestionType::kSeparator);
+  suggestion.children.emplace_back(
+      CreateEditPredictionImprovementsInformation());
+
+  if (n_fields_to_fill > kNumberFieldsToShowInSuggestionLabel) {
+    // When more than `kNumberFieldsToShowInSuggestionLabel` are filled, include
+    // the "& More".
+    size_t number_of_more_fields_to_fill =
+        n_fields_to_fill - kNumberFieldsToShowInSuggestionLabel;
+    const std::u16string more_fields_label_substr =
+        number_of_more_fields_to_fill > 1
+            ? l10n_util::GetStringFUTF16(
+                  IDS_AUTOFILL_PREDICTION_IMPROVEMENTS_FILL_SUGGESTION_AND_N_MORE_FIELDS,
+                  base::NumberToString16(number_of_more_fields_to_fill))
+            : l10n_util::GetStringUTF16(
+                  IDS_AUTOFILL_PREDICTION_IMPROVEMENTS_FILL_SUGGESTION_AND_ONE_MORE_FIELD);
+    label = base::StrCat({label, u" ", more_fields_label_substr});
   }
+  suggestion.labels.emplace_back();
+  suggestion.labels.back().emplace_back(label);
 
   // TODO(crbug.com/365512352): Figure out how to handle Undo suggestion.
   std::vector<autofill::Suggestion> filling_suggestions = {suggestion};
diff --git a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager_unittest.cc b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager_unittest.cc
index 4d18f16..3c291858 100644
--- a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager_unittest.cc
+++ b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager_unittest.cc
@@ -509,6 +509,81 @@
           HasType(SuggestionType::kPredictionImprovementsFeedback)));
 }
 
+// Tests that the filling suggestion label is correct when only one field can be
+// filled.
+TEST_F(
+    AutofillPredictionImprovementsManagerTest,
+    FillingSuggestion_OneFieldCanBeFilled_CreateLabelThatContainsOnlyOneFieldData) {
+  autofill::test::FormDescription form_description = {
+      .fields = {{.role = autofill::NAME_FIRST,
+                  .heuristic_type = autofill::NAME_FIRST}}};
+  autofill::FormData form = autofill::test::GetFormData(form_description);
+  test_api(*manager_).SetCache(PredictionsByGlobalId{
+      {form.fields()[0].global_id(), {u"Jane", u"First name"}}});
+
+  std::vector<Suggestion> suggestions_to_show;
+  EXPECT_TRUE(
+      manager_->MaybeUpdateSuggestions(suggestions_to_show, form.fields()[0],
+                                       /*should_add_trigger_suggestion=*/true));
+  EXPECT_EQ(suggestions_to_show[0].labels[0][0].value, u"Fill First name");
+}
+
+// Tests that the filling suggestion label is correct when 3 fields can be
+// filled.
+TEST_F(AutofillPredictionImprovementsManagerTest,
+       FillingSuggestion_ThreeFieldsCanBeFilled_UserSingularAndMoreString) {
+  autofill::test::FormDescription form_description = {
+      .fields = {{.role = autofill::NAME_FIRST,
+                  .heuristic_type = autofill::NAME_FIRST},
+                 {.role = autofill::ADDRESS_HOME_STREET_NAME,
+                  .heuristic_type = autofill::ADDRESS_HOME_STREET_NAME},
+                 {.role = autofill::ADDRESS_HOME_STATE,
+                  .heuristic_type = autofill::ADDRESS_HOME_STATE,
+                  .form_control_type = autofill::FormControlType::kSelectOne}}};
+  autofill::FormData form = autofill::test::GetFormData(form_description);
+  test_api(*manager_).SetCache(PredictionsByGlobalId{
+      {form.fields()[0].global_id(), {u"Jane", u"First name"}},
+      {form.fields()[1].global_id(), {u"Country roads str", u"Street name"}},
+      {form.fields()[2].global_id(), {u"33", u"state", u"West Virginia"}}});
+
+  std::vector<Suggestion> suggestions_to_show;
+  EXPECT_TRUE(
+      manager_->MaybeUpdateSuggestions(suggestions_to_show, form.fields()[0],
+                                       /*should_add_trigger_suggestion=*/true));
+  EXPECT_EQ(suggestions_to_show[0].labels[0][0].value,
+            u"Fill First name, Street name & 1 more field");
+}
+
+// Tests that the filling suggestion label is correct when more than 3 fields
+// can be filled.
+TEST_F(
+    AutofillPredictionImprovementsManagerTest,
+    FillingSuggestion_MoreThanThreeFieldsCanBeFilled_UserPluralAndMoreString) {
+  autofill::test::FormDescription form_description = {
+      .fields = {
+          {.role = autofill::NAME_FIRST,
+           .heuristic_type = autofill::NAME_FIRST},
+          {.role = autofill::NAME_LAST, .heuristic_type = autofill::NAME_LAST},
+          {.role = autofill::ADDRESS_HOME_STREET_NAME,
+           .heuristic_type = autofill::ADDRESS_HOME_STREET_NAME},
+          {.role = autofill::ADDRESS_HOME_STATE,
+           .heuristic_type = autofill::ADDRESS_HOME_STATE,
+           .form_control_type = autofill::FormControlType::kSelectOne}}};
+  autofill::FormData form = autofill::test::GetFormData(form_description);
+  test_api(*manager_).SetCache(PredictionsByGlobalId{
+      {form.fields()[0].global_id(), {u"Jane", u"First name"}},
+      {form.fields()[1].global_id(), {u"Doe", u"Last name"}},
+      {form.fields()[2].global_id(), {u"Country roads str", u"Street name"}},
+      {form.fields()[3].global_id(), {u"33", u"state", u"West Virginia"}}});
+
+  std::vector<Suggestion> suggestions_to_show;
+  EXPECT_TRUE(
+      manager_->MaybeUpdateSuggestions(suggestions_to_show, form.fields()[0],
+                                       /*should_add_trigger_suggestion=*/true));
+  EXPECT_EQ(suggestions_to_show[0].labels[0][0].value,
+            u"Fill First name, Last name & 2 more fields");
+}
+
 class AutofillPredictionImprovementsManagerUserFeedbackTest
     : public AutofillPredictionImprovementsManagerTest,
       public testing::WithParamInterface<
diff --git a/components/autofill_prediction_improvements_strings.grdp b/components/autofill_prediction_improvements_strings.grdp
index 7e5eae2..05e52fe 100644
--- a/components/autofill_prediction_improvements_strings.grdp
+++ b/components/autofill_prediction_improvements_strings.grdp
@@ -9,8 +9,17 @@
   <message name="IDS_AUTOFILL_PREDICTION_IMPROVEMENTS_FILL_ALL_MAIN_TEXT" desc="Suggestion shown when the user focuses a prediction improvements field." translateable="false">
     Fill all
   </message>
-  <message name="IDS_AUTOFILL_PREDICTION_IMPROVEMENTS_FILL_SUGGESTION_AND_MORE" desc="Text displayed as label to indicate more fields will be filled." translateable="false">
-    &amp; more
+  <message name="IDS_AUTOFILL_PREDICTION_IMPROVEMENTS_FILL_LABEL_TEXT" desc="Text shown as a label when the user sees a prediction improvements suggestion." translateable="false">
+    Fill
+  </message>
+  <message name="IDS_AUTOFILL_PREDICTION_IMPROVEMENTS_FILL_LABEL_SEPARATOR" desc="The separator character used in the prediction improvements label." translateable="false">
+    , '''
+  </message>
+  <message name="IDS_AUTOFILL_PREDICTION_IMPROVEMENTS_FILL_SUGGESTION_AND_ONE_MORE_FIELD" desc="Text displayed as label to indicate more fields will be filled." translateable="false">
+    &amp; 1 more field
+  </message>
+  <message name="IDS_AUTOFILL_PREDICTION_IMPROVEMENTS_FILL_SUGGESTION_AND_N_MORE_FIELDS" desc="Text displayed as label to indicate more fields will be filled." translateable="false">
+    &amp; <ph name="N_FIELDS">$1<ex>4</ex></ph> more fields
   </message>
 
   <!-- Save prompt -->
diff --git a/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationInfoTest.java b/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationInfoTest.java
index 70ba875a..bd7009d3 100644
--- a/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationInfoTest.java
+++ b/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationInfoTest.java
@@ -90,7 +90,6 @@
         assertNotNull(info);
 
         // Make sure hashCode() doesn't crash.
-        int hashValue = info.hashCode();
-        assertNotNull(hashValue);
+        info.hashCode();
     }
 }
diff --git a/components/browser_ui/widget/android/java/res/layout/title_and_description_layout.xml b/components/browser_ui/widget/android/java/res/layout/title_and_description_layout.xml
index 0504a27..52d15a98 100644
--- a/components/browser_ui/widget/android/java/res/layout/title_and_description_layout.xml
+++ b/components/browser_ui/widget/android/java/res/layout/title_and_description_layout.xml
@@ -5,79 +5,83 @@
 found in the LICENSE file.
 -->
 
-<LinearLayout
+<androidx.constraintlayout.widget.ConstraintLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="0dp"
     android:layout_height="wrap_content"
-    android:layout_weight="1"
-    android:orientation="vertical"
-    android:layout_gravity="center_vertical" >
+    android:layout_weight="1" >
 
-    <LinearLayout
-        android:layout_width="match_parent"
+    <ImageView
+        android:id="@+id/before_title_icon"
+        android:layout_marginEnd="6dp"
+        android:layout_marginTop="4dp"
+        android:layout_marginBottom="2dp"
+        android:layout_width="16dp"
+        android:layout_height="16dp"
+        android:contentDescription="@null"
+        android:visibility="gone"
+        app:layout_constraintHorizontal_bias="0.0"
+        app:layout_constraintHorizontal_chainStyle="packed"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toStartOf="@+id/title" />
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="start"
-        android:orientation="horizontal"
-        tools:ignore="UseCompoundDrawables" >
+        android:maxLines="1"
+        android:ellipsize="end"
+        android:textAppearance="@style/TextAppearance.TextLarge.Primary"
+        app:layout_constraintHorizontal_bias="0.0"
+        app:layout_constraintHorizontal_chainStyle="packed"
+        app:layout_constrainedWidth="true"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toEndOf="@+id/before_title_icon"
+        app:layout_constraintEnd_toStartOf="@+id/space_anchor" />
 
-        <ImageView
-            android:id="@+id/icon"
-            android:layout_marginEnd="6dp"
-            android:layout_marginTop="4dp"
-            android:layout_marginBottom="2dp"
-            android:layout_width="16dp"
-            android:layout_height="16dp"
-            android:contentDescription="@null"
-            android:visibility="gone" />
+    <!-- Anchor to ensure the chain terminates without any elements being pushed offscreen. -->
+    <Space
+        android:id="@+id/space_anchor"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        app:layout_constraintEnd_toEndOf="parent" />
 
-        <TextView
-            android:id="@+id/title"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:maxLines="1"
-            android:ellipsize="end"
-            android:textAppearance="@style/TextAppearance.TextLarge.Primary" />
-    </LinearLayout>
-
-    <androidx.constraintlayout.widget.ConstraintLayout
-        android:id="@+id/chip_description"
-        android:layout_width="match_parent"
+    <TextView
+        android:id="@+id/description"
+        android:layout_gravity="center_vertical|start"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:orientation="horizontal">
+        android:maxLines="1"
+        android:ellipsize="end"
+        android:textAppearance="@style/TextAppearance.TextMedium.Secondary"
+        app:layout_constraintHorizontal_bias="0.0"
+        app:layout_constraintHorizontal_chainStyle="packed"
+        app:layout_constraintTop_toBottomOf="@+id/title"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toStartOf="@+id/chip" />
 
-        <TextView
-            android:id="@+id/description"
-            android:layout_gravity="center_vertical|start"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:maxLines="1"
-            android:ellipsize="end"
-            android:textAppearance="@style/TextAppearance.TextMedium.Secondary"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toTopOf="parent" />
-
-        <!-- An optional chip view. Used for history.TODO(b/331856225): Consider adding min_width
-             to prevent the description view from occupying the whole space. -->
-        <org.chromium.components.browser_ui.widget.chips.ChipView
-            android:id="@+id/chip"
-            android:layout_gravity="center_vertical|start"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="8dp"
-            android:visibility="gone"
-            app:layout_constrainedWidth="true"
-            app:layout_constraintBottom_toBottomOf="@+id/description"
-            app:layout_constraintStart_toEndOf="@+id/description"
-            app:layout_constraintTop_toTopOf="@+id/description"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintHorizontal_bias="0"
-            style="@style/HistoryAppChip" />
-    </androidx.constraintlayout.widget.ConstraintLayout>
+    <!-- An optional chip view. Used for history. -->
+    <org.chromium.components.browser_ui.widget.chips.ChipView
+        android:id="@+id/chip"
+        android:layout_gravity="center_vertical|start"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="8dp"
+        android:visibility="gone"
+        app:layout_constrainedWidth="true"
+        app:layout_constraintTop_toTopOf="@+id/description"
+        app:layout_constraintBottom_toBottomOf="@+id/description"
+        app:layout_constraintStart_toEndOf="@+id/description"
+        app:layout_constraintEnd_toStartOf="@+id/space_anchor"
+        style="@style/HistoryAppChip" />
 
     <FrameLayout
         android:id="@+id/custom_content_container"
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content" />
-</LinearLayout>
+        android:layout_height="wrap_content"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/description" />
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableItemViewBaseTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableItemViewBaseTest.java
index e28bdd80..89cc040b2 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableItemViewBaseTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableItemViewBaseTest.java
@@ -61,7 +61,7 @@
         SelectionDelegate<Integer> selectionDelegate = new SelectionDelegate<>();
         mSelectableItemViewBase.setSelectionDelegate(selectionDelegate);
 
-        Integer item = new Integer(1);
+        Integer item = 1;
         assertNull(mSelectableItemViewBase.getItem());
         mSelectableItemViewBase.setItem(item);
         assertEquals(item, mSelectableItemViewBase.getItem());
@@ -87,7 +87,7 @@
 
     @Test
     public void testSelection_NullDelegate() {
-        Integer item = new Integer(1);
+        Integer item = 1;
         assertNull(mSelectableItemViewBase.getItem());
         mSelectableItemViewBase.setItem(item);
         assertNull(mSelectableItemViewBase.getItem());
diff --git a/components/cronet/android/api/src/org/chromium/net/apihelpers/ContentTypeParametersParser.java b/components/cronet/android/api/src/org/chromium/net/apihelpers/ContentTypeParametersParser.java
index 3394c465..d3a56a9 100644
--- a/components/cronet/android/api/src/org/chromium/net/apihelpers/ContentTypeParametersParser.java
+++ b/components/cronet/android/api/src/org/chromium/net/apihelpers/ContentTypeParametersParser.java
@@ -172,7 +172,7 @@
     }
 
     private static boolean isAscii(char ch) {
-        return (char) 0 <= ch && ch <= (char) 127;
+        return ch <= 127;
     }
 
     private static boolean isWhitespace(char c) {
diff --git a/components/cronet/android/sample/javatests/src/org/chromium/cronet_sample_apk/CronetSampleTest.java b/components/cronet/android/sample/javatests/src/org/chromium/cronet_sample_apk/CronetSampleTest.java
index 8090371..1af4c5a 100644
--- a/components/cronet/android/sample/javatests/src/org/chromium/cronet_sample_apk/CronetSampleTest.java
+++ b/components/cronet/android/sample/javatests/src/org/chromium/cronet_sample_apk/CronetSampleTest.java
@@ -18,6 +18,6 @@
     @Test
     @SmallTest
     public void testSimple() throws Exception {
-        assertThat(1).isEqualTo(1);
+        assertThat(1 + 1).isEqualTo(2);
     }
 }
diff --git a/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/Options.java b/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/Options.java
index 74c77bc..09d0176 100644
--- a/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/Options.java
+++ b/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/Options.java
@@ -14,29 +14,34 @@
 import java.util.Map;
 
 /**
- * Adding an option here will make it show up in the list of available options.
- * Each {@link Option} has the following attributes:
+ * Adding an option here will make it show up in the list of available options. Each {@link Option}
+ * has the following attributes:
+ *
  * <ul>
- *   <li>A short name which appears in bold on the options list.</li>
- *  <li>A description which provides a thorough explanation of what this option does.</li>
- *  <li>An {@link Action} which is applied on CronetEngine's Builder each time the user hits "Reset
- * Engine". </li> <li>A default value, every option must have a default value.</li>
+ *   <li>A short name which appears in bold on the options list.
+ *   <li>A description which provides a thorough explanation of what this option does.
+ *   <li>An {@link Action} which is applied on CronetEngine's Builder each time the user hits "Reset
+ *       Engine".
+ *   <li>A default value, every option must have a default value.
  * </ul>
+ *
  * <b>NOTE</b>: Each option must map to one {@link OptionsIdentifier OptionsIdentifier}. This is
  * necessary to provide custom implementation for options that does not configure the builders. See
  * {@link OptionsIdentifier#SLOW_DOWNLOAD} as an example.
  *
- * <p> To add a new option, do the following:
+ * <p>To add a new option, do the following:
+ *
  * <ol>
- *  <li> Add a new optionIdentifier {@link OptionsIdentifier} </li>
- *  <li> Inject a new Option instance into your optionIdentifier enum value. </li>
- *  <li> Implement the logic for the new option within a new {@link Action}. </li>
- *  <li> If the {@link Action} interface is not enough to satisfy the use-case. Feel free to add
- * custom logic, See {@link OptionsIdentifier#SLOW_DOWNLOAD} as an example.</li>
- *  <li> Restart the APK and verify that your option is working as intended. </li>
+ *   <li>Add a new optionIdentifier {@link OptionsIdentifier}
+ *   <li>Inject a new Option instance into your optionIdentifier enum value.
+ *   <li>Implement the logic for the new option within a new {@link Action}.
+ *   <li>If the {@link Action} interface is not enough to satisfy the use-case. Feel free to add
+ *       custom logic, See {@link OptionsIdentifier#SLOW_DOWNLOAD} as an example.
+ *   <li>Restart the APK and verify that your option is working as intended.
  * </ol>
  */
 public class Options {
+    @SuppressWarnings("ImmutableEnumChecker")
     public enum OptionsIdentifier {
         MIGRATE_SESSIONS_ON_NETWORK_CHANGE_V2(
                 new BooleanOption(
@@ -57,9 +62,9 @@
                 new BooleanOption(
                         "migrate_sessions_early_v2",
                         "Enable QUIC early session migration. This will make quic send probing"
-                                + " packets when the network is degrading, QUIC will migrate the "
-                                + "sessions to a different network even before the original network "
-                                + "has disconnected.",
+                            + " packets when the network is degrading, QUIC will migrate the"
+                            + " sessions to a different network even before the original network"
+                            + " has disconnected.",
                         new Action<Boolean>() {
                             @Override
                             @OptIn(markerClass = ConnectionMigrationOptions.Experimental.class)
@@ -72,7 +77,8 @@
         SLOW_DOWNLOAD(
                 new BooleanOption(
                         "Slow Download (10s)",
-                        "Hang the onReadCompleted for 10s before proceeding. This should simulate slow connection.",
+                        "Hang the onReadCompleted for 10s before proceeding. This should simulate"
+                                + " slow connection.",
                         new Action<>() {},
                         false));
 
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestRule.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestRule.java
index b1c75f7..240d8a3 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestRule.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestRule.java
@@ -678,6 +678,8 @@
         ExperimentalCronetEngine.Builder getCronetEngineBuilder(Context context);
     }
 
+    // Warning should go away once we can use java.util.function.Function.
+    @SuppressWarnings("ImmutableEnumChecker")
     public enum CronetImplementation {
         STATICALLY_LINKED(
                 context ->
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
index 92bd66a..914c621 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
@@ -1813,17 +1813,11 @@
         String url = NativeTestServer.getFileURL("/cacheable.txt");
 
         // When cache is disabled, making a request does not write to the cache.
-        checkRequestCaching(
-                cronetEngine, url, false, true
-                /** disable cache */
-                );
+        checkRequestCaching(cronetEngine, url, false, true);
         checkRequestCaching(cronetEngine, url, false);
 
         // When cache is enabled, the second request is cached.
-        checkRequestCaching(
-                cronetEngine, url, false, true
-                /** disable cache */
-                );
+        checkRequestCaching(cronetEngine, url, false, true);
         checkRequestCaching(cronetEngine, url, true);
 
         // Shut down the server, next request should have a cached response.
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
index dc544afd..9bde7eb 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
@@ -2875,12 +2875,12 @@
         }
     }
 
-    @Test
-    @SmallTest
     /**
      * Open many connections and cancel them right away. This test verifies all internal sockets and
      * other Closeables are properly closed. See crbug.com/726193.
      */
+    @Test
+    @SmallTest
     public void testGzipCancel() throws Exception {
         String url = NativeTestServer.getFileURL("/gzipped.html");
         for (int i = 0; i < 100; i++) {
@@ -2912,10 +2912,10 @@
         }
     }
 
+    /** Do a HEAD request and get back a 404. */
     @Test
     @SmallTest
     @RequiresMinApi(8) // JavaUrlRequest fixed in API level 8: crrev.com/499303
-    /** Do a HEAD request and get back a 404. */
     public void test404Head() throws Exception {
         TestUrlRequestCallback callback = new TestUrlRequestCallback();
         UrlRequest.Builder builder =
@@ -2997,12 +2997,12 @@
         assertThat(CronetTestUtil.nativeGetTaggedBytes(tag)).isGreaterThan(priorBytes);
     }
 
-    @Test
-    @SmallTest
     /**
      * Initiate many requests concurrently to make sure neither Cronet implementation crashes.
      * Regression test for https://crbug.com/844031.
      */
+    @Test
+    @SmallTest
     public void testManyRequests() throws Exception {
         String url = NativeTestServer.getMultiRedirectURL();
         final int numRequests = 2000;
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/TestUrlRequestCallback.java b/components/cronet/android/test/javatests/src/org/chromium/net/TestUrlRequestCallback.java
index 4191b21..11e51d43 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/TestUrlRequestCallback.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/TestUrlRequestCallback.java
@@ -198,7 +198,7 @@
             // Termination shouldn't take long. Use 1 min which should be more than enough.
             mExecutorService.awaitTermination(1, TimeUnit.MINUTES);
         } catch (InterruptedException e) {
-            fail("ExecutorService is interrupted while waiting for termination");
+            throw new RuntimeException(e);
         }
         assertThat(mExecutorService.isTerminated()).isTrue();
     }
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLStreamHandlerTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLStreamHandlerTest.java
index 1fd392d..75e19256 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLStreamHandlerTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLStreamHandlerTest.java
@@ -88,6 +88,7 @@
 
     @Test
     @SmallTest
+    @SuppressWarnings("AddressSelection")
     public void testOpenConnectionWithProxy() throws Exception {
         URL url = new URL(NativeTestServer.getEchoMethodURL());
         CronetHttpURLStreamHandler streamHandler =
diff --git a/components/embedder_support/android/native_java_unittests/src/org/chromium/components/embedder_support/util/InputStreamUnittest.java b/components/embedder_support/android/native_java_unittests/src/org/chromium/components/embedder_support/util/InputStreamUnittest.java
index f1863018..1f1352cd 100644
--- a/components/embedder_support/android/native_java_unittests/src/org/chromium/components/embedder_support/util/InputStreamUnittest.java
+++ b/components/embedder_support/android/native_java_unittests/src/org/chromium/components/embedder_support/util/InputStreamUnittest.java
@@ -9,6 +9,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 
+@SuppressWarnings("InputStreamSlowMultibyteRead")
 class InputStreamUnittest {
     private InputStreamUnittest() {}
 
diff --git a/components/facilitated_payments/core/browser/facilitated_payments_manager.cc b/components/facilitated_payments/core/browser/facilitated_payments_manager.cc
index f810f95..aa71cc4 100644
--- a/components/facilitated_payments/core/browser/facilitated_payments_manager.cc
+++ b/components/facilitated_payments/core/browser/facilitated_payments_manager.cc
@@ -60,11 +60,7 @@
   ukm_source_id_ = ukm_source_id;
   trigger_source_ = TriggerSource::kCopyEvent;
   // Check whether the domain for the render_frame_host_url is allowlisted.
-  auto decision = optimization_guide_decider_->CanApplyOptimization(
-      render_frame_host_url,
-      optimization_guide::proto::PIX_MERCHANT_ORIGINS_ALLOWLIST,
-      /*optimization_metadata=*/nullptr);
-  if (decision != optimization_guide::OptimizationGuideDecision::kTrue) {
+  if (!IsMerchantAllowlisted(render_frame_host_url)) {
     // The merchant is not part of the allowlist, ignore the copy event.
     return;
   }
@@ -79,21 +75,20 @@
 
 void FacilitatedPaymentsManager::RegisterPixAllowlist() const {
   optimization_guide_decider_->RegisterOptimizationTypes(
-      {optimization_guide::proto::PIX_PAYMENT_MERCHANT_ALLOWLIST,
-       optimization_guide::proto::PIX_MERCHANT_ORIGINS_ALLOWLIST});
+      {optimization_guide::proto::PIX_MERCHANT_ORIGINS_ALLOWLIST});
 }
 
-optimization_guide::OptimizationGuideDecision
-FacilitatedPaymentsManager::GetAllowlistCheckResult(const GURL& url) const {
+bool FacilitatedPaymentsManager::IsMerchantAllowlisted(const GURL& url) const {
   // Since the optimization guide decider integration corresponding to PIX
   // merchant lists are allowlists for the question "Can this site be
   // optimized?", a match on the allowlist answers the question with "yes".
-  // Therefore, `kTrue` indicates that `url` is allowed for running PIX code
-  // detection. If the optimization type was not registered in time when we
+  // Therefore, `kTrue` indicates that `url` is allowed for detecting PIX code
+  // on copy events. If the optimization type was not registered in time when we
   // queried it, it will be `kUnknown`.
   return optimization_guide_decider_->CanApplyOptimization(
-      url, optimization_guide::proto::PIX_PAYMENT_MERCHANT_ALLOWLIST,
-      /*optimization_metadata=*/nullptr);
+             url, optimization_guide::proto::PIX_MERCHANT_ORIGINS_ALLOWLIST,
+             /*optimization_metadata=*/nullptr) ==
+         optimization_guide::OptimizationGuideDecision::kTrue;
 }
 
 void FacilitatedPaymentsManager::OnPixCodeValidated(
diff --git a/components/facilitated_payments/core/browser/facilitated_payments_manager.h b/components/facilitated_payments/core/browser/facilitated_payments_manager.h
index 7c70c47..2bbbc3e 100644
--- a/components/facilitated_payments/core/browser/facilitated_payments_manager.h
+++ b/components/facilitated_payments/core/browser/facilitated_payments_manager.h
@@ -223,8 +223,8 @@
   // 1. In the allowlist
   // 2. Not in the allowlist
   // 3. Infra for querying is not ready
-  optimization_guide::OptimizationGuideDecision GetAllowlistCheckResult(
-      const GURL& url) const;
+  // Returns true if the result is [1].
+  bool IsMerchantAllowlisted(const GURL& url) const;
 
   // Called by the utility process after validation of the `pix_code`. If the
   // utility processes has disconnected (e.g., due to a crash in the validation
diff --git a/components/facilitated_payments/core/browser/facilitated_payments_manager_unittest.cc b/components/facilitated_payments/core/browser/facilitated_payments_manager_unittest.cc
index c1e382a..dc0f365 100644
--- a/components/facilitated_payments/core/browser/facilitated_payments_manager_unittest.cc
+++ b/components/facilitated_payments/core/browser/facilitated_payments_manager_unittest.cc
@@ -242,7 +242,6 @@
 TEST_F(FacilitatedPaymentsManagerTest, RegisterPixAllowlist) {
   EXPECT_CALL(*optimization_guide_decider_,
               RegisterOptimizationTypes(testing::ElementsAre(
-                  optimization_guide::proto::PIX_PAYMENT_MERCHANT_ALLOWLIST,
                   optimization_guide::proto::PIX_MERCHANT_ORIGINS_ALLOWLIST)))
       .Times(1);
 
diff --git a/components/ip_protection/common/ip_protection_token_manager_impl.cc b/components/ip_protection/common/ip_protection_token_manager_impl.cc
index 8823367..389fadf 100644
--- a/components/ip_protection/common/ip_protection_token_manager_impl.cc
+++ b/components/ip_protection/common/ip_protection_token_manager_impl.cc
@@ -262,7 +262,9 @@
     return;
   }
 
-  VLOG(2) << "IPPATC::OnGotAuthTokens got " << tokens->size() << " tokens";
+  VLOG(2) << "IPPATC::OnGotAuthTokens got " << tokens->size()
+          << " tokens for proxy "
+          << int(proxy_layer_);
   try_get_auth_tokens_after_ = base::Time();
 
   RemoveExpiredTokens();
diff --git a/components/module_installer/android/junit/src/org/chromium/components/module_installer/engine/SplitCompatEngineTest.java b/components/module_installer/android/junit/src/org/chromium/components/module_installer/engine/SplitCompatEngineTest.java
index d168f62..ece61a8 100644
--- a/components/module_installer/android/junit/src/org/chromium/components/module_installer/engine/SplitCompatEngineTest.java
+++ b/components/module_installer/android/junit/src/org/chromium/components/module_installer/engine/SplitCompatEngineTest.java
@@ -86,12 +86,8 @@
         // Arrange.
         String installedModule = "m1";
         String uninstalledModule = "m2";
-        Set<String> installedModules =
-                new HashSet<String>() {
-                    {
-                        add(installedModule);
-                    }
-                };
+        Set<String> installedModules = new HashSet<String>();
+        installedModules.add(installedModule);
         doReturn(installedModules).when(mManager).getInstalledModules();
 
         // Act & Assert.
diff --git a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatch.java b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatch.java
index d53b337..e8b4249 100644
--- a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatch.java
+++ b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatch.java
@@ -509,6 +509,7 @@
     }
 
     @Override
+    @SuppressWarnings("LiteProtoToString")
     public String toString() {
         List<String> pieces =
                 Arrays.asList(
diff --git a/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java b/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java
index 76cc7d0..14543ea 100644
--- a/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java
+++ b/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java
@@ -341,9 +341,7 @@
         // we should have only one column.
         Bitmap[][] bitmapMatrix = mModel.get(PlayerFrameProperties.BITMAP_MATRIX);
         Assert.assertTrue(Arrays.deepEquals(bitmapMatrix, new Bitmap[4][1]));
-        Assert.assertEquals(
-                new ArrayList<Pair<View, Rect>>(),
-                mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS));
+        Assert.assertEquals(List.of(), mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS));
     }
 
     /**
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/JniPaymentApp.java b/components/payments/content/android/java/src/org/chromium/components/payments/JniPaymentApp.java
index 0bb4fd1..176e0bd5 100644
--- a/components/payments/content/android/java/src/org/chromium/components/payments/JniPaymentApp.java
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/JniPaymentApp.java
@@ -235,6 +235,8 @@
         mNativeObject = 0;
     }
 
+    // TODO(crbug.com/40286193): Use an explicit destroy() method.
+    @SuppressWarnings("Finalize")
     @Override
     public void finalize() throws Throwable {
         dismissInstrument();
diff --git a/components/performance_manager/public/scenarios/loading_scenario_observer.h b/components/performance_manager/public/scenarios/loading_scenario_observer.h
index cd61194..b68eef62 100644
--- a/components/performance_manager/public/scenarios/loading_scenario_observer.h
+++ b/components/performance_manager/public/scenarios/loading_scenario_observer.h
@@ -5,7 +5,6 @@
 #ifndef COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_SCENARIOS_LOADING_SCENARIO_OBSERVER_H_
 #define COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_SCENARIOS_LOADING_SCENARIO_OBSERVER_H_
 
-#include "base/numerics/clamped_math.h"
 #include "base/sequence_checker.h"
 #include "components/performance_manager/public/graph/graph.h"
 #include "components/performance_manager/public/graph/page_node.h"
@@ -40,9 +39,9 @@
     LoadingScenario GetScenario() const;
 
    private:
-    base::ClampedNumeric<size_t> focused_loading_pages_;
-    base::ClampedNumeric<size_t> visible_loading_pages_;
-    base::ClampedNumeric<size_t> loading_pages_;
+    size_t focused_loading_pages_ = 0;
+    size_t visible_loading_pages_ = 0;
+    size_t loading_pages_ = 0;
   };
 
   LoadingScenarioObserver() = default;
diff --git a/components/performance_manager/scenarios/loading_scenario_observer.cc b/components/performance_manager/scenarios/loading_scenario_observer.cc
index af68b40..ea734a2 100644
--- a/components/performance_manager/scenarios/loading_scenario_observer.cc
+++ b/components/performance_manager/scenarios/loading_scenario_observer.cc
@@ -8,6 +8,7 @@
 
 #include "base/check_op.h"
 #include "base/notreached.h"
+#include "base/numerics/checked_math.h"
 #include "base/sequence_checker.h"
 #include "components/performance_manager/public/graph/graph.h"
 #include "components/performance_manager/public/graph/page_node.h"
@@ -31,6 +32,16 @@
   NOTREACHED();
 }
 
+// Increments `num` in-place, and CHECK's on overflow.
+void CheckIncrement(size_t& num) {
+  num = base::CheckAdd(num, 1).ValueOrDie();
+}
+
+// Decrements `num` in-place, and CHECK's on underflow.
+void CheckDecrement(size_t& num) {
+  num = base::CheckSub(num, 1).ValueOrDie();
+}
+
 }  // namespace
 
 LoadingScenario LoadingScenarioObserver::LoadingCounts::GetScenario() const {
@@ -49,27 +60,24 @@
 void LoadingScenarioObserver::LoadingCounts::IncrementLoadingPageCounts(
     bool visible,
     bool focused) {
-  loading_pages_++;
+  CheckIncrement(loading_pages_);
   if (visible) {
-    visible_loading_pages_++;
+    CheckIncrement(visible_loading_pages_);
   }
   if (focused) {
-    focused_loading_pages_++;
+    CheckIncrement(focused_loading_pages_);
   }
 }
 
 void LoadingScenarioObserver::LoadingCounts::DecrementLoadingPageCounts(
     bool visible,
     bool focused) {
-  CHECK_GT(loading_pages_.RawValue(), 0u);
-  loading_pages_--;
+  CheckDecrement(loading_pages_);
   if (visible) {
-    CHECK_GT(visible_loading_pages_.RawValue(), 0u);
-    visible_loading_pages_--;
+    CheckDecrement(visible_loading_pages_);
   }
   if (focused) {
-    CHECK_GT(focused_loading_pages_.RawValue(), 0u);
-    focused_loading_pages_--;
+    CheckDecrement(focused_loading_pages_);
   }
 }
 
diff --git a/components/privacy_sandbox/privacy_sandbox_features.cc b/components/privacy_sandbox/privacy_sandbox_features.cc
index fdfe248..44f7ad8 100644
--- a/components/privacy_sandbox/privacy_sandbox_features.cc
+++ b/components/privacy_sandbox/privacy_sandbox_features.cc
@@ -148,7 +148,7 @@
 
 BASE_FEATURE(kTrackingProtection3pcdUx,
              "TrackingProtection3pcdUx",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             base::FEATURE_DISABLED_BY_DEFAULT);
 
 BASE_FEATURE(kFingerprintingProtectionUserBypass,
              "FingerprintingProtectionUserBypass",
diff --git a/components/resources/OWNERS b/components/resources/OWNERS
index cff876a..ec153ba 100644
--- a/components/resources/OWNERS
+++ b/components/resources/OWNERS
@@ -13,7 +13,7 @@
 per-file offline_pages_resources.grdp=file://components/offline_pages/OWNERS
 per-file policy_resources.grdp=file://components/policy/OWNERS
 per-file printing_resources.grdp=file://printing/OWNERS
-per-file search_engine_choice_scaled_resources.grdp=file://components/search_engines/search_engine_choice/OWNERS
+per-file search_engine_choice_scaled_resources.grdp=file://components/search_engines/search_engine_choice/COMPLIANCE_OWNERS
 per-file security_interstitials_dev_ui_resources.grdp=file://components/security_interstitials/OWNERS
 per-file security_interstitials_resources.grdp=file://components/security_interstitials/OWNERS
 per-file sync_driver_resources.grdp=file://components/sync/OWNERS
diff --git a/components/resources/default_100_percent/search_engine_choice/OWNERS b/components/resources/default_100_percent/search_engine_choice/OWNERS
index a8ff91d..c7aa42b 100644
--- a/components/resources/default_100_percent/search_engine_choice/OWNERS
+++ b/components/resources/default_100_percent/search_engine_choice/OWNERS
@@ -1 +1 @@
-file://components/search_engines/search_engine_choice/OWNERS
+file://components/search_engines/search_engine_choice/COMPLIANCE_OWNERS
diff --git a/components/resources/default_200_percent/search_engine_choice/OWNERS b/components/resources/default_200_percent/search_engine_choice/OWNERS
index a8ff91d..c7aa42b 100644
--- a/components/resources/default_200_percent/search_engine_choice/OWNERS
+++ b/components/resources/default_200_percent/search_engine_choice/OWNERS
@@ -1 +1 @@
-file://components/search_engines/search_engine_choice/OWNERS
+file://components/search_engines/search_engine_choice/COMPLIANCE_OWNERS
diff --git a/components/resources/default_300_percent/search_engine_choice/OWNERS b/components/resources/default_300_percent/search_engine_choice/OWNERS
index a8ff91d..c7aa42b 100644
--- a/components/resources/default_300_percent/search_engine_choice/OWNERS
+++ b/components/resources/default_300_percent/search_engine_choice/OWNERS
@@ -1 +1 @@
-file://components/search_engines/search_engine_choice/OWNERS
+file://components/search_engines/search_engine_choice/COMPLIANCE_OWNERS
diff --git a/components/safe_browsing/android/remote_database_manager.cc b/components/safe_browsing/android/remote_database_manager.cc
index 525753a..ddb40db 100644
--- a/components/safe_browsing/android/remote_database_manager.cc
+++ b/components/safe_browsing/android/remote_database_manager.cc
@@ -55,83 +55,40 @@
 //
 class RemoteSafeBrowsingDatabaseManager::ClientRequest {
  public:
-  enum class CallbackType {
-    BROWSE_URL,
-    DOWNLOAD_URL,
-  };
-
   ClientRequest(Client* client,
-                CallbackType callback_type,
                 RemoteSafeBrowsingDatabaseManager* db_manager,
-                const std::vector<GURL>& urls);
+                const GURL& url);
+
   void OnRequestDone(SBThreatType matched_threat_type,
                      const ThreatMetadata& metadata);
-  void AddPendingCheck();
 
   // Accessors
   Client* client() const { return client_; }
-  const std::vector<GURL>& urls() const { return urls_; }
-  size_t pending_checks() const { return pending_checks_; }
+  const GURL& url() const { return url_; }
   base::WeakPtr<ClientRequest> GetWeakPtr() {
     return weak_factory_.GetWeakPtr();
   }
 
  private:
-  void CompleteCheck();
-
   raw_ptr<Client, DanglingUntriaged> client_;
-  CallbackType callback_type_;
   raw_ptr<RemoteSafeBrowsingDatabaseManager, DanglingUntriaged> db_manager_;
-  std::vector<GURL> urls_;
-  size_t pending_checks_ = 0;
-  SBThreatType most_severe_threat_type_ = SBThreatType::SB_THREAT_TYPE_SAFE;
-  ThreatMetadata most_severe_metadata_;
+  GURL url_;
   base::ElapsedTimer timer_;
   base::WeakPtrFactory<ClientRequest> weak_factory_{this};
 };
 
 RemoteSafeBrowsingDatabaseManager::ClientRequest::ClientRequest(
     Client* client,
-    CallbackType callback_type,
     RemoteSafeBrowsingDatabaseManager* db_manager,
-    const std::vector<GURL>& urls)
-    : client_(client),
-      callback_type_(callback_type),
-      db_manager_(db_manager),
-      urls_(urls) {}
+    const GURL& url)
+    : client_(client), db_manager_(db_manager), url_(url) {}
 
 void RemoteSafeBrowsingDatabaseManager::ClientRequest::OnRequestDone(
     SBThreatType matched_threat_type,
     const ThreatMetadata& metadata) {
   DVLOG(1) << "OnRequestDone took " << timer_.Elapsed().InMilliseconds()
-           << " ms for client " << client_;
-
-  if (most_severe_threat_type_ == SBThreatType::SB_THREAT_TYPE_SAFE) {
-    most_severe_threat_type_ = matched_threat_type;
-    most_severe_metadata_ = metadata;
-  }
-
-  --pending_checks_;
-
-  if (pending_checks_ == 0) {
-    CompleteCheck();
-  }
-}
-
-void RemoteSafeBrowsingDatabaseManager::ClientRequest::AddPendingCheck() {
-  ++pending_checks_;
-}
-
-void RemoteSafeBrowsingDatabaseManager::ClientRequest::CompleteCheck() {
-  switch (callback_type_) {
-    case CallbackType::BROWSE_URL:
-      client_->OnCheckBrowseUrlResult(urls_[0], most_severe_threat_type_,
-                                      most_severe_metadata_);
-      break;
-    case CallbackType::DOWNLOAD_URL:
-      client_->OnCheckDownloadUrlResult(urls_, most_severe_threat_type_);
-      break;
-  }
+           << " ms for client " << client_ << " and URL " << url_;
+  client_->OnCheckBrowseUrlResult(url_, matched_threat_type, metadata);
   UMA_HISTOGRAM_TIMES("SB2.RemoteCall.Elapsed", timer_.Elapsed());
   // CancelCheck() will delete *this.
   db_manager_->CancelCheck(client_);
@@ -155,6 +112,7 @@
   for (auto itr = current_requests_.begin(); itr != current_requests_.end();
        ++itr) {
     if ((*itr)->client() == client) {
+      DVLOG(2) << "Canceling check for URL " << (*itr)->url();
       current_requests_.erase(itr);
       return;
     }
@@ -184,14 +142,11 @@
     return true;  // Safe, continue right away.
   }
 
-  auto req = std::make_unique<ClientRequest>(
-      client, ClientRequest::CallbackType::BROWSE_URL, this,
-      std::vector<GURL>{url});
+  auto req = std::make_unique<ClientRequest>(client, this, url);
 
   DVLOG(1) << "Checking for client " << client << " and URL " << url;
-  SafeBrowsingApiHandlerBridge::ResponseCallback callback =
+  auto callback =
       base::BindOnce(&ClientRequest::OnRequestDone, req->GetWeakPtr());
-  req->AddPendingCheck();
   switch (check_type) {
     case CheckBrowseUrlType::kHashDatabase:
       SafeBrowsingApiHandlerBridge::GetInstance().StartHashDatabaseUrlCheck(
@@ -210,45 +165,8 @@
 bool RemoteSafeBrowsingDatabaseManager::CheckDownloadUrl(
     const std::vector<GURL>& url_chain,
     Client* client) {
-  DCHECK(ui_task_runner()->RunsTasksInCurrentSequence());
-
-  if (!enabled_) {
-    return true;
-  }
-
-  auto req = std::make_unique<ClientRequest>(
-      client, ClientRequest::CallbackType::DOWNLOAD_URL, this, url_chain);
-
-  // Must add pending checks in a separate loop so that synchronous responses
-  // from the SafeBrowsingApiHandlerBridge can't complete the check early.
-  for (const GURL& url : url_chain) {
-    if (!CanCheckUrl(url)) {
-      continue;
-    }
-    req->AddPendingCheck();
-  }
-
-  if (req->pending_checks() == 0) {
-    return true;
-  }
-
-  for (const GURL& url : url_chain) {
-    if (!CanCheckUrl(url)) {
-      continue;
-    }
-    DVLOG(1) << "Checking for client " << client << " and URL " << url;
-    auto callback = SafeBrowsingApiHandlerBridge::ResponseCallback(
-        base::BindOnce(&ClientRequest::OnRequestDone, req->GetWeakPtr()));
-    SafeBrowsingApiHandlerBridge::GetInstance().StartHashDatabaseUrlCheck(
-        std::move(callback), url,
-        CreateSBThreatTypeSet({SBThreatType::SB_THREAT_TYPE_URL_MALWARE,
-                               SBThreatType::SB_THREAT_TYPE_URL_UNWANTED}));
-  }
-
-  current_requests_.push_back(std::move(req));
-
-  // Defer the resource load.
-  return false;
+  NOTREACHED_IN_MIGRATION();
+  return true;
 }
 
 bool RemoteSafeBrowsingDatabaseManager::CheckExtensionIDs(
@@ -291,14 +209,11 @@
     return true;
   }
 
-  auto req = std::make_unique<ClientRequest>(
-      client, ClientRequest::CallbackType::BROWSE_URL, this,